0

我需要知道何时显示工具条菜单项(以便在 JIT 基础上创建内容)。

我有一个潜在的非常大的东西层次结构。它可以在树形视图中导航,也可以通过“面包屑”导航,例如“音乐 > 流行音乐 > 迈克尔杰克逊 > 坏”。面包屑由工具条实现,每个级别都显示有一个 LinkLabel。当点击一个链接时,例如“音乐”,一个上下文菜单与当前项目的所有同级一起动态构建。

我试图营造一种总是加载整个层次结构的错觉。当我获取数据时,我确保获得足够的信息来呈现一个节点并知道该节点是否有子节点。如果它确实有孩子,我创建一个具有特殊含义的虚拟节点“这个项目有卸载的孩子”并将其作为唯一的孩子。这会导致树视图将节点呈现为可展开的。然后,我使用树视图的 BeforeExpand 事件来获取下一个级别并及时更新节点集合,当它实际上将被显示时。

这对树很有效,但我需要对面包屑样式的上下文菜单做同样的事情。我已经为有孩子的项目创建了一个子菜单,将我的特殊“未加载的孩子”项目放在那里。问题是我找不到让我知道该子菜单何时显示的事件。这可以通过悬停、点击、键盘导航等方式实现;如果我还没有找到更合适的钩子,我真的不想破解它。

换句话说,我需要的是与“BeforeExpand”等效的东西,但对于 ToolStripMenuItem(根据 MS 自己的命名准则,事件 btw 应该称为“NodeExpanding”,即应该使用动名词形式(“ing”)而不是而不是“Before”前缀,过去时(“Expanded”)而不是“After”前缀)。

我希望很清楚我要做什么,并且有人知道如何解决这个问题!

4

1 回答 1

0

事实证明我可以使用 Paint 事件。我真的不认为它会起作用,但确实如此。

如果我们对 ToolStripMenuItem 使用术语“item”,对层次结构中的逻辑节点使用“node”,我们可以将 Paint 处理程序伪编码(实际上是我的 PoC 项目中的 C# 代码):

void menuItem_Paint(object sender, PaintEventArgs e)
{
    var item = (ToolStripMenuItem)sender;

    var node = item.Tag as Node;
    if (node == null || node.Parent == null || node.Parent.RealChildren)
        return;

    node = node.Parent;
    item = FindMenuItem(menu.Items, node);

    if (node != null)
        ReplaceDummyWithData(node, item);
}

void ReplaceDummyWithData(Node node, ToolStripMenuItem item)
{
    Dbug("Removing dummy: {0}", node.Label);

    // Modify logical tree..
    node.Remove(node.Children[0]);
    MakeNode(node, "Data #1");
    MakeNode(node, "Data #2");

    // Rebuild menu items..
    item.DropDownItems.Clear();
    AddMenuItems(item.DropDownItems, node.Children);
    node.RealChildren = true;
}

我真的不认为这会起作用,但它似乎工作正常。我刚刚硬编码了一个无限深的层次结构,其中每个项目都有两个标记为“Data #1”和“Data #2”的孩子,但修改它并不难,因此项目是根据真实数据创建的。关键是要找出一种及时修改菜单项树的方法,这似乎是一种可以接受的方法。

FindMenuItem 是对使用给定节点标记的项目的递归搜索,

ToolStripMenuItem FindMenuItem(ToolStripItemCollection collection, Node node)
{
    foreach (ToolStripMenuItem item in collection)
    {
        if (item.Tag == node)
            return item;

        var found = FindMenuItem(item.DropDownItems, node);
        if (found != null) 
            return found;
    }
    return null;
}

PoC 中的 MakeNode 总是会创建一个虚拟孩子,尽管在数据驱动的事情中,我将根据它是否真的有孩子来做:

Node MakeNode(Node parent, string label)
{
    var n = new Node(parent) { Label = label, RealChildren = false };
    new Node(n) { Label = "dummy" };
    return n;
}

Node 构造函数接受父节点;也许不是最易读的代码。它来自我的 PoC,代码的目的是找出我的问题的解决方案,而不是编写在其他方面都很棒的代码!

于 2013-01-09T15:54:12.140 回答