如何为WPF Treeview挂钩BeforeNodeExpand和AfterNodeExpand?

时间:2021-11-19 23:53:10

Intention: To display the busy/Wait cursor for the duration that the children of the node are populated.

意图:在填充节点子节点的持续时间内显示busy / Wait光标。

Seems to be hidden in WPF. I remember having some kind of event in Winforms Tree Control where you could subscribe to - to achieve this.

似乎隐藏在WPF中。我记得在Winforms Tree Control中有一些你可以订阅的事件 - 实现这个目的。

Currently I have a TreeView which has a number of listviews tied to its SelectedItem. All controls are databound and use DataTemplates to display the items. The code is functional. But when I expand a tree-node with a lot of children, the UI goes sluggish.. it looks like the click didnt register.. and I being the impatient user go clickety-click on the node.

目前我有一个TreeView,它有许多与其SelectedItem相关的列表视图。所有控件都是数据绑定,并使用DataTemplates显示项目。代码功能齐全。但是当我扩展一个有很多孩子的树节点时,用户界面变得缓慢......看起来点击没有注册...而我是不耐烦的用户去点击节点点击。

So how do I do this ? I'd like to set the Cursor to Wait in BeforeExpand and reset in AfterExpand.

那我该怎么做?我想在AfterExpand中将Cursor设置为Wait并在AfterExpand中重置。

Code Scribble:

代码乱涂乱画:

<HierarchicalDataTemplate DataType="{x:Type local:LinqToSqlNodeClass}" ItemsSource="{Binding Path=Children}">
  // visual representation
</HierarchicalDataTemplate>
// more typed data templates 

<TreeView ItemsSource="{Binding Path=Nodes}" />

2 个解决方案

#1


5  

The ItemContainerGenerator will start generating after the Expanded event, so you can use that to set the Cursor, and set it back once ItemContainerGenerator.StatusChanged fires to indicate that your children have been populated.

ItemContainerGenerator将在Expanded事件之后开始生成,因此您可以使用它来设置Cursor,并在ItemContainerGenerator.StatusChanged触发时将其设置回来,以指示您的子项已被填充。

Since TreeViewItem.Expanded is a routed event, you can just subscribe at some parent level:

由于TreeViewItem.Expanded是一个路由事件,您可以在某个父级别订阅:

myTreeView.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(TreeViewItemExpanded));

Where TreeViewItemExpanded is defined elsewhere, something like this:

TreeViewItemExpanded在其他地方定义的地方,如下所示:

private void TreeViewItemExpanded(object sender, RoutedEventArgs e)
{
    // we will only go through with this if our children haven't been populated
    TreeViewItem sourceItem = e.OriginalSource as TreeViewItem;
    if ((sourceItem != null)
        && (sourceItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated))
    {
        // create a handler that will check our children and reset the cursor when the ItemContainerGenerator has finished
        EventHandler itemsGenerated = null;
        DateTime before = DateTime.Now;
        itemsGenerated = delegate(object o, EventArgs args)
        {
            // if the children are done being generated...
            if ((o as ItemContainerGenerator).Status == GeneratorStatus.ContainersGenerated)
            {
                (o as ItemContainerGenerator).StatusChanged -= itemsGenerated;  // we're done, so remove the handler
                sourceItem.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate    // asynchronous reset of cursor
                {
                    myWindow.Cursor = Cursors.Arrow;    // reset cursor
                    Debug.WriteLine("Expanded in " + (DateTime.Now - before));
                });
            }
        };
        sourceItem.ItemContainerGenerator.StatusChanged += itemsGenerated;  // add the handler
        myWindow.Cursor = Cursors.Wait;     // wait cursor
    }
    e.Handled = true;
}

#2


0  

Edit - The only way to accomplish this would be to make a class derived from TreeViewItem and override the OnExpand and setup a new Expanding Routed Event before you call the base Expand. Then you will use this new class as your container, instead of the default TreeViewItem.

编辑 - 实现此目的的唯一方法是创建一个派生自TreeViewItem的类,并在调用基础展开之前覆盖OnExpand并设置新的扩展路由事件。然后,您将使用此新类作为容器,而不是默认的TreeViewItem。

Then you could do something like

然后你可以做类似的事情

<TreeView cust:CustomTreeViewItem.Expanding="TreeView_Expanding">....</TreeView>

Previous Answer....

上一个答案....

The only thing I can see you using is the Collapsed and Expanded events on the TreeViewItem.

我唯一能看到你使用的是TreeViewItem上的Collapsed和Expanded事件。

You will have to change your template to use a TreeViewItem, with the events defined.

您必须更改模板以使用TreeViewItem,并定义事件。

Will this work for your purposes?

这是否适用于您的目的?

#1


5  

The ItemContainerGenerator will start generating after the Expanded event, so you can use that to set the Cursor, and set it back once ItemContainerGenerator.StatusChanged fires to indicate that your children have been populated.

ItemContainerGenerator将在Expanded事件之后开始生成,因此您可以使用它来设置Cursor,并在ItemContainerGenerator.StatusChanged触发时将其设置回来,以指示您的子项已被填充。

Since TreeViewItem.Expanded is a routed event, you can just subscribe at some parent level:

由于TreeViewItem.Expanded是一个路由事件,您可以在某个父级别订阅:

myTreeView.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(TreeViewItemExpanded));

Where TreeViewItemExpanded is defined elsewhere, something like this:

TreeViewItemExpanded在其他地方定义的地方,如下所示:

private void TreeViewItemExpanded(object sender, RoutedEventArgs e)
{
    // we will only go through with this if our children haven't been populated
    TreeViewItem sourceItem = e.OriginalSource as TreeViewItem;
    if ((sourceItem != null)
        && (sourceItem.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated))
    {
        // create a handler that will check our children and reset the cursor when the ItemContainerGenerator has finished
        EventHandler itemsGenerated = null;
        DateTime before = DateTime.Now;
        itemsGenerated = delegate(object o, EventArgs args)
        {
            // if the children are done being generated...
            if ((o as ItemContainerGenerator).Status == GeneratorStatus.ContainersGenerated)
            {
                (o as ItemContainerGenerator).StatusChanged -= itemsGenerated;  // we're done, so remove the handler
                sourceItem.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, (ThreadStart)delegate    // asynchronous reset of cursor
                {
                    myWindow.Cursor = Cursors.Arrow;    // reset cursor
                    Debug.WriteLine("Expanded in " + (DateTime.Now - before));
                });
            }
        };
        sourceItem.ItemContainerGenerator.StatusChanged += itemsGenerated;  // add the handler
        myWindow.Cursor = Cursors.Wait;     // wait cursor
    }
    e.Handled = true;
}

#2


0  

Edit - The only way to accomplish this would be to make a class derived from TreeViewItem and override the OnExpand and setup a new Expanding Routed Event before you call the base Expand. Then you will use this new class as your container, instead of the default TreeViewItem.

编辑 - 实现此目的的唯一方法是创建一个派生自TreeViewItem的类,并在调用基础展开之前覆盖OnExpand并设置新的扩展路由事件。然后,您将使用此新类作为容器,而不是默认的TreeViewItem。

Then you could do something like

然后你可以做类似的事情

<TreeView cust:CustomTreeViewItem.Expanding="TreeView_Expanding">....</TreeView>

Previous Answer....

上一个答案....

The only thing I can see you using is the Collapsed and Expanded events on the TreeViewItem.

我唯一能看到你使用的是TreeViewItem上的Collapsed和Expanded事件。

You will have to change your template to use a TreeViewItem, with the events defined.

您必须更改模板以使用TreeViewItem,并定义事件。

Will this work for your purposes?

这是否适用于您的目的?