I will try to keep my question as capacious as possible. While writing this question I realized that it was possible to write shorter. The whole point in the last two sentences.
The idea: to store FlowDocument (xaml file) and images (they are stored in the media folder) in one zip archive. And in theory, everything is not bad.
Problem: it is difficult to make a Source for BitmapImage, since there is no Uri before the file that is in the archive
Suppose we inserted a picture in the RichTextBox . And when we inserted it, the UriSource and BaseUri images look like this (pay attention to pack: // payload :) :
<?xml version="1.0"?> <FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" NumberSubstitution.CultureSource="User" AllowDrop="True" PagePadding="5,0,5,0"> <BlockUIContainer TextAlignment="Justify"> <Image Height="400" Width="600"> <Image.Source> <BitmapImage CacheOption="OnLoad" UriSource="./Image1.bmp" BaseUri="pack://payload:,,wpf1,/Xaml/Document.xaml"/> </Image.Source> </Image> </BlockUIContainer> </FlowDocument>
All this joy must be preserved. How do I do it:
- I make all the necessary folders
- I make a copy of FlowDocument (I need to calmly change the Source of pictures)
Looking for pictures in FlowDocument ( Find pictures in FlowDocument )
save them in the media folder
I change the Source of the picture to null, and in the Tag of the picture I write the path relative to a piece of code (without creating folders)
private void Save_Executed(object sender, ExecutedRoutedEventArgs e) { \\omitted string richTextBoxFlowDocument = xamlWriter.Save(rtbEditor.Document); //ΠΠΎΠΏΠΈΡ FlowDocument FlowDocument rtbDocumentCopy = (FlowDocument)XamlReader.Parse(richTextBoxFlowDocument); //ΠΠ°Ρ ΠΎΠ΄ΠΈΠΌ Π²ΡΠ΅ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ Π² RichTextBox List<Image> images = FindImages(rtbDocumentCopy).ToList(); foreach (var image in images) { \\Omitted BitmapEncoder encoder = new PngBitmapEncoder(); //ΠΠ΅Π»Π°Π΅ΠΌ BitmapImage ΠΈΠ· image source var imageToSave = image.Source as BitmapImage; encoder.Frames.Add(BitmapFrame.Create(imageToSave)); //Π‘ΠΎΡ ΡΠ°Π½ΡΠ΅ΠΌ ΠΊΠ°ΡΡΠΈΠ½ΠΊΡ using (var fileStream = new FileStream(pathToMediaPlusNewFile, FileMode.Create)) { encoder.Save(fileStream); } //ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ Π² Tag ΠΌΠ΅ΡΡΠΎ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ flowdocument, image.Tag = nameofMediaFolder + "/" + eligibleMediaFileName; //ΠΡΠ»ΠΈ source Π½Π΅ ΡΠΊΠ°Π·Π°Π½, ΡΠΎ Π²ΡΠ΅ΠΌ ΠΏΠ»Π΅Π²Π°ΡΡ image.Source = null; } richTextBoxFlowDocument = XamlWriter.Save(rtbDocumentCopy); File.WriteAllText(nameOfXamlRelativePath, richTextBoxFlowDocument, Encoding.UTF8); //ΠΠΈΠΏΡΠ΅ΠΌ ΠΏΠ°ΠΏΠΊΡ ZipFile.CreateFromDirectory(cardGUIDFolderRelativePath, cardGUIDFolderRelativePath + ".zip"); //Π£Π΄Π°Π»ΡΠ΅ΠΌ ΠΏΠ°ΠΏΠΊΡ ΠΈ Π²ΡΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Directory.Delete(cardGUIDFolderRelativePath, true); }
At this stage, everything is good if all Image have the correct Uri of which you can make a picture.
Now it needs to be opened and re-saved. One of my attempts was to make a List<MemoryStream>
in each stream stuffed with a picture that I read from zip and then from a stream and save them. But I confess at once that I didnβt understand Stream .
Open:
List<MemoryStream> memoryStreams = new List<MemoryStream>(); private void Open_Executed(object sender, ExecutedRoutedEventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); if (dlg.ShowDialog() == true) { //ΠΡΠΊΡΡΠ²Π°Π΅ΠΌ zip ΡΠ°ΠΉΠ». UserData\<GUID> using (FileStream fullCardZipFile = File.Open(dlg.FileName, FileMode.Open, FileAccess.ReadWrite)) { //Π‘ΠΌΠΎΡΡΠΈΠΌ zip c ZipArchive using (ZipArchive archive = new ZipArchive(fullCardZipFile, ZipArchiveMode.Update)) { //ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ entry Π΄Π»Ρ Π½Π°ΡΠ΅Π³ΠΎ xaml. ZipArchiveEntry xamlFileEntry = archive.GetEntry(nameOfXamlCardDefault); //ΠΡΠΊΡΡΠ²Π°Π΅ΠΌ Π½Π°Ρ xaml using (Stream xamlFileStreamInZip = xamlFileEntry.Open()) { rtbEditor.Document = XamlReader.Load(xamlFileStreamInZip) as FlowDocument; //ΠΡΠ΅ΠΌ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ List<Image> images = FindImages(rtbEditor.Document).ToList(); foreach (var image in images) { var imageFileEntry = archive.GetEntry(image.Tag.ToString()); var bitmap = new BitmapImage(); using (Stream imageFileStream = imageFileEntry.Open()) { //ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΡ MemoryStream var memoryStream = new MemoryStream(); imageFileStream.CopyTo(memoryStream); memoryStreams.Add(memoryStream); //ΠΠ΅Π»Π°Ρ ΠΊΠ°ΡΡΠΈΠ½ΠΊΡ bitmap.BeginInit(); bitmap.StreamSource = memoryStreams.Last(); bitmap.CacheOption = BitmapCacheOption.OnDemand; bitmap.EndInit(); image.Source = bitmap; } } } } } } return; }
And in RichTextBox Document it turns out this:
<?xml version="1.0"?> <FlowDocument xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" NumberSubstitution.CultureSource="User" AllowDrop="True" PagePadding="5,0,5,0"> <Paragraph> <Image Tag="Media/image0.png"> <Image.Source> <BitmapImage CacheOption="OnDemand" BaseUri="{x:Null}"/> </Image.Source> </Image> <Image Tag="Media/image1.png"> <Image.Source> <BitmapImage CacheOption="OnDemand" BaseUri="{x:Null}"/> </Image.Source> </Image> </Paragraph> </FlowDocument>
And such a miracle can not be saved, since there is no normal BitmapImage Source. InvalidOperationException: You must set the property "UriSource" or "StreamSource".
What did I do:
- Make temporary folder
- Save pictures there and make Source on them
- And from this folder, take the pictures you need and save them.
- Clear this folder
It all worked, but I want to save the image in a MemoryStream and when I save a picture from the MemoryStream to a zip file and retrieve it, just like the program itself does with the miracle pack: // payload: ,, . How to do it?