I am struggling with the best way to handle a WPF application that has dynamic data updates. Should I use the rich data binding provided by WPF or not?
我正在努力处理具有动态数据更新的WPF应用程序的最佳方法。我应该使用WPF提供的丰富数据绑定吗?
I will simplify my problem domain as much as possible in the discussion below, and briefly mention some solutions. I am very interested in whatever comments this community can provide.
我将在下面的讨论中尽可能简化我的问题域,并简要提一些解决方案。我对这个社区可以提供的任何评论都很感兴趣。
Simplified Problem Domain: Suppose you have a system with about 1,000 items, each with a unique “name”, and a dynamic “speed” and “location”. The data for these items is updated 60x per second. You wish to display this data to the user in a variety of UI’s – everything from “labels displaying numbers” to graphical dials/meters/gauges/etc. Perfect job for WPF – separate the visuals from the rest of the application. How would you design this system?
简化问题域:假设您的系统包含大约1,000个项目,每个项目都有唯一的“名称”,以及动态“速度”和“位置”。这些项目的数据每秒更新60次。您希望在各种UI中向用户显示此数据 - 从“显示数字的标签”到图形表盘/仪表/仪表/等等。 WPF的完美工作 - 将视觉效果与应用程序的其余部分分开。你会如何设计这个系统?
My first solution: My first solution was to define an object with speed and location properties (doubles) called “DataItem”, define an interface with a name property (string). Controls that display the data would have to implement the name interface, A “DataManager” was created that scanned for FrameworkElements that implemented the interface, built a list a name/FrameworkElements pairs at initialization time. Then, 60 times per second, the list of 1,000 DataItems was updated, and the DataContext of each matching FrameworkElement was set to the DataItem object. This worked, performance was acceptable (particularly if data was not always changing).
我的第一个解决方案:我的第一个解决方案是定义一个名为“DataItem”的具有速度和位置属性(双精度)的对象,定义一个带有name属性(string)的接口。显示数据的控件必须实现名称接口,创建了一个“DataManager”,用于扫描实现接口的FrameworkElements,在初始化时为列表构建名称/ FrameworkElements对。然后,每秒60次,更新1,000个DataItem的列表,并将每个匹配的FrameworkElement的DataContext设置为DataItem对象。这是有效的,性能是可以接受的(特别是如果数据并不总是在变化)。
My second solution addressed the problem that the UI Controls in the first solution all had to implement some interface – yuk. I want to use out-of-the-box-unmodified WPF controls (in some cases). So, the second solution was to define an “Attached property” (I put it on the DataManager object), so you could – in xaml – do stuff like
我的第二个解决方案解决了第一个解决方案中的UI控件都必须实现一些接口的问题 - yuk。我想使用开箱即用的未经修改的WPF控件(在某些情况下)。所以,第二个解决方案是定义一个“附加属性”(我把它放在DataManager对象上),所以你可以 - 在xaml中 - 做类似的东西
<Label DataManager.Name = "objectname" Content="{Binding}" />
Somehow, this solution still does not seem right. My third solution was to look into implementing a custom DataSourceProvider. I was not successful. I could not get my head around the data source provider model, and how it would be used in this case.
不知何故,这个解决方案似乎仍然不对。我的第三个解决方案是研究实现自定义DataSourceProvider。我没有成功。我无法理解数据源提供程序模型,以及在这种情况下如何使用它。
Right now, I am looking at the CodePlex “dynamic data display” project posted by Microsoft Research. That project is all about plotting/graphing dynamic data, but there could be some ideas there. So here I am on * – normally a place with short questions and quick answers. :-)
现在,我正在研究Microsoft Research发布的CodePlex“动态数据显示”项目。该项目完全是关于绘制/绘制动态数据的,但可能会有一些想法。所以我在*上 - 通常是一个简短问题和快速答案的地方。 :-)
I am very new to WPF and would appreciate any thoughts anyone has on these issues. Please keep in mind that the problem domain specified here is simplified, so I need a robust solution. There are actually many different types of data objects, each with different properties; and data updates are not the same rate for each object, and the UI controls display single items and groups of items, the data comes in from many sources, etc.
我是WPF的新手,非常感谢任何人对这些问题的看法。请记住,此处指定的问题域已经简化,因此我需要一个强大的解决方案。实际上有许多不同类型的数据对象,每个对象具有不同的属性;并且每个对象的数据更新速率不同,UI控件显示单个项目和项目组,数据来自多个来源,等等。
1 个解决方案
#1
6
You're almost there. You want to data bind at two levels.
你快到了。您希望在两个级别进行数据绑定。
First - have your DataItems implement INotifyPropertyChanged
or have them derive from DispatchingObject
and implement dependency properties for each of the values you want to bind to
首先 - 让您的DataItem实现INotifyPropertyChanged或让它们派生自DispatchingObject并为您要绑定到的每个值实现依赖项属性
Second - keep all of your DataItems in one of ObservableCollection<DataItem>
or a DataTable. You need a collection that supports INotifyCollectionChanged
or IBindingList
.
第二 - 将所有DataItem保存在ObservableCollection
Third - use some kind of container control (listbox, 3rd pary grid control, etc) to bind the list of DataItems to your app
第三 - 使用某种容器控件(列表框,第三个网格控件等)将DataItems列表绑定到您的应用程序
Fourth - Define a DataTemplate
to give each of the DataItems a visual look
第四 - 定义DataTemplate,使每个DataItem都具有视觉效果
Fifth - if needed, use a DataTemplateSelector
to dynamically choose the DataTemplate for different kinds of DataItems
第五 - 如果需要,使用DataTemplateSelector为不同类型的DataItem动态选择DataTemplate
Here's a minimal bit of xaml to get you started
这是一个最小的xaml来帮助您入门
<ListBox ItemsSource="{Binding ...}" ItemTemplateSelector="{StaticResource DTSelector}">
#1
6
You're almost there. You want to data bind at two levels.
你快到了。您希望在两个级别进行数据绑定。
First - have your DataItems implement INotifyPropertyChanged
or have them derive from DispatchingObject
and implement dependency properties for each of the values you want to bind to
首先 - 让您的DataItem实现INotifyPropertyChanged或让它们派生自DispatchingObject并为您要绑定到的每个值实现依赖项属性
Second - keep all of your DataItems in one of ObservableCollection<DataItem>
or a DataTable. You need a collection that supports INotifyCollectionChanged
or IBindingList
.
第二 - 将所有DataItem保存在ObservableCollection
Third - use some kind of container control (listbox, 3rd pary grid control, etc) to bind the list of DataItems to your app
第三 - 使用某种容器控件(列表框,第三个网格控件等)将DataItems列表绑定到您的应用程序
Fourth - Define a DataTemplate
to give each of the DataItems a visual look
第四 - 定义DataTemplate,使每个DataItem都具有视觉效果
Fifth - if needed, use a DataTemplateSelector
to dynamically choose the DataTemplate for different kinds of DataItems
第五 - 如果需要,使用DataTemplateSelector为不同类型的DataItem动态选择DataTemplate
Here's a minimal bit of xaml to get you started
这是一个最小的xaml来帮助您入门
<ListBox ItemsSource="{Binding ...}" ItemTemplateSelector="{StaticResource DTSelector}">