【问题标题】:WPF C# ObservableCollection Not updating GUIWPF C# ObservableCollection 不更新 GUI
【发布时间】:2014-04-06 05:51:17
【问题描述】:

感谢tencntraze的建议,这个问题已经解决了。

请参阅此问题的底部和 tencntraze 的答案以获取解决方案。

我的程序中有一个相当奇怪的错误,我目前正在努力解决。

我正在开发一个 MVVM 应用程序,我的视图中有一个简单的 ListBox,它的 ItemsSource 绑定到字符串的 ObservableCollection。每当我向这个集合添加一个项目时,它 /does/ 触发一个事件以通知集合已更改,但这不会在运行时反映在 GUI 上,除非我在程序运行时尝试手动调整窗口大小,几乎就像调整窗口大小会强制 GUI 刷新一样。

相关控件的XAML如下

<ListBox ItemsSource="{Binding Path=LogWindow}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="0,0,0,0" />

如您所见,这是完全正常的。在此视图的 ViewModel 中,我有一个名为 LogWindow 的公共属性,定义如下

public ObservableCollection<string> LogWindow
    {
        get
        {
            return _logWindow;
        }

        set
        {
            _logWindow = value;
            OnPropertyChanged("LogWindow");
        }
    }

这个的私有成员如下

private ObservableCollection<string> _logWindow;

在 ViewModel 构造函数中,我初始化 _logWindow 并将 LogWindow CollectionChanged 事件连接到一个处理程序,该处理程序又触发一个 PropertyChanged 通知。

我的代码如下

public MainWindowViewModel(IDialogService dialogService)
        {
            ... skipped for brevity
            LogWindow = new ObservableCollection<string>();
            LogWindow.CollectionChanged += LogWindow_CollectionChanged;
            ...
        }

CollectionChanged 事件处理程序如下

void LogWindow_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged("LogWindow");
        }

最后,当我的视图收到通知说它收到了来自模型的新消息时,我将一个新字符串添加到 observable 集合中,如下所示

LogWindow.Add(_vehicleWeightManager.SerialMessage);

当 LogWindow.Add 发生时,我的事件侦听器触发并且我的断点被命中以确认代码达到了如下图所示

在此之后,我的代码调用了从我的 ViewModelBase 继承的 OnPropertyChanged 函数,该函数对于我的其他 GUI 元素(如标签等)来说很好,因此几乎可以肯定这不是问题。

我对 LogWindow 应用了一个监视来跟踪集合确实被添加到,如下图所示

所以在这个阶段我很茫然。如果我以某种方式调整窗口大小,我的 GUI 只会更新,否则没有视觉更新。数据存在于绑定的属性中,并且当新项目添加到属性中时,GUI 会收到警报...有没有人有任何可能有帮助的见解?

感谢 tencntraze 和 kallocain 的建议,问题已解决。

问题是由另一个线程试图添加到导致错误行为的 LogWindow 集合引起的。我没有按照建议使用 Dispatcher,而是使用了 TaskFactory 代码,如下所示。

TaskFactory uiFactory;
...
...
public MainWindowViewModel(IDialogService dialogService)
        {
            ...
            _logWindow = new ObservableCollection<string>();
            uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
        }
...
...
void _vehicleWeightManager_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            ...
            if (e.PropertyName == "VehicleWeight")
            {
                // Marshal the SerialMessage property to the GUI thread.
                uiFactory.StartNew(() => LogWindow.Add(_vehicleWeightManager.SerialMessage.Replace("\n", ""))).Wait();
            }
        }

上述方法的进一步阅读可以找到here

【问题讨论】:

  • 只是一个建议:删除不必要的集合更改处理程序。通过告诉 WPF “嘿,我在列表中添加了一些内容,请再次获取整个列表”,您基本上破坏了 ObservableCollection 的全部目的。可观察列表将引发适当的事件,无需引发列表本身已更改。当您实际使用完全不同的列表引用时会使用此选项。
  • 你不需要为 LogWindow 提高 PropertyChanged,仅仅因为添加了一个元素。 ListBox 知道,它已绑定到ObservableCollection 并将订阅集合的CollectionChanged 事件。但这不应该导致你的问题。你能描述一下,何时以及如何调用LogWindow.Add? “当我的视图收到通知时”是什么意思?
  • 感谢您的提示,我按照上面的方式进行操作,模糊地希望它会强制更新 GUI,最初我按照您的建议设置了它,哈哈。
  • Kallocain,我有另一个类处理串行 com 端口上的串行通信,我订阅了 WeightManager 类中的属性更改事件,该变量称为 _vehicleWeightManager。当 WeightManager 类实例化的 SerialMessage 属性发生变化时,视图模型会接收到这一点并将其接收到的消息添加到可观察集合中。
  • 这为我的理论提供了更多证据,即您可能需要在 UI 线程上调用集合的更新,我会试一试。

标签: c# wpf user-interface mvvm observablecollection


【解决方案1】:

如果您在另一个线程上接收消息,则需要将其调用回 UI 线程,否则可能会出现不可预知的行为。有时这是一个例外,有时它什么都不做。

this.Dispatcher.BeginInvoke(new Action(() =>
{
    LogWindow.Add(_vehicleWeightManager.SerialMessage);
}));

【讨论】:

  • Tencntraze,你的假设是绝对正确的。正如上面的 cmets 所讨论的,它是由 .Net 中包含的串行通信 API 中的线程操作被抽象掉的。我真的很感激这个建议,因为它解决了我在那里遇到的问题。作为记录,我使用 TaskFactory 代替调度程序。我将它的 TaskScheduler 设置为 Current Synchronization 上下文以执行相同的操作,然后在添加到 Collection 的地方,我开始了一个新任务以添加到 LogWindow 属性。
猜你喜欢
  • 2014-08-31
  • 1970-01-01
  • 2017-08-31
  • 2021-11-15
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
  • 2023-03-08
相关资源
最近更新 更多