代码之家  ›  专栏  ›  技术社区  ›  Jonathan

iHierarchyData和iHierarchicaleNumerable在Winforms中

  •  6
  • Jonathan  · 技术社区  · 14 年前

    目前,我知道如何在treeview控件中懒洋洋地实现节点的加载过程,并阅读stackoverflow中的相关问题,但我也在阅读asp.net中的ihierarchydata和ihierarchicalenumerable接口(我不知道如何编写asp.net代码),它们允许将集合绑定到树视图,以便自动显示项目。

    我想知道在winforms和c中是否也可以这样做。我认为前面提到的接口在winforms中不可用。

    谢谢。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Aaronaught    14 年前

    windows窗体 TreeView 不知道如何绑定到 IHierarchyData 实例,这并不奇怪,因为 伊赫拉奇达 和相关的接口是供web控件使用的(特别是站点地图)。

    然而,构建自己的数据绑定类并不难。这似乎是一个有趣的问题,所以我把一个只是为了好玩。我带你去看看内部运作。

    首先,创建一个基本组件类。Visual Studio将以如下代码开始:

    public partial class TreeViewHierarchyBinding : Component
    {
        public TreeViewHierarchyBinding()
        {
            InitializeComponent();
        }
    
        public TreeViewHierarchyBinding(IContainer container)
        {
            container.Add(this);
            InitializeComponent();
        }
    }
    

    这个组件需要的一个明显的“状态”是 TreeNode 对其 伊赫拉奇达 . 现在我们可以把它扔进 树冠 Tag 属性,但我们的目标是使此组件尽可能无创,并跟踪其自身状态。因此,我们将使用词典。将此字段添加到类:

    private Dictionary<TreeNode, IHierarchyData> nodeDictionary = new 
        Dictionary<TreeNode, IHierarchyData>();
    

    现在,至少,这个组件需要知道如何填充特定的父组件 树冠 A的 树视图 类的相应绑定 伊赫拉奇达 ,下面我们来编写代码:

    private void PopulateChildNodes(TreeNodeCollection parentCollection,
        IHierarchicalEnumerable children)
    {
        parentCollection.Clear();
        foreach (object child in children)
        {
            IHierarchyData childData = children.GetHierarchyData(child);
            TreeNode childNode = new TreeNode(childData.ToString());
            if (childData.HasChildren)
            {
                childNode.Nodes.Add("Dummy");   // Make expandable
            }
            nodeDictionary.Add(childNode, childData);
            parentCollection.Add(childNode);
        }
    }
    
    private void UpdateRootNodes(TreeView tv, IHierarchyData hierarchyData)
    {
        if (tv == null)
        {
            return;
        }
        tv.Nodes.Clear();
        if (hierarchyData != null)
        {
            IHierarchicalEnumerable roots = hierarchyData.GetChildren();
            PopulateChildNodes(tv.Nodes, roots);
        }
    }
    

    这部分应该很简单。第一种方法只是填充 TreeNodeCollection (即 Nodes 财产 树冠 )从 伊赫拉奇达 实例,使用 IHierarchyEnumerable 接口。这种方法唯一真正有趣的地方是:

    1. 伊赫拉奇达 实例有子实例;这使“+”在树状视图中可见,否则我们将无法展开得更深;并且

    2. 使用 伊赫拉奇达 与之匹配的实例。

    第二种方法更简单,它执行初始的“绑定工作”,替换 我们的顶级 伊赫拉奇达 实例。

    我们的组件需要做的下一件事是从 树视图 执行延迟加载。下面是执行此操作的代码:

    private void RegisterEvents(TreeView tv)
    {
        tv.BeforeExpand += TreeViewBeforeExpand;
    }
    
    private void UnregisterEvents(TreeView tv)
    {
        tv.BeforeExpand -= TreeViewBeforeExpand;
    }
    
    private void TreeViewBeforeExpand(object sender, TreeViewCancelEventArgs e)
    {
        if (e.Node.Checked)
        {
            return;
        }
        IHierarchyData hierarchyData;
        if (nodeDictionary.TryGetValue(e.Node, out hierarchyData))
        {
            PopulateChildNodes(e.Node.Nodes, hierarchyData.GetChildren());
            e.Node.Checked = true;
        }
    }
    

    前两个方法应该是自解释的,第三个方法是实际的延迟加载代码。我们有点作弊,用 TreeNode.Checked 属性来描述是否已加载子节点,以便我们不进行任何不必要的重新加载。当我实现延迟加载的树时,我总是这样做,因为根据我的经验,我几乎从不使用 Treenode.已检查 财产。但是,如果确实需要将此属性用于其他用途,则可以使用其他属性(如 标签 ,创建另一个字典以保存展开的状态,或修改现有字典以保存复合类(包含 伊赫拉奇达 以及 Expanded 财产)。我暂时保持简单。

    如果之前在树中实现了延迟加载,那么剩下的应该已经有意义了,所以让我们跳过前面的步骤。现在唯一要做的就是实现一些设计器/用户属性,这些属性将真正连接树和数据:

    private IHierarchyData dataSource;
    private TreeView treeView;
    
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public IHierarchyData DataSource
    {
        get { return dataSource; }
        set
        {
            if (value != dataSource)
            {
                dataSource = value;
                nodeDictionary.Clear();
                UpdateRootNodes(treeView, value);
            }
        }
    }
    
    [Category("Behavior")]
    [DefaultValue(null)]
    [Description("Specifies the TreeView that the hierarchy should be bound to.")]
    public TreeView TreeView
    {
        get { return treeView; }
        set
        {
            if (value != treeView)
            {
                if (treeView != null)
                {
                    UnregisterEvents(treeView);
                }
                treeView = value;
                nodeDictionary.Clear();
                RegisterEvents(value);
                UpdateRootNodes(treeView, dataSource);
            }
        }
    }
    

    轻松点。我们有一个 DataSource 接受根的属性 伊赫拉奇达 和A 树视图 可以从设计器访问的属性。再说一次,简单的事情,当 数据来源 属性已更新,我们只需重置查找并重新填充根。当 树视图 属性更新后,我们需要做更多的工作,注册事件,确保从旧树视图中注销事件,并在数据源更改时执行所有相同的操作。

    这就是它的全部!打开windows窗体设计器,删除 树视图 ,然后放下 TreeViewHierarchyBinding 并设置其 树视图 属性设置为刚才删除的树视图。最后,在你的代码中的某个地方(即 Form_Load 事件),给它一个数据源:

    private void Form1_Load(object sender, EventArgs e)
    {
        DirectoryInfo dir = new DirectoryInfo("C:\\");
        treeViewHierarchyBinding1.DataSource = new FileSystemHierarchyData(dir);
    }
    

    (注意-这使用了示例 FileSystemHierarchyData 上面写着 MSDN page for IHierarchyData . 这个例子不太可靠,它没有检查 UnauthorizedAccessException 或者其他什么,但这足以证明这一点)。

    就这样。运行你的应用程序并观看它绑定。现在可以重用 树状缠绕 组件在任何地方-只要把它放到表单上,给它分配一个 树视图 ,然后给它一个 伊赫拉奇达 实例作为数据源。

    我已经把 complete code on PasteBin 如果你想要复制粘贴版本。

    玩得高兴!

        2
  •  1
  •   Scott Dorman    14 年前

    这些接口是可用的,但需要您添加对system.web.ui的引用。(它可能还要求您使用完整的.NET框架可再发行组件,而不是客户端配置文件,尽管我对此并不确定。)

    更大的问题是:winforms treeview控件是否自动理解如何使用这些接口?我相信这个问题的答案是“不”,但你需要对此进行测试/验证。

        3
  •  1
  •   Nathan Wheeler    14 年前

    有一篇有趣的文章 here 这向您展示了如何构建扩展方法来实现我认为您正在寻找的功能。在system.windows.forms.treeview中没有本机可用性,无法绑定到我可以找到的集合。

    您可以在项目中包含system.web.ui以使iHierarchyData和iHierarchicaleNumerable接口可用,但是如果没有扩展方法,TreeView将无法附加到这些接口。

    网站的示例源代码将允许您将任何idictionary集合绑定到treeview。