1
votes

I have a RichTextBox control inside a DataTemplate for a ListView. The idea is that I want to dynamically add Runs/InlineUIElements/images, etc to the rich text boxes in the Listview at Bind time. The problem is there's no ondatabinding or similar event. I tried the Loaded event of the RichTextBox but it appears that WPF reuses controls so the content was being messed up when I scrolled (putting the wrong content in the wrong order, because the load events were firing during scroll). I should also mention the binding to the ListView is taking place in the codebehind, by manually adding rows to the ListView.Items collection.

Relevant markup

 <ListView Background="#F7F7F7" HorizontalAlignment="Stretch" Foreground="Black" x:Name="chatPane" Grid.Row="1" 
                  ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch" SelectionMode="Multiple"
              ItemTemplateSelector="{StaticResource messageTypeDataTemplateSelector}" SelectionChanged="ChatPane_OnSelectionChanged">
        </ListView>

<common:MessageTypeDataTemplateSelector 
        TextMessageTemplate="{StaticResource TextMessage}" 
        EnterMessageTemplate="{StaticResource EnterMessage}" 
        ExitMessageTemplate="{StaticResource ExitMessage}"
        TimestampMessageTemplate="{StaticResource TimestampMessage}"
        ImageMessageTemplate="{StaticResource ImageMessage}"
        x:Key="messageTypeDataTemplateSelector" />

<DataTemplate x:Key="TextMessage">
        <Grid Grid.ColumnSpan="3" RowSpan="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition  Width="Auto"/>
                <ColumnDefinition Width=".5*"/>
                <ColumnDefinition Width="70*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding UserName}" Visibility="{Binding Source={StaticResource chatSettings}, Path=HideAvatars, Converter={StaticResource booleanToVisibility}}" FontWeight="Bold" TextAlignment="Right" Grid.Column="0" Width="150" />
            <Image VerticalAlignment="Top" Source="{Binding AvatarUrl}" Visibility="{Binding Source={StaticResource chatSettings}, Path=ShowAvatars, Converter={StaticResource booleanToVisibility}}" Grid.Column="0" Width="60" Margin="0,0,10,0" />
            <TextBlock Text=" : " Visibility="{Binding Source={StaticResource chatSettings}, Path=HideAvatars, Converter={StaticResource booleanToVisibility}}" Grid.Column="1"  />
            <RichTextBlock Loaded="FrameworkElement_OnLoaded" TextWrapping="Wrap" Grid.Column="2" />
        </Grid>
    </DataTemplate>
1
Just so you know, Windows 8 Store apps do not use WPF. They are using Windows Runtime (RT), which is not WPF and not Silverlight, though sharing some similarities between the two. I know this doesn't answer your question, but just sharing since it could come in handy when looking through the MSDN documentation. - Chris Leyva
And what does your FrameworkElement_OnLoaded method look like? - Chris Leyva

1 Answers

4
votes

You are absolutely correct. There is no OnDataBinding event in WinRT. How about this idea:

Create an AttachedProperty for the RichTextBlock (http://msdn.microsoft.com/en-us/library/ms749011(v=vs.110).aspx) and then Bind your item to that. When you register the Attached Property, in the FrameworkPropertyMetadata, you can specify a PropertyChangedCallback that will fire whenever the Attached Property's value changes. Like this:

For now, to make sure it works, in your current .xaml.cs file do the following:

public static readonly DependencyProperty RichTextBlockItemProperty = DependencyProperty.RegisterAttached(
  "RichTextBlockItem",
  typeof(object),
  typeof(RichTextBlock),
  new PropertyMetadata(null, RichTextBlockItemChanged)
);

// Don't forget this!
public static object GetRichTextBlockItem(DependencyObject obj)
{
    return (object)obj.GetValue(RichTextBlockItemProperty);
}

// And this!
public static void SetRichTextBlockItem(DependencyObject obj, object value)
{
   obj.SetValue(RichTextBlockItemProperty, value);
}

public void RichTextBlockItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    // Here you can do whatever you wish. This method will fire when the item is bound.
    // d is the RichTextBlock object.
    // e.NewValue is the value that was just bound.
    // So, from here you can dynamically add your Runs/Images/etc
}

And then in your .xaml file make sure you add a local namespace so you can do this:

<Page .... xmlns:local="using:CurrentXamlPageNamespace".../>

<DataTemplate x:Key="TextMessage">
    <Grid Grid.ColumnSpan="3" RowSpan="1">
        <!-- I got rid of the other xaml just to hightlight my answer. But you still need it. -->
        <RichTextBlock local:RichTextBlock.RichTextBlockItem="{Binding}" TextWrapping="Wrap" Grid.Column="2" />
    </Grid>
</DataTemplate>