问题描述
我正在尝试创建一个显示格式化文本的列表框。我希望能够从代码中更改格式。
为了显示格式化文本,我选择了 TextBlock 并打算使用 TextBlock.Inlines 集合进行格式化。 TextBlock.Inlines 是不可绑定的,所以我创建了从 TextBlock 派生的新类 BindableTextBlock。这个类有一个依赖属性 InlineList,我试图将它绑定到模型中的 InlinesColl ObservableCollection。
问题是 InlinesColl 中的更改不会通知我的 InlineList 关于 PropertyChanged 事件。绑定仅在 BindableTextBlock 对象创建时运行一次,以后再也不运行了。
知道为什么吗?
XAML:
<ListBox x:Name="PART_lb" VerticalAlignment="Stretch" ItemsSource="{Binding ItemColl}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" >
<ListBox.ItemTemplate>
<DataTemplate>
<local:BindableTextBlock InlineList="{Binding Path=InlinesColl}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
BindableTextBlock 类:
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);
}
}
模型类
public class TextBlockModel
{
ObservableCollection<Inline> _inlinesColl = new ObservableCollection<Inline>();
public ObservableCollection<Inline> InlinesColl
{
get { return _inlinesColl; }
set {_inlinesColl = value; }
}
}
带有 ListBox ItemSource 集合的 ViewModel
ObservableCollection<TextBlockModel> _itemColl = new ObservableCollection<TextBlockModel>();
public ObservableCollection<TextBlockModel> ItemColl
{
get { return _itemColl; }
set { _itemColl = value; }
}
测试项目here
解决方法
在你的情况下 - 当集合中的项目被添加\删除时,你没有处理案例
需要深入了解并在分配新集合时订阅 CollectionChanged。
public class BindableTextBlock : TextBlock
{
static int Cntr = 0;
public BindableTextBlock()
{
Console.WriteLine("BindableTextBlock constructor " + Cntr);
Cntr++;
}
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;
// subscribe to collection changed
textBlock.UpdateInlineListSource((ObservableCollection < Inline > )e.OldValue,(ObservableCollection < Inline > )e.NewValue);
}
public void UpdateInlineListSource(ObservableCollection<Inline> oldCollection,ObservableCollection<Inline> newCollection)
{
if (oldCollection!=null)
oldCollection.CollectionChanged -= OnCollectionChanged;
if (newCollection != null)
{
newCollection.CollectionChanged += OnCollectionChanged;
OnCollectionChanged(this,new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCollectionChanged(this,new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,newCollection));
}
}
private void OnCollectionChanged(object sender,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var newItems = e.NewItems?.Cast<Inline>()?.ToList() ?? new List<Inline>();
var oldItems = e.OldItems?.Cast<Inline>()?.ToList() ?? new List<Inline>();
// changed source
if (e.Action==NotifyCollectionChangedAction.Reset)
this.Inlines.Clear();
foreach (var itemForDelete in oldItems)
{
if (this.Inlines.Contains(itemForDelete))
this.Inlines.Remove(itemForDelete);
}
foreach (var itemsForAdd in newItems)
{
this.Inlines.Add(itemsForAdd);
}
}
}