Search code examples
c#wpfrichtextbox

Keep source of image in RichTextBox after pasting or moving the image


I'm using a RichTextBox thant can contains images. I import them with an OpenFileDialog and put then into an InlineUIContainer.

It is displayed and the xaml code is as follows.

<Paragraph>
    <Image Source="file://C:/Path/Test.png" Stretch="Fill" Width="200" Height="100" />
</Paragraph>

I can save my xaml document in the database, close it and open it again without any problems, the image will still be displayed.

But if I ever move the image from within the document oc copy and paste it, my xaml code becomes as follow :

<BlockUIContainer>
    <Image Stretch="Fill" Width="200" Height="100">
        <Image.Source>
            <BitmapImage BaseUri="pack://payload:,,wpf3,/Xaml/Document.xaml" UriSource="./Image1.png" CacheOption="OnLoad" />
        </Image.Source>
    </Image>
</BlockUIContainer>

It is now contained within a BlockUIContainer, which I don't want for display purposes, but it's not my bigger problem, because I lost the ImageSource, thus not able to display them again.

Do you have any solutions to prevent this behavior ?

I tried to intercept the Copy and Paste event with DataObject.AddCopyingHandler et DataObject.AddPastingHandler, but couldn't manage to get the desired result.

I also found this question Saving source string of image in richtextbox after moving or pasting from clipboard with a similar problem but I don't understand what is meant by "saving it to the XAML Package".


Solution

  • According to the documentation the DataFormats.XamlPackage

    Specifies the Extensible Application Markup Language (XAML) package data format.

    In other words, this format gives possibility to save document as a packed xaml. Actually, this is archived document: binary file.

    Therefore, instead of saving to a file you can get bytes array and save in to the database as binary data (varbinary) and when this is necessary load it to the RichTextBox back.


    Below you can find some example that demonstrate how you can use this format to save/load a document from the RichTextBox to save and restore documents containing images.

    The MainWindow.xaml:

    <Window x:Class="WpfApp11.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        
            mc:Ignorable="d"
            Title="MainWindow" Height="250" Width="550" 
            x:Name="mainWindow" Topmost="True">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <RichTextBox Grid.Row="0" x:Name="rtb" AllowDrop="True" Padding="2" FontSize="18"
                         HorizontalAlignment="Left"
                         VerticalScrollBarVisibility="Auto"
                         HorizontalScrollBarVisibility="Auto" >         
            </RichTextBox>
    
            <Grid Grid.Row="1" Margin="3">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>            
                <Button Grid.Column="1" Click="Button_Save" Margin="2" Padding="3">Save</Button>
                <Button Grid.Column="2" Click="Button_Load" Margin="2" Padding="3">Load</Button>
            </Grid>        
        </Grid>
    </Window>
    

    The MainWindow.xaml.cs:

    using System;
    using System.IO;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    
    namespace WpfApp11
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Save(object sender, RoutedEventArgs e) { rtb.SaveRtf(); }
    
            private void Button_Load(object sender, RoutedEventArgs e) { rtb.LoadRtf(); }
        }
    
        public static class RichTextBoxExt
        {
            public static void SaveRtf(this RichTextBox rtb)
            {
                var dlg = new Microsoft.Win32.SaveFileDialog
                {
                    DefaultExt = ".xpack",
                    Filter = "XamlPackage Files (*.xpack)|*.xpack|All Files|*.*"
                };
    
                if (dlg.ShowDialog() == true)
                {
                    try
                    {
                        var range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
                        using (var ms = new MemoryStream())
                        {
                            range.Save(ms, DataFormats.XamlPackage);
                            using (var file = new FileStream(dlg.FileName, FileMode.OpenOrCreate, FileAccess.Write))
                            {
                                ms.WriteTo(file);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex.Message);
                    }
                }
            }
    
            public static void LoadRtf(this RichTextBox rtb)
            {
                var dlg = new Microsoft.Win32.OpenFileDialog
                {
                    DefaultExt = ".xpack",
                    Filter = "XamlPackage Files (*.xpack)|*.xpack|All Files|*.*"
                };
    
                if (dlg.ShowDialog() == true)
                {
                    var range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
                    using (var stream = new StreamReader(dlg.FileName))
                    {
                        try
                        {
                            range.Load(stream.BaseStream, DataFormats.XamlPackage);
                        }
                        catch (Exception ex)
                        {
                            System.Diagnostics.Debug.WriteLine(ex.Message);
                        }
                    }
                }
            }
        }
    }