How do I bind rich text (FlowDocument?) from my view model to a RichTextBlock?
Asked Answered
M

4

6

The only examples I've been able to find online for binding text to a WinRT RichTextBlock look like this:

<RichTextBlock>
    <Paragraph>
        <Run Text="{Binding Content}"/>
    </Paragraph>
</RichTextBlock>

The only examples I've been able to find for actually showing rich text look like this:

<RichTextBlock>
    <Paragraph>
        <Run>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ligula nisi, vehicula nec eleifend vel, rutrum non dolor. Vestibulum ante ipsum primis in faucibus orci</Run>
        <Run FontSize="30">luctus</Run>
        <Run>et ultrices posuere cubilia Curae; Curabitur elementum scelerisque accumsan. In hac habitasse platea dictumst. Maecenas eu nibh vitae nibh laoreet placerat. Duis dolor ante, semper luctus ullamcorper eget, placerat et ligula. Donec placerat tincidunt vehicula. Fusce condimentum lacus quis libero blandit semper sed vel quam. Proin eget nisl lacinia nibh convallis scelerisque at sed massa. Duis commodo tincidunt consequat. Duis malesuada, nisl a pharetra placerat, odio dui suscipit quam, vitae rhoncus sem risus quis odio. Aliquam justo nunc, adipiscing id elementum sit amet, feugiat vel enim. Aliquam pharetra arcu nec elit luctus euismod. Suspendisse potenti.</Run>
    </Paragraph>
</RichTextBlock>

How would I go about databinding the text of the RichTextBlock to a property in my view model that may contain multiple paragraphs and runs? What type does that view model property need to be?

I've seen some references to using a FlowDocument, but I can't tell if that will work with a RichTextBlock. However, even those examples don't show any data binding to the document.

Middleweight answered 20/11, 2012 at 23:13 Comment(1)
I'm switching to HTML/CSS/JavaScript for this app. Text display in HTML/CSS seems much easier. Especially when you want multiple columns.Middleweight
B
2

RichTextBlock does not seem to provide document binding. Instead you can use custom RichTextBlock controls to achieve it. You can try Bindable RichTextBlock

Byssinosis answered 21/11, 2012 at 5:6 Comment(3)
The RichTextBox control that he extends in that blog post doesn't seem to exist for winRT XAML.Middleweight
IMHO, it should work by inheriting from RichTextBlock instead of RichTextBox. Let us know your adventure with it.Byssinosis
The Windows 8 control RichTextBlock is sealed, so inheriting from it is not an option.Middleweight
K
2

I had the same problem and my solution is to do it manually.

  1. Subscribe to the notifyPropertyChanged in your view.xaml.cs
  2. When an update occurs, check whether it is your property that has been updated
  3. If so, update your RichTextBlock

Here is the code.

private void Page_OnLoaded(object sender, RoutedEventArgs e)
        {    
            SetReferences();
            (DataContext as INotifyPropertyChanged).PropertyChanged += OnPropertyChanged;
        }

        private void Page_OnUnloaded(object sender, RoutedEventArgs e)
        {
            (DataContext as INotifyPropertyChanged).PropertyChanged -= OnPropertyChanged;
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var propertyName = GetPropertyName<MyViewModel, object>(x => x.References); // I use this to avoid bugs at runtime
            if (e.PropertyName == propertyName)
                SetReferences();
        }

        private void SetReferences()
        {
            var references = (DataContext as MyViewModel).References;
            ReferencesRichTextBlock.Blocks.Clear();
            foreach (var reference in references)
            {
                var paragraph = new Paragraph();
                paragraph.Inlines.Add(new Run { Text = reference.Title, FontWeight = FontWeights.Bold});
                paragraph.Inlines.Add(new Run { Text = " : "});
                paragraph.Inlines.Add(new Run { Text = reference.Content});
                paragraph.Inlines.Add(new LineBreak());

                ReferencesRichTextBlock.Blocks.Add(paragraph);
            }
        }

        public static string GetPropertyName<T, P>(Expression<Func<T, P>> action) where T : class
        {
            var expression = (MemberExpression)action.Body;
            var name = expression.Member.Name;

            return name;
        }

And of course, this is the xaml

<Page ...
      Loaded="Page_OnLoaded"
      Unloaded="Page_OnUnloaded">
...
<RichTextBlock x:Name="ReferencesRichTextBlock">

                            </RichTextBlock>
</Page>
Killjoy answered 7/3, 2016 at 13:49 Comment(1)
That looks promising. I never did finish the app I was working on when I posted this question and hadn't been planning to get back to it, but maybe with a viable answer I will try again.Middleweight
C
1

I have bound data to the RichTextBlock in one of my projects. See the XAML below. I'm binding a few string values and an image. You can see a snapshot of how the page gets rendered here: enter image description here

    <common:RichTextColumns x:Name="richTextColumns" Margin="117,0,117,47" VerticalAlignment="Top">
<RichTextBlock x:Name="richTextBlock" Width="560" Style="{StaticResource ItemRichTextStyle}" TextWrapping="Wrap">
    <Paragraph>
    <Run FontSize="20" FontWeight="Light" Text="{Binding Title}"/>
    <LineBreak/>
    </Paragraph>
    <Paragraph LineStackingStrategy="MaxHeight">
    <InlineUIContainer>
        <ItemsControl ItemsSource="{Binding Authors}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
            <StackPanel>
                <HyperlinkButton Content="{Binding}"
                    VerticalAlignment="Center" FontSize="14"
                    Tapped="AuthorSearchLinkTapped"
                    IsTapEnabled="True"
                    Padding="0, 0, 0, 0"/>
            </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        </ItemsControl>
    </InlineUIContainer>
    </Paragraph>
    <Paragraph LineStackingStrategy="MaxHeight">
    <InlineUIContainer>
        <Image x:Name="image" MaxHeight="200" Margin="0,20,0,10" Stretch="Uniform" Source="{Binding ImageUrl}"/>
    </InlineUIContainer>
    </Paragraph>
    <Paragraph LineStackingStrategy="MaxHeight">
    <InlineUIContainer>
        <Grid Width="560" Height="125" MaxHeight="125">
        <TextBlock Text="Loading ad..." 
               VerticalAlignment="Center" 
               HorizontalAlignment="Center"
               Style="{StaticResource BasicTextStyle}"/>
        <UI:AdControl ApplicationId="ec6615c8-dc88-4413-af37-1fc3b5603e85" 
                  AdUnitId="104236"
                  Width="250" Height="125"
                  HorizontalAlignment="Center"/>
        </Grid>
    </InlineUIContainer>
    </Paragraph>
    <Paragraph>
    <Run FontWeight="SemiLight" Text="{Binding Description}"/>
    </Paragraph>
</RichTextBlock>

<!-- Additional columns are created from this template -->
<common:RichTextColumns.ColumnTemplate>
    <DataTemplate>
    <RichTextBlockOverflow Width="560" Margin="80,0,0,0">
        <RichTextBlockOverflow.RenderTransform>
        <TranslateTransform X="-1" Y="4"/>
        </RichTextBlockOverflow.RenderTransform>
    </RichTextBlockOverflow>
    </DataTemplate>
</common:RichTextColumns.ColumnTemplate>
</common:RichTextColumns>
Clementeclementi answered 21/11, 2012 at 4:9 Comment(1)
This is essentially an expanded example of the first option in my question. If you know the format at design time, it's easy. But I don't see how this provides for arbitrary bold words within the bound text for instance. I need the rich formatting to be provided via data binding, not preset in the page XAML.Middleweight
C
0

I had a similar situation. I wanted to update the contents of a ReadOnly RichTextBox with status messages based on some ongoing operation. Here's what I ended up doing:

<UserControl 
  x:Class="RawhideCsControl.StorageViewUserControl2" 
  <!-- >

      <RichTextBox  x:Name="StatusText" HorizontalAlignment="Left"  Height="350.72" VerticalAlignment="Top" Width="636">
          <FlowDocument>
               <Paragraph>
                    <Run Text="{Binding Status}"></Run>
               </Paragraph>
          </FlowDocument>
      </RichTextBox>

  <!-- >
</UserControl>

code:

    public partial class StorageViewUserControl2 : System.Windows.Controls.UserControl
    {
        // . . .

        StatusChanger statusChanger = new StatusChanger();
        private void AddStatusLine(string format, params object[] args)
        {
            if (args.Length > 0)
                format = string.Format(format, args);

            //m_StorageViewTreeModel.MirrorStatusDisplay += string.Format("{0}\r\n", format); 
            statusChanger.Status += string.Format("{0}\r\n", format);

        }//end this.AddStatusLine(str)

        // . . .
     } // end partial class


    public class StatusChanger : INotifyPropertyChanged
    {
        private string status = string.Empty;
        public string Status
        {
            get { return status; }
            set
            {
                status = value;
                OnPropertyChanged("Status");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged( string PropertyName )
        {
            if( PropertyChanged != null )
                PropertyChanged( this, new PropertyChangedEventArgs(PropertyName));
        }
    } // end class StatusChanger
Chink answered 13/1, 2014 at 21:59 Comment(1)
I can't see how this solves my problem. Does this allow you to have some bold text and some normal text within your status messages? You don't show any rich formatting in your answer. This looks the same as what I put in my question showing data binding to a string.Middleweight

© 2022 - 2024 — McMap. All rights reserved.