4

我正在尝试使用 TreeView 来显示对象的树结构。我有四种类型的对象树,公司(根节点)、城市、商店和员工。

该界面旨在添加/删除 Cities/Stores/Employees,因此 TreeView 必须更新以反映任何更改。

我想知道让 TreeView 显示树结构并在更改时接收更新的正确方法。

我在想 Company 对象应该有事件,例如 company.CityAdded 和 company.CityRemoved,然后我在 TreeView 周围放置的任何包装器都会响应这些事件?当 TreeView 建立后,每个城市/商店/员工都会有一个节点。然后每个节点可以响应它在树中表示的节点的事件。

这是正确的想法吗?或者有没有更好的方法?

4

5 回答 5

2

我只是想补充一点,如果 WPF 是一个选项,那么使用分层数据绑定和 observablecollections 将变得非常简单。它基本上为您处理所有事件,并让您只与业务对象进行交互。

于 2009-02-07T15:23:03.270 回答
1

你在监听事件的概念上是正确的(这是一个标准的发布者/订阅者模式)。

对于树视图的实际更新,我倾向于有两种方法:AddOrUpdateTreeItemRemoveTreeItem. add 或 update 方法执行它所说的,查找树项(基于路径)并更新它或添加它。当然,如果模型是在创建表单的线程之外的线程上更新的,则需要使用Control.BeginInvoke().

如果您在 form_load 或其他地方填充完整的树,这种方法可能会有点慢,因此您可能对初始填充有不同的方法,并使用我在此处描述的概念进行后续更新。

我对列表视图做同样的事情,这是一个典型的例子。添加树项的主要区别在于,您可能需要添加父节点才能添加所请求的节点,这使得它有点递归。试试看。

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);
    }

    ...
}
于 2009-02-06T19:02:57.897 回答
1

听起来你是在正确的道路上。我不得不做类似的事情,我想分享一些建议:

  1. 将对象引用存储在 TreeNode 标记属性中。

  2. 给每个 Treenode 一个唯一的名称,可以很容易地识别一个对象,例如:对象哈希码、公司 ID 等。

这样,您可以在对象状态更改时轻松查找和更新 TreeNode。当用户选择一个节点时,您可以从 Tag 属性中获取它所代表的对象。

祝你好运。

于 2009-02-06T19:09:27.930 回答
0

代替 ...

  • 业务对象订阅 UI 事件
  • 命令更新 UI
  • 更新 UI 时更新业务对象

...您也可以反过来做(即命令更新业务对象树,从而导致对 UI 的相应更新)。

于 2009-02-06T19:14:49.170 回答
0

更新的发布/订阅模式的部分关键是如何包装事件触发时要做什么的信息。

当代表“Store X”的对象更新了一个新名称,并触发了一个事件来宣布这已经发生时,哪个对象使用该事件?

同样,当添加城市 Y 时,应该通知哪个对象创建?

一种常见的方法是使用某种大型 uber-manager 类来处理整个过程——它订阅所有事件并完成所有事情。

另一种方法,我用过的效果很好,是创建更简单的包装器/协调器对象,只处理拼图的一部分。通常,我在这些类的名称后面加上“ Editor”。

因此,您可以拥有一个CityEditor类,其构造函数既接受一个City对象,又接受TreeNode代表该对象的 。将CityEditor订阅Cityobject 和 上的事件TreeNode,并负责用标题填充TreeNode并选择图标。

当 City 对象更新时,CityEditor通过更新TreeNode. 当 City 对象被移除时,CityEditor确保节点从Treeview.

当一个新的 Store 对象被添加到 中City时,CityEditor可以负责创建一个StoreEditor来协调该级别的更新。类似地,当将 anEmployee添加到 a时,处理 的更新的Store实例。EmployeeEditorTreeview

于 2009-02-08T09:20:07.730 回答