繁体   English   中英

从 BackgroundWorker/ProgressChanged 事件更新 ObservableCollection

[英]Update ObservableCollection from BackgroundWorker/ProgressChanged Event

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

然而,ping 可能需要一些时间,每台计算机最多需要几秒钟,具体取决于它是否必须超时。 所以我在BackgroundWorker中运行实际 ping 并使用ReportProgress方法更新 UI。

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

[编辑]根据 Fantasyfix,这里的关键是:“仅当列表更改(添加、交换、删除)时,ObservableCollection 才会触发。” 由于我是在设置 object 的属性而不是对其进行修改,因此 ObservableCollection 没有通知更改列表——它不知道如何进行。 实施INotifyPropertyChanged后一切正常。 相反,用新的更新实例替换列表中的 object 也可以解决问题。 [/编辑]

顺便说一句,我正在使用 C# 3.5 而我不在 position 中,我可以在其中添加额外的依赖项,例如 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;
            }
        }
    }
}

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

我已经为此奋斗了几天,所以我现在已经到了我实际上不再尝试直接修改ObservableCollection中引用的对象的地步。 因此,丑陋的循环通过 ComputerList 集合并直接设置属性。

知道这里发生了什么以及如何解决它吗?

当您更改集合内项目的属性时,observableCollection 不会触发(它甚至应该如何知道)。 ObservableCollection 仅在列表更改(添加、交换、删除)时触发。

如果你想检测 ComputerEntry 属性的变化,class 必须实现 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 这样的实现吗?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM