wil*_*ill 27 data-binding wpf textblock inlines
我的WPF应用程序从后端服务接收消息流,我需要在UI中显示.这些消息差异很大,我想为每条消息提供不同的视觉布局(字符串格式,颜色,字体,图标等等).
我希望能够为每条消息创建一个内联(Run,TextBlock,Italic等),然后以某种方式将它们全部放入一个ObservableCollection<>并在UI中使用我的TextBlock.Inlines上的WPF数据绑定魔法.我找不到怎么做,这可能吗?
小智 13
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设置到WrapPanel其ItemsSource设置到您的收藏.(这里可能需要进行一些实验,因为Inline它不是a UIElement,所以它的默认渲染可能是使用ToString()而不是显示.)
或者,您可能需要构建一个新控件,例如MultipartTextBlock,使用可绑定PartsSource属性和TextBlock作为其默认模板.当PartsSource设置时,您的控件将附加CollectionChanged事件处理程序(直接或通过CollectionChangedEventManager),并TextBlock.Inlines在PartsSource集合更改时从代码更新集合.
在任何一种情况下,如果您的代码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 工作,在这种情况下,您需要订阅集合更改。
在WPF的第4版中,您将能够绑定到Run对象,这可以解决您的问题.
我过去通过重写ItemsControl并将文本显示为ItemsControl中的项来解决了这个问题.看看WPF博士在这方面做过的一些教程:http://www.drwpf.com
感谢弗兰克的解决方案。我不得不做一些小改动才能让它对我有用。
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)
| 归档时间: |
|
| 查看次数: |
18721 次 |
| 最近记录: |