How to create a two way binding between a DocX Byte[] and a RadRichTextBox

3 posts, 1 answers
  1. Nathan
    Nathan avatar
    12 posts
    Member since:
    Dec 2011

    Posted 12 Dec 2014 Link to this post

    In my view model I have a byte[] (called "DocumentBytes") that is read from a .docx file.  I have my view set up like this:

    <telerik:DocxDataProvider
      x:Name="docxDataProvider"
      Docx="{Binding DocumentBytes, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
      RichTextBox="{Binding ElementName=SnippetPreviewRichTextBox}"
      />
     
    <telerik:RadRichTextBox x:Name="SnippetPreviewRichTextBox"/>

    The rich textbox correctly displays the document from the file.  When the user makes a change to the content of the rich textbox and then the textbox loses focus, I need the byte[] (DocumentBytes) to be written back with the updated contents of the textbox, and in the docx format, as it was provided to the DocxDataProvider.

    Out-of-the-box this didn't seem to work.  I expected the data provider to just write the value back to my property, but it didn't do that.

    So, I tried to implement this myself using my view's code behind:
    private bool DocumentHasChanged { get; set; }
     
    private void SnippetPreviewRichTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
      this.DocumentHasChanged = false;
    }
     
    private void SnippetPreviewRichTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
      if (!this.DocumentHasChanged)
        return;
      var viewModel = this.DataContext as SnippetPreviewViewModel;
      if (viewModel == null)
        return;
     
      var formatProvider = new DocxFormatProvider();
      var docXBytes = formatProvider.Export(this.SnippetPreviewRichTextBox.Document);
      viewModel.DocumentBytes = docXBytes;
    }
     
    private void SnippetPreviewRichTextBox_DocumentContentChanged(object sender, EventArgs e)
    {
      this.DocumentHasChanged = true;
    }


    This caused problems and threw exceptions when I would edit the document's content in the rich textbox, and then click on back on it (the LostFocus event was firing each time I clicked on the rich textbox).

    I'm really surprised there isn't a short and concise example in the WPF documentation on this, and it's something I imagine most people have to do.  Perhaps the documentation is there and I just haven't found it.

    So, what I'm looking for is a example of how to actually bind a byte[] from my view model, and get the view to write back to that same property when the rich textbox's content is updated.
  2. Answer
    Petya
    Admin
    Petya avatar
    975 posts

    Posted 17 Dec 2014 Link to this post

    Hello Nathan,

    Please make sure to set the UpdateSourceTrigger property of the data provider, not the binding:
    <telerik:DocxDataProvider x:Name="docxDataProvider" Docx="{Binding DocumentBytes, Mode=TwoWay}" UpdateSourceTrigger="LostFocus" RichTextBox="{Binding ElementName=SnippetPreviewRichTextBox}" />
    This should cause your property to update when the control loses focus.

    Unfortunately, there is no way to prevent the behavior concerning the LostFocus event of RadRichTextBox. The reason the event is fired is the fact that the caret in control is represented by a TextBox, so focus is passed along to it when you edit the document. I'm afraid I have no suggestions on how you could prevent the behavior.

    As to the approach you tried implementing yourself, I couldn't say what is causing this. Can you share the stack trace of the error? Also, please specify the version of Telerik UI for WPF that you are using.

    Regards,
    Petya
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  3. UI for WPF is Visual Studio 2017 Ready
  4. Nathan
    Nathan avatar
    12 posts
    Member since:
    Dec 2011

    Posted 17 Dec 2014 in reply to Petya Link to this post

    Thanks for the response Petya.

    The UpdateSourceTrigger property was the trick.  I ended up not going with LostFocus because of the extra calls that happen when you click into it (I have side effects that happen when the byte[] gets values set to it), and I wanted to control the push of the bytes only when lost focus, if the document has changed.

    So I ended up going with this in my View's Xaml

    <telerik:DocxDataProvider  x:Name="docxDataProvider"  Docx="{Binding DocumentBytes, Mode=TwoWay}" UpdateSourceTrigger="Explicit"  RichTextBox="{Binding ElementName=SnippetPreviewRichTextBox}"  />
     
    <telerik:RadRichTextBox x:Name="SnippetPreviewRichTextBox"/>


    And this in the code behind

    private bool DocumentHasChanged { get; set; }
     
    private void SnippetPreviewRichTextBox_GotFocus(object sender, RoutedEventArgs e)
    {
      this.DocumentHasChanged = false;
    }
     
    private void SnippetPreviewRichTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
      if (!this.DocumentHasChanged)
        return;
     
      this.docxDataProvider.UpdateBytes();
    }
     
    private void SnippetPreviewRichTextBox_DocumentContentChanged(object sender, EventArgs e)
    {
      this.DocumentHasChanged = true;
    }
     
    private void SnippetPreviewRichTextBox_DocumentChanged(object sender, EventArgs e)
    {
      this.DocumentHasChanged = false;
    }
Back to Top