C# 将列表绑定到文本框赢得';如果可观察到收集,则不更新

C# 将列表绑定到文本框赢得';如果可观察到收集,则不更新,c#,wpf,xaml,C#,Wpf,Xaml,我正在WPF中创建一个自定义控件。我将列表绑定到依赖项属性。这反过来又绑定到一个列表框,该列表框按预期显示所有项目 现在我想将此列表中的一项绑定到Textblock,因此我将整个列表绑定到Textblock。我有一个转换器,可以提取我想要的单个项目 它工作得很好,但出于几个原因,我想使用observeablecollection而不是List 奇怪的是,当我在运行时更改我的observabalcollection中的值时,该值会显示在列表框中(成功),但不会显示在我的文本块中。转换器甚至没有被击

我正在WPF中创建一个自定义控件。我将
列表
绑定到
依赖项属性
。这反过来又绑定到一个
列表框
,该列表框按预期显示所有项目

现在我想将此列表中的一项绑定到
Textblock
,因此我将整个列表绑定到
Textblock
。我有一个
转换器
,可以提取我想要的单个项目

它工作得很好,但出于几个原因,我想使用
observeablecollection
而不是
List

奇怪的是,当我在运行时更改我的
observabalcollection
中的值时,该值会显示在
列表框中(成功),但不会显示在我的
文本块中。
转换器
甚至没有被击中

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        this.Errors = new ObservableCollection<IEventDetail>();
        this.Errors.CollectionChanged += Errors_CollectionChanged;
        var bw = new BackgroundWorker();
        bw.DoWork += ((o, e) =>
        {
            System.Threading.Thread.Sleep(1500);
            Dispatcher.Invoke(() =>
            {
                this.Errors.Add(new MyEvents("example of some detail", "Failed title"));
            });

            System.Threading.Thread.Sleep(2500);

            Dispatcher.Invoke(() =>
            {
                this.Errors.Add(new MyEvents("Another example", "Failed title 2"));
            });
        });
        bw.RunWorkerAsync();//background worker for testing/debugging only
    }

    private void Errors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        OnPropertyChanged("Errors");
    }

    private ObservableCollection<IEventDetail> _errors;
    public ObservableCollection<IEventDetail> Errors
    {
        get
        {
            return this._errors;
        }
        set
        {
            this._errors = value;
            OnPropertyChanged("Errors");
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

是否需要执行其他操作?

如注释
TextBlock
it中所述,它只绑定到
事件
属性更改(不是它的项),因此除非创建新的集合实例,否则它不会触发。对
INotifyCollectionChanged
作出反应是
ItemsSource
属性的特征

解决方案1

让一切保持现状,只需给
TextBlock
一些名字

<TextBlock Text="{Binding ...}" x:Name="myTextBlock"/>
解决方案2

使用自定义属性创建您自己的集合类,该类继承自
ObservableCollection
,该属性将执行转换器所执行的操作

public class MyObservableCollection<T> : ObservableCollection<T>
{
    private string _convertedText;

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        this.ConvertedText = ...; // <- do here what your IValueConverter does
    }

    public string ConvertedText
    {
        get { return _convertedText; }
        private set
        {
            _convertedText = value;
            OnPropertyChanged(new PropertyChangedEventArgs("ConvertedText"));
        }
    }
}
公共类MyObservableCollection:ObservableCollection
{
私有字符串_convertedText;
CollectionChanged上的受保护覆盖无效(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
基础。变更的集合(e);

this.ConvertedText=…;//问题必须在xaml上,onpropertychanged在observableCollection中是隐式的。(您能发布xaml吗?)尝试设置explicti双向绑定和UpdataSourceTrigger=PropertyChanged@Xilmiki,谢谢,但是,绑定正在
列表框
,而不是
文本块
。所以我不确定为什么我需要双向绑定,或者甚至使用
UpdateSourceTrigger
请尝试设置显式双向绑定和UpdateSourceTrigger=Pro文本框绑定中的pertychange,我在我的WPF项目中遇到了相同的问题。@Dave it
TextBlock
它只绑定到
Events
属性更改(不是它的项)因此,除非创建新集合,否则它不会触发。即使手动引发
错误的属性更改事件
,也不会起作用,因为UI会看到集合实例实际上与
INotifyCollectionChanged
相同,这是
项资源
属性的特征。转换器做什么?您可以d基于添加自定义属性的
observateCollection
创建集合类型,该属性执行转换器的操作,或者在用户控件中订阅
CollectionChanged
事件,并手动刷新
TextBlock
<TextBlock Text="{Binding ...}" x:Name="myTextBlock"/>
public partial class MyUserControl : UserControl
{
    public static readonly DependencyProperty EventsProperty =
                               DependencyProperty.Register("Events", 
                                   typeof(IEnumerable), 
                                   typeof(MyUserControl), 
                                   new PropertyMetadata(EventsPropertyChanged));

    private static void EventsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((MyUserControl)d).EventsPropertyChanged(e);
    }

    private void EventsPropertyChanged(DependencyPropertyChangedEventArgs args)
    {
        var newCollection = args.NewValue as INotifyCollectionChanged;
        if (newCollection != null)
            newCollection.CollectionChanged += (s, e) => myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
    }

    public IEnumerable Events
    {
        get { return (IEnumerable)GetValue(EventsProperty); }
        set { SetValue(EventsProperty, value); }
    }

}
public class MyObservableCollection<T> : ObservableCollection<T>
{
    private string _convertedText;

    protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        this.ConvertedText = ...; // <- do here what your IValueConverter does
    }

    public string ConvertedText
    {
        get { return _convertedText; }
        private set
        {
            _convertedText = value;
            OnPropertyChanged(new PropertyChangedEventArgs("ConvertedText"));
        }
    }
}