Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#树状视图设计-显示树状结构的最佳方式?_C#_Oop_Treeview - Fatal编程技术网

C#树状视图设计-显示树状结构的最佳方式?

C#树状视图设计-显示树状结构的最佳方式?,c#,oop,treeview,C#,Oop,Treeview,我正在尝试使用TreeView来显示对象的树结构。我有一个由四种类型的对象组成的树:公司(根节点)、城市、商店和员工 该界面旨在添加/删除城市/商店/员工,因此树视图必须更新以反映任何更改 我想知道如何让TreeView显示树结构,并在树结构发生变化时接收更新 我认为Company对象应该有事件,比如Company.CityAdded和Company.CityRemoved,那么我在树视图中放置的任何包装都会响应这些事件吗?树视图建立后,每个城市/商店/员工将有一个节点。然后,每个节点都可以响应

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

该界面旨在添加/删除城市/商店/员工,因此树视图必须更新以反映任何更改

我想知道如何让TreeView显示树结构,并在树结构发生变化时接收更新

我认为Company对象应该有事件,比如Company.CityAdded和Company.CityRemoved,那么我在树视图中放置的任何包装都会响应这些事件吗?树视图建立后,每个城市/商店/员工将有一个节点。然后,每个节点都可以响应它在树中表示的节点的事件


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

关于监听事件的概念,你说得对(这是一种标准的发布者/订阅者模式)

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

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

我对ListView也这么做,下面是一个典型的例子。添加树项时的主要区别在于,您可能需要添加父节点才能添加所请求的节点,这使得它有点递归。试试看

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

    ...
}
公共类MyForm:Form
{
...
无效数据\u已更改(对象发送方,DataChangedEventArgs e)
{
if(this.invokererequired)
{
this.BeginInvoke(新事件处理程序(数据已更改),发送方,e);
返回;
}
AddOrUpdateListItem(如数据项);
}
...
}

听起来你走对了路。我也做了类似的事情,我想分享一些建议:

  • 在TreeNode标记属性中存储对象引用

  • 为每个Treenode指定一个唯一的名称,以便轻松标识对象,例如:对象哈希代码、公司ID等

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

    祝你好运。

    而不是

    • 业务对象订阅UI事件
    • 命令更新用户界面
    • 当UI更新时,业务对象也会更新

    。。。您也可以用另一种方法(即命令更新业务对象树,从而相应地更新UI)。

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

    发布/订阅更新模式的关键部分是如何包装事件触发时的操作信息

    当表示“Store X”的对象更新为新名称,并触发事件以宣布此情况已发生时,哪个对象使用该事件

    类似地,添加城市Y时,应通知哪个对象创建

    一种常见的方法是使用某种大型uber manager类来处理整个过程——它订阅所有事件并执行所有操作

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

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

    当城市对象更新时,
    CityEditor
    通过更新
    TreeNode
    响应触发的事件。删除城市对象时,
    CityEditor
    确保节点已从
    Treeview
    中删除

    当一个新的商店对象被添加到
    城市
    中时,
    城市编辑器
    可以创建一个
    商店编辑器
    ,以协调该级别的更新。类似地,当
    员工
    被添加到
    商店
    时,
    员工编辑器
    的实例将处理
    树视图

    +1的更新,以提及BeginInvoke()。但是,您可能需要修改示例来说明这一点。