我已经创建了一个自定义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覆盖从未被执行。
-
使ViewModel派生自dependdecyobject是没有意义的。这只会使实现变得复杂和混乱。
-
ObservableCollection类型的Batteries属性必须是ViewModel中一个常规的CLR只读属性。
public ObservableCollection<Battery>() Batteries {get;}
= new ObservableCollection<Battery>();
- 如果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
}
- 如果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
}