WPF 可观察集合<T> UI 框架元素嵌套 2 个类深度



我已经创建了一个自定义FrameWorkElement (Battery.cs)来在UI中向用户表示数据。在Battery.cs类中,我有几个dependencyProperties,以便UI可以监视各种更改并在更改时重新呈现对象。

我把ObservableCollection在我的MainWindowViewModel.cs中,它通过ListBox绑定到主视图。

一切都工作正常,但这只是为了测试,因为我需要将集合移动到另一个将要管理/更新电池的类中。这种管理将异步发生,因此我遇到了很多问题与电池.cs类内的DependencyProperties调用,因为它们是在UI线程上,而不是管理/进程线程。

所以我删除了DependencyProperties,并试图将DependencyProperty移动到MainWindowViewModel.cs。现在我没有得到关于哪个线程拥有所有权的错误,我可以看到ObservableCollection中的电池正在更新。然而,OnRender方法永远不会被UI调用。所以电池永远不会被渲染/显示了。

下面是MainWindowViewModel.cs

中的DependencyProperty的代码
public static readonly DependencyProperty batteriesProperty = DependencyProperty.Register(
"Batteries",
typeof(ObservableCollection<Battery>), 
typeof(MainWindow),
new UIPropertyMetadata(new ObservableCollection<Battery>()));
public ObservableCollection<Battery> Batteries
{
get { return tbModel.Modules[0].batteries; }
}

我想我的主要问题可能在这一行

new UIPropertyMetadata(new ObservableCollection<Battery>()));

然而,我似乎无法弄清楚它应该是什么,或者如何调整代码,使UI更新图形一旦我调用InvalidateVisual在Battery.cs类。

public void UpdatePacket(Packet packet)
{
packet= packet;
Voltage = packet.Voltage;
InvalidateVisual();
}

InvalidateVisual()方法正在执行,但是OnRender覆盖从未被执行。

  1. 使ViewModel派生自dependdecyobject是没有意义的。这只会使实现变得复杂和混乱。

  2. ObservableCollection类型的Batteries属性必须是ViewModel中一个常规的CLR只读属性。

public ObservableCollection<Battery>() Batteries {get;}
= new ObservableCollection<Battery>();
  1. 如果ViewModel的实例存在于应用程序的整个会话中,那么在ViewModel构造函数中,同步绑定到这个集合。
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
if (Dispatcher.CheckAccess())
{
BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot);
}
else
{
Dispatcher.Invoke(()=>BindingOperations.EnableCollectionSynchronization(Batteries, ((ICollection)Batteries).SyncRoot));
}
// Some Code
} 
  1. 如果ViewModel的实例可以被销毁,相互替换,那么绑定的同步将保留对ViewModel实例的引用,因此该实例将不会被GC删除。此外,绑定的同步并不总是为处理集合提供线程安全性。
    在这些情况下,您没有使用BindingOperations。EnableCollectionSynchronization()。
    但是,您总是只能在Dispatcher线程中使用集合。
protected static readonly Dispatcher Dispatcher = Application.Current.Dispatcher;
public MainWindowViewModel()
{
// Some Code
} 
... SomeMethod(...) 
{
// Some Code
if (Dispatcher.CheckAccess())
{
Batteries.Add(...);
}
else
{
Dispatcher.Invoke(()=>Batteries.Add(...));
// Or
Dispatcher.InvokeAsync(()=>Batteries.Add(...));
}

// Some Code
}