从BackgroundWorker / ProgressChanged事件更新ObservableCollection

时间:2011-04-29 13:50:43

标签: c# wpf

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

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

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

[编辑] Per 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(未显示)调用,启动RefreshWorker.RunWorkerAsync(this.ComputerList);

我已经打了好几天了,所以我现在已经到了我实际上不再试图直接修改ObservableCollection中引用的对象了。因此,丑陋的循环遍历ComputerList集合并直接设置属性。

有什么想法在这里发生以及如何解决它?

2 个答案:

答案 0 :(得分:1)

当您更改集合内部的项目的属性时,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");
                }
            }
        }
    }

答案 1 :(得分:0)

长时间没有使用过这个,但是你不需要实现像INotifyPropertyChanged这样的东西吗?