绑定TextBlock.Inlines的数据

wil*_*ill 27 data-binding wpf textblock inlines

我的WPF应用程序从后端服务接收消息流,我需要在UI中显示.这些消息差异很大,我想为每条消息提供不同的视觉布局(字符串格式,颜色,字体,图标等等).

我希望能够为每条消息创建一个内联(Run,TextBlock,Italic等),然后以某种方式将它们全部放入一个ObservableCollection<>并在UI中使用我的TextBlock.Inlines上的WPF数据绑定魔法.我找不到怎么做,这可能吗?

小智 13

您可以将依赖项属性添加到TextBlock子类

public class BindableTextBlock : TextBlock
{
    public ObservableCollection<Inline> InlineList
    {
        get { return (ObservableCollection<Inline>)GetValue(InlineListProperty); }
        set { SetValue(InlineListProperty, value); }
    }

    public static readonly DependencyProperty InlineListProperty =
        DependencyProperty.Register("InlineList",typeof(ObservableCollection<Inline>), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock textBlock = sender as BindableTextBlock;
        ObservableCollection<Inline> list = e.NewValue as ObservableCollection<Inline>;
        list.CollectionChanged += new     System.Collections.Specialized.NotifyCollectionChangedEventHandler(textBlock.InlineCollectionChanged);
    }

    private void InlineCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            int idx = e.NewItems.Count -1;
            Inline inline = e.NewItems[idx] as Inline;
            this.Inlines.Add(inline);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


ito*_*son 11

这是不可能的,因为该TextBlock.Inlines属性不是依赖属性.只有依赖项属性才能成为数据绑定的目标.

根据您的具体布局要求,你可以做到这一点使用ItemsControl,其ItemsPanel设置到WrapPanelItemsSource设置到您的收藏.(这里可能需要进行一些实验,因为Inline它不是a UIElement,所以它的默认渲染可能是使用ToString()而不是显示.)

或者,您可能需要构建一个新控件,例如MultipartTextBlock,使用可绑定PartsSource属性和TextBlock作为其默认模板.当PartsSource设置时,您的控件将附加CollectionChanged事件处理程序(直接或通过CollectionChangedEventManager),并TextBlock.InlinesPartsSource集合更改时从代码更新集合.

在任何一种情况下,如果您的代码Inline直接生成元素(因为Inline不能同时在两个地方使用),可能需要谨慎.您也可以考虑公开文本,字体等的抽象模型(即视图模型)并Inline通过a 创建实际对象DataTemplate.这也可以提高可测试性,但显然增加了复杂性和工作量.


小智 9

这是利用 WPF 行为/附加属性的替代解决方案:

public static class TextBlockExtensions
{
    public static IEnumerable<Inline> GetBindableInlines ( DependencyObject obj )
    {
        return (IEnumerable<Inline>) obj.GetValue ( BindableInlinesProperty );
    }

    public static void SetBindableInlines ( DependencyObject obj, IEnumerable<Inline> value )
    {
        obj.SetValue ( BindableInlinesProperty, value );
    }

    public static readonly DependencyProperty BindableInlinesProperty =
        DependencyProperty.RegisterAttached ( "BindableInlines", typeof ( IEnumerable<Inline> ), typeof ( TextBlockExtensions ), new PropertyMetadata ( null, OnBindableInlinesChanged ) );

    private static void OnBindableInlinesChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        var Target = d as TextBlock;

        if ( Target != null )
        {
            Target.Inlines.Clear ();
            Target.Inlines.AddRange ( (System.Collections.IEnumerable) e.NewValue );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的 XAML 中,像这样使用它:

<TextBlock MyBehaviors:TextBlockExtensions.BindableInlines="{Binding Foo}" />
Run Code Online (Sandbox Code Playgroud)

这使您不必从 TextBlock 继承。它也可以使用ObservableCollection而不是IEnumerable 工作,在这种情况下,您需要订阅集合更改。


tim*_*ath 6

在WPF的第4版中,您将能够绑定到Run对象,这可以解决您的问题.

我过去通过重写ItemsControl并将文本显示为ItemsControl中的项来解决了这个问题.看看WPF博士在这方面做过的一些教程:http://www.drwpf.com


Law*_*Man 6

感谢弗兰克的解决方案。我不得不做一些小改动才能让它对我有用。

public class BindableTextBlock : TextBlock
{
    public ObservableCollection<Inline> InlineList
    {
        get { return (ObservableCollection<Inline>) GetValue(InlineListProperty); }
        set { SetValue(InlineListProperty, value); }
    }

    public static readonly DependencyProperty InlineListProperty =
        DependencyProperty.Register("InlineList", typeof (ObservableCollection<Inline>), typeof (BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

    private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock textBlock = (BindableTextBlock) sender;
        textBlock.Inlines.Clear();
        textBlock.Inlines.AddRange((ObservableCollection<Inline>) e.NewValue);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 最好在 `AddRange` 之前添加一个 `textBlock.Inlines.Clear()`,以便在更改时重置内联 (5认同)