如何在WinForms GUI控件和客户端类之间同步数据?

什么方法被认为是保持GUI控件中的数据结构与应用程序维护的数据结构同步的“标准”?

例如:在WinForms中,如果创建一个ListView实例,而不是将其指向表示列表中显示的项的数据结构,则必须以编程方式实例化ListViewItem并调用.Add方法来手动复制它们,一个接一个地进入ListView本身内部的数据结构。 从线程的角度来看,这是有道理的,并且在呈现的上下文中也有意义,控件应该需要存在专用数据结构,控件单独知道有关维护的细节。

但是,这会产生两个问题:

冗余:如果客户端类管理自己的实体列表,允许用户从WinForms UI中选择它们,则必须通过以下方法读取,转换然后在UI控件内重新创建整个列表:.Add( ListViewItem项目)列表现在占用两倍的内存。

复杂性:由于现在存在两个列表,因此必须以编程方式确保它们保持同步。 这可以通过从客户端类的集合对象触发的事件来实现,或者程序员可以在添加/删除另一个列表时始终在一个列表中添加/删除。

我见过许多实例,程序员将使用像ListView这样的UI元素作为用于维护列表的实际集合对象的快捷方式。 例如,每个用户输入的项目将立即插入ListView,然后当访问用户的entires时,应用程序只是遍历ListView。 当您从UI逻辑中正确分离业务/应用程序逻辑时,此方法无法应用。

总的来说,将应用程序数据存储在GUI控件内部的数据结构中似乎并不正确。 同样,存储两个列表并使它们保持程序化同步似乎也不是一个优雅的解决方案。 理想情况下,只需要为UI元素提供对位于客户端范围内的列表的引用。

那么,解决这个问题的“正确”方法是什么?

每个UI控件都需要一些自己的状态。 对于复杂的控件(如ListView),状态相应复杂。 诀窍是尽可能简化控件状态的维护。 使用标准的ListView,这是不可能的 – 程序员必须完成工作。

这就是我编写ObjectListView (.NET WinForms ListView的开源包装器)的原因之一。 它允许您在更高级别使用ListView,其中控件状态的维护是不可见的。 ObjectListView直接对模型对象进行操作:

this.objectListView1.Objects = listOfModelObjects; this.objectListView1.SelectedObject = aPerson; 

一旦你可以在这个级别工作,数据绑定本身就没用了。 但是,如果您真的想使用它,可以使用ObjectListView项目中的数据可绑定DataListView。 它为您提供两全其美的体验。

使用ObjectListView,没有理由切换到不那么有趣的DataGridView。 ObjectListView为您提供了易于使用ListView的优秀UIfunction的DataGridView,还有更多:

替代文字http://sofzh.miximages.com/c%23/24vo5fb.png

标准DataBinding可能会让您的生活变得更加简单。 使用BindingList ,您可以轻松实现双向绑定。 双向绑定使用户可以通过编程方式(即模型)或用户进行更改来轻松更新UI。 该列表将保持同步。

但是,如果您负担得起,您可能想要将ListView换成(只读) DataGridView 。 使用DataBinding,它可以让您更轻松。

 dataGridView.DataSource = new BindingList(initialList); 

DataBinding是要走的路。 看看这篇文章,它非常有用:

.NET / C#Windows窗体中的数据绑定

数据绑定为开发人员提供了一种方法,可以在表单上的控件和应用程序中的数据(他们的数据模型)之间创建读/写链接。 传统上,在应用程序中使用数据绑定来利用存储在数据库中的数据。 Windows窗体数据绑定允许您从数据库以及其他结构(如数组和集合)中的数据访问数据。

alt text http://sofzh.miximages.com/c%23/dotnet_databinding_1.gif

我认为这个问题没有正确的方法。 通常,我强烈支持通过各种方式将数据源集合与UI分离。 存在几种数据绑定解决方案将两者结合在一起。

无论如何,让我对你暴露的观点做一些考虑。

关于冗余 :你确实会在用户的脑海中为基本相同的实体提供2个列表。 这些清单完全不相同。

首先,不会有重复的内存。 UI端的列表可能引用了数据源列表中的原始对象,但它只不过是一个引用。 总内存消耗不会比在UI中具有单个集合对象大得多

其次,两个列表(UI与数据源)可能没有相同数量的项目。 如果在界面中使用分页,则UI列表可能比数据源列表小得多。

复杂性 :使用良好的数据绑定解决方案可以大大降低UI与其数据源同步的复杂性。 在某些情况下,将两个列表分开将实际简化您的代码。

考虑具有单独的窗口/页面/用户控件/负责编辑(新旧)对象的任何可能性。 如果您的UI上只有一个对象集合,则此单独的组件必须引用保存该列表的同一UI控件。 这根本没有意义。

直接访问ListView (例如)的问题是您正在修改内存中数据的“主”副本。 这意味着如果您的用户决定取消他们的更改,您需要能够回滚他们可能做出的任何更改。

因此,您可以选择,增加两个数据结构副本的内存,或者增加必须回滚取消的复杂性。

查看有关数据绑定的这篇文章。 对于ListViews之类的东西,您可以绑定到实现其中一个列表接口的自定义类。 通过向事件的自定义类添加支持,只要对列表进行更改,您就可以自动更新GUI(反之亦然)。

我刚刚通过List(Of MyObject)和ListView来完成这个混乱。 ListView的缺点是它不像许多其他对象容器那样提供.DataSource属性,例如ComboBox,ListBox和DataGridView。 我选择放弃ListView而不是DataGridView,因为我的要求不需要ListView(我在DataGridView中添加了一个自定义的第一列来渲染图像,这就是我最初选择ListView的原因。但你是对,保持同步的额外代码太令人头疼了。关于你的问题,做我做的事。想一想你是否真的需要一个ListView,考虑你是否可以使用DataGridView来满足要求。