Как загрузить картинку в RichTextBox, также как это делает программа?

140
26 декабря 2019, 22:10

Постараюсь максимально ёмко уместить свой вопрос. Пока писал этот вопрос понял, что можно было написать короче. Вся суть в двух последних предложениях.

Идея: хранить FlowDocument(xaml файл) и картинки(они хранятся в папке media) в одном zip архиве. И в теории все неплохо.

Проблема: тяжело сделать Source у BitmapImage, так как нет Uri до файла, который находится в архиве

Допустим мы вставили картинку в RichTextBox. И когда мы ее вставили, то UriSource и BaseUri картинки выглядит так (внимание на 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>

Всю эту радость надо сохранить. Как это делаю я:

  1. делаю все нужные папки
  2. делаю копию FlowDocument(нужно, чтобы спокойно изменять Source картинок)
  3. ищу картинки в FlowDocument (Найти картинки в FlowDocument)

  4. сохраняю их в папке media

  5. меняю Source картинки на null, и в Tag картинки записываю путь относительно Кусок кода (без создания папок):

     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);
    }
    

    На этом этапе все хорошо, если у всех Image есть правильные Uri из которых можно сделать картинку.

Теперь это нужно открыть и заново сохранять. Одной из моих попыток было сделать List<MemoryStream> в каждый поток запихнуть по картинке, которую прочитаю из zip и потом из потока их и сохранять. Но сразу признаюсь, что в Stream я плохо разобрался.

Открываем:

 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;
    }

И в RichTextBox Document получается такое:

<?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>

И такое чудо уже не сохранить, так как нет нормального Source у BitmapImage. InvalidOperationException: Необходимо задать свойство "UriSource" или "StreamSource".

Что я делал:

  1. Сделать временную папку
  2. Сохранять туда картинки и делать на них Source
  3. И из этой папки брать картинки, которые нужны и сохранять их
  4. Очистить эту папку

Это все работало, но я хочу сохранять картинку в MemoryStream и когда сохраняю в zip-файл, из MemoryStream картинку и доставать, также как это делает сама программа с чудо pack://payload:,,. Как это сделать?

READ ALSO
Получение значений из сокета

Получение значений из сокета

Клиент записывает в сокет последовательно значения Uint, Uint, Short

136
Проверка процессов критический или нет

Проверка процессов критический или нет

Какой самый надёжный метод определения системных процессов в системе?

122
Как назначить StreamSource в BitmapImage в codebehind?

Как назначить StreamSource в BitmapImage в codebehind?

Есть zip файл, в котором хранится FlowDocument (Cardxaml) и папка с изображениями (Media)

117
как задать цвет числу в С#?

как задать цвет числу в С#?

Есть код, который переводит все в двоичный кодТеперь мне нужно задать числу 1 - черный цвет, а 0 - белый цвет

134