问题
I'm trying to create a ListBox that displays formatted text. I want to be able to change formatting from code.
For displaying formatted text I chose TextBlock and intend to use the TextBlock.Inlines collection for formatting. TextBlock.Inlines is not bindable so I created new class BindableTextBlock derived from TextBlock. This class has one dependency property InlineList that I'm trying to bind to InlinesColl ObservableCollection in Model.
The problem is that changes in InlinesColl don't notify my InlineList about PropertyChanged event. Binding is functioned just once at BindableTextBlock object creation time and never after.
Any ideas why?
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 class:
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);
}
}
Model class
public class TextBlockModel
{
ObservableCollection<Inline> _inlinesColl = new ObservableCollection<Inline>();
public ObservableCollection<Inline> InlinesColl
{
get { return _inlinesColl; }
set {_inlinesColl = value; }
}
}
ViewModel with collection for ListBox ItemSource
ObservableCollection<TextBlockModel> _itemColl = new ObservableCollection<TextBlockModel>();
public ObservableCollection<TextBlockModel> ItemColl
{
get { return _itemColl; }
set { _itemColl = value; }
}
Test project here
回答1:
In your case - you didn't handle case when item inside collection added\deleted
Need go deeply and subscirbe to CollectionChanged when new collection will be assigned.
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);
}
}
}
来源:https://stackoverflow.com/questions/65447022/wpf-binding-inside-listbox-itemtemplate-to-observablecollection-source-source