我正在尝试使用TreeView来显示对象的树结构。我有一个四种类型的对象树,公司(根节点),城市,商店和员工。
该界面旨在添加/删除Cities / Stores / Employees,因此TreeView必须更新以反映任何更改。
我想知道正确的方法是让TreeView显示树结构,并在更改时接收更新。
我认为Company对象应该有事件,例如company.CityAdded和company.CityRemoved,然后我在TreeView周围的任何包装器响应那些事件?构建TreeView后,每个城市/商店/员工将有一个节点。然后,每个节点都可以响应它在树中表示的节点的事件。
这是正确的想法吗?或者有更好的方法吗?
答案 0 :(得分:2)
我只想补充一点,如果WPF是一个选项,那么使用heirarchtical数据绑定和observablecollections会变得非常简单。它基本上可以为您处理所有事件,并让您只需与业务对象进行交互。
答案 1 :(得分:1)
关于听取事件的概念(这是一个标准的发布者/订阅者模式),你是正确的。
对于树视图的实际更新,我倾向于使用两种方法:AddOrUpdateTreeItem
和RemoveTreeItem
。添加或更新方法执行它所说的内容,查找树项(基于路径)并更新或添加它。当然,如果模型在创建表单之外的其他线程上更新,则需要使用Control.BeginInvoke()
封送调用。
如果你在form_load或其他东西填充一个完整的树,这种方法可能有点慢,所以你可能有一个不同的方法用于初始填充,并使用我在这里描述的概念用于后续更新。
我对listviews也这样做,这是一个典型的例子。添加树项时的主要区别在于您可能需要添加父节点以添加所请求的节点,这使其有点递归。试一试。
private void AddOrUpdateListItem(DomainModelObject item)
{
ListViewItem li = lvwListView.Items[GetKey(item)];
if (li == null)
{
li = new ListViewItem
{
Name = GetKey(item),
Tag = item
};
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.SubItems.Add(new ListViewItem.ListViewSubItem());
li.ImageIndex = 0;
lvwListView.Items.Add(li);
}
li.Text = [Itemtext];
li.SubItems[1].Text = [Itemtext];
li.SubItems[2].Text = [Itemtext];
li.SubItems[3].Text = [Itemtext];
}
以下是BeginInvoke()
如何实施的示例:
public class MyForm : Form
{
...
void data_Changed(object sender, DataChangedEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<DataChangedEventArgs>(data_Changed), sender, e);
return;
}
AddOrUpdateListItem(e.DataItem);
}
...
}
答案 2 :(得分:1)
听起来你正走在正确的道路上。我不得不做类似的事情,我想分享一些指示:
在TreeNode标记属性中存储对象引用。
为每个Treenode提供一个可以轻松识别对象的唯一名称,例如:对象哈希码,公司ID等。
这样,您可以在对象状态更改时轻松查找和更新TreeNode。当用户选择一个节点时,您可以从Tag属性中获取它所代表的对象。
祝你好运。答案 3 :(得分:0)
而不是......
...你也可以反过来做(即命令更新业务对象树,这会导致UI的相应更新)。
答案 4 :(得分:0)
更新的发布/订阅模式的关键部分是如何包装事件触发时要执行的操作的信息。
当用新名称更新表示“Store X”的对象,并触发事件以宣布发生了这种情况时,哪个对象会消耗该事件?
同样,当添加城市Y时,应该通知创建哪个对象?
一种常见的方法是拥有一个处理整个过程的大型超级经理类 - 它订阅所有事件并完成所有事情。
一种不同的方法,我已经习惯了很好的效果,就是创建更简单的包装器/协调器对象,只处理拼图的一部分。通常,我使用“Editor
”后缀这些类的名称。
因此,您可以拥有一个CityEditor
类,其构造函数同时包含City
个对象和代表该对象的TreeNode
。 CityEditor
会在City
对象和TreeNode
上订阅活动,并会使用标题填充TreeNode
并选择图标。
当City对象更新时,CityEditor
会通过更新TreeNode
来响应触发的事件。删除City对象后,CityEditor
会确保该节点已从Treeview
中删除。
当新的商店对象添加到City
时,CityEditor
可以负责创建StoreEditor
来协调该级别的更新。同样,当Employee
添加到Store
时,EmployeeEditor
的实例会处理Treeview
的更新。