views:

536

answers:

3

A WPF FlowDocument can only belong to a single RichTextBox. However, we'd like to use a single document which can be manipulated at different points (in space and time) in the UI. It will never happen that there are two RichTextBoxes simultaneously displaying a single document (and can't, because WPF will complain).

Using a MemoryStream and XamlReader/Writer won't work here as we would like to retain a single document and reflect changes wherever it is used, so copying it every time is out.

Is there any other viable way to achieve that? Something you could use as underlying model from which to create FlowDocuments or so?

Clarification: The RichTextBoxes that use the Document are no longer visible or anywhere in the logical/visual tree when another one will be instantiated. Though I probably can't enforce that they have already been eaten by the GC. Seemingly this causes a problem when re-using the Document immediately after removing the RichTextBox from the window; that still throws an exception that the Document is already used in another control.

Basically be have a set of "wizard pages" that are displayed one after another and it can happen that we re-use the Document on two successive "pages" but instantiate a new RTBox each time. Maybe there's a problem or a better way?

A: 

If only one editor is visible in the interface at any given time, I should think it would be possible to synchronize the contents of all editors in the LostFocus event of the active editor.

If changes in one editor need to be reflected immediately in another currently visible part of the app, you could fake it by using a Rectangle, say, where the Fill was a VisualBrush referencing the active editor and the Rectangle's width and height were bound to the editor's actual width and height. The tricky part would be handling the active editor switch in a relatively seamless fashion.

EDIT: I guess I don't understand why you cannot do a XamlWriter.Save / XamlReader.Parse sequence when moving between pages in the wizard, but...what about using the same RichTextBox instance all the time and re-parent it to a container in each page when that page becomes visible/active?

Daniel Pratt
I elaborated a little more in the question. We deefinitely don't need to reflect those changes immediately elsewhere in the UI. But a Document resists being used more than once, seemingly, which can be circumvented with XamlWriter/Reader for static content, but not for dynamic stuff :(
Joey
+1  A: 

Depending on how your wizard is constructed I actually would recommend you simply move the RichTextBox from page to page. WPF controls can be ‘unparented’ and ‘reparented’ at will, so just make the RichTextBox instance available in the context shared throughout your wizard and make sure you unparent / reparent as you move from page to page. This also has the benefit of saving any styles or changes to the editors state across pages in the wizard (which is probably desirable).

If it isn’t possible to share the RichTextBox instance across pages, I think there is a way to disassociate the document from the original RichTextBox. It appears that in order to disassociate the document from RichTextBox1, you have to provide RichTextBox1 with a new document. You can’t set RichTextBox1.Document to null, but you CAN set RichTextBox1.Document to new FlowDocument() and I believe that will work. I’m unable to test this right now, but I saw it was recommended in the last post of this MSDN forum thread:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/797bfc96-cf24-4071-bff8-ce8c4a7b897b

Jared Bienz - MSFT
+1  A: 

The FlowDocument cannot be shared by several RichTextBox controls directly..

So you need to 'detach' that document first...

So

RTB2.Document = RTB1.Document;

wont work and will result in your exception..

FlowDocument doc = RTB1.Document;
RTB1.Document = new FlowDocument(); //Document cannot be null, so therefor the new FlowDocument instance
RTB2.Document = doc;

will work like a charm...

My code sample:

Xaml:

<Window x:Class="WpfApplication2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>


        <RichTextBox x:Name="RTB1" />

        <RichTextBox x:Name="RTB2" Grid.Column="1" />

        <Button x:Name="button" Grid.Row="1" Grid.ColumnSpan="2" Content="click" Click="button_Click"  />

    </Grid>

</Window>

Code behind..

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        FlowDocument doc = RTB1.Document;
        RTB1.Document = new FlowDocument();
        RTB2.Document = doc;
    }
}

Its not the prettiest in the book, but it works...

Hope this helps..

Arcturus