从BackgroundWorker/ProgressChanged事件中更新ObservableCollection

本文关键字:更新 ObservableCollection 事件 ProgressChanged BackgroundWorker | 更新日期: 2023-09-27 17:53:38

我正在编写一个简单的计算机故障排除工具。基本上,它只是一个WPF窗口,ListBox绑定到ObservableCollection<ComputerEntry>,其中ComputerEntry是一个简单的类,包含计算机主机名和状态。该工具所做的只是ping列表中的每个计算名称,如果收到响应,则更新ComputerEntry.Status以指示计算机已连接到某个网络…

然而,ping可能需要一些时间,根据是否需要超时,每台计算机最多需要几秒钟。所以我在BackgroundWorker中运行实际的ping,并使用ReportProgress方法来更新UI。

不幸的是,在对象更新后,ObservableCollection似乎没有引发PropertyChanged事件。集合使用新信息进行更新,但是ListBox中的状态永远不会改变。大概是因为它不知道集合发生了变化。

[编辑]根据fantasticfix,这里的关键是:"ObservableCollection仅在列表被更改(添加、交换、删除)时触发。"由于我是在设置对象的属性而不是修改它,ObservableCollection没有通知列表更改——它不知道如何通知。在实现INotifyPropertyChanged之后,一切都很好。相反,用一个新的更新实例替换列表中的对象也会解决这个问题。(/编辑)

顺便说一句,我正在使用c# 3.5,我不能在一个位置,我可以添加额外的依赖,如TPL。

作为一个简化的例子[没有更多的工作就不会编译…]:

//Real one does more but hey its an example...
public class ComputerEntry
{
    public string ComputerName { get; private set; }
    public string Status { get; set; }
    public ComputerEntr(string ComputerName)
    {
        this.ComptuerName = ComputerName;
    }
}

//...*In Window Code*...
private ObservableCollection<ComputerEntry> ComputerList { get; set; }
private BackgroundWorker RefreshWorker;
private void Init()
{
     RefreshWorker = new BackgroundWorker();
     RefreshWorker.WorkerReportsProgress = true;
     RefreshWorker.DoWork += new DoWorkEventHandler(RefreshWorker_DoWork);
     RefreshWorker.ProgressChanged += new ProgressChangedEventHandler(RefreshWorker_ProgressChanged);
}
private void Refresh()
{
    RefreshWorker.RunWorkerAsync(this.ComputerList);
}
private void RefreshWorker_DoWork(object sender, DoWorkEventArgs e)
{
    List<ComputerEntry> compList = e as List<ComputerEntry>;
    foreach(ComputerEntry o in compList)
    {
        ComputerEntry updatedValue = new ComputerEntry();
        updatedValue.Status = IndicatorHelpers.PingTarget(o.ComputerName);
        (sender as BackgroundWorker).ReportProgress(0, value);
    }
}
private void RefreshWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    ComputerEntry updatedValue = new ComputerEntry();
    if(e.UserState != null)
    {
        updatedValue = (ComputerEntry)e.UserState;
        foreach(ComputerEntry o in this.ComputerList)
        {
            if (o.ComputerName == updatedValue.ComputerName)
            {
                o.Status = updatedValue.Status;
            }
        }
    }
}

很抱歉混乱,但它的所有支持代码相当长。无论如何,void Refresh()是从DispatcherTimer(未显示)调用的,该DispatcherTimer启动RefreshWorker.RunWorkerAsync(this.ComputerList);

我已经为此战斗了几天,所以我现在已经到了我实际上不再试图修改ObservableCollection中直接引用的对象的地步。因此,在ComputerList集合中循环并直接设置属性是很难看的。

你知道这里发生了什么,我该如何解决它吗?

从BackgroundWorker/ProgressChanged事件中更新ObservableCollection

当你改变集合内项目的属性时,observableCollection不会触发(它怎么知道呢)。ObservableCollection仅在列表被更改(添加、交换、删除)时触发。

如果你想检测ComputerEntry属性的变化,该类必须实现INotifyPropertyChange接口(如果你知道MVVM,它就像一个轻量级的MVVM模式)

public class ComputerEntry : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        private void RaisePropertyChanged(String propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private String _ComputerName;
        public String ComputerName
        {
            get
            {
                return _ComputerName;
            }
            set
            {
                if (_ComputerName != value)
                {
                    _ComputerName = value;
                    this.RaisePropertyChanged("ComputerName");
                }
            }
        }
    }

在很长一段时间没有使用这个,但你不需要像INotifyPropertyChanged实现的东西吗?