1

I have a WPF TreeView control defined like this:

<TreeView x:Name="samplesTree" MouseDoubleClick="samplesTree_MouseDoubleClick"
          KeyUp="samplesTree_KeyUp"
          SelectedItemChanged="samplesTree_SelectedItemChanged"
          IsVisibleChanged="treeView_IsVisibleChanged">
</TreeView>

I track what nodes have been expanded or collapsed using this code:

private List<object> SamplesExpandedTags = new List<object>();

private void stag_Collapsed(object sender, RoutedEventArgs e)
{
    object tag = (sender as TreeViewItem).Tag;
    if (SamplesExpandedTags.Contains(tag))
        SamplesExpandedTags.Remove(tag);
}

private void stag_Expanded(object sender, RoutedEventArgs e)
{
    object tag = (sender as TreeViewItem).Tag;
    if (!SamplesExpandedTags.Contains(tag))
        SamplesExpandedTags.Add(tag);
}

...

// Items are created in code behind, not using binding to DataSource 
TreeViewItem item = new TreeViewItem();
item.Header = tv.NazevTypuVyrobku;
item.Tag = string.Format("TV{0}",tv.TypVyrobkuID);
item.Expanded += new RoutedEventHandler(stag_Expanded);
item.Collapsed += new RoutedEventHandler(stag_Collapsed);

By debugging I have found, that when the child node is being Collapsed then also Collapsed event of parent node fires, so stag_Collapsed is run for the whole chain of parent elements of the actually collapsed node.

I have added this code at the beginning of the stag_Collapsed method:

if ((sender as TreeViewItem).IsExpanded) return;

Now the code works as it should, but why the Collapsed event fires on all the parent TreeViewItems and not only on the TreeViewItem actually being collapsed? I would expect that IsExpanded property is allways false on sender.


The suggested solution to set:

e.Handled = true;

is not bullet proof, since this code:

subitem.Expanded += new RoutedEventHandler(stag_Expanded);
subitem.Expanded += new RoutedEventHandler(sampleOperation_Expanded);
subitem.Collapsed += new RoutedEventHandler(stag_Collapsed);

is working differently then this code:

subitem.Expanded += new RoutedEventHandler(sampleOperation_Expanded);
subitem.Expanded += new RoutedEventHandler(stag_Expanded);
subitem.Collapsed += new RoutedEventHandler(stag_Collapsed);

In the first case sampleOperation_Expanded does not get executed at all because of e.Handled = true; command. I thought that order of event handler execution should not be significant?

Community
  • 1
  • 1
Vojtěch Dohnal
  • 7,867
  • 3
  • 43
  • 105

1 Answers1

4

Perhaps try setting e.Handled to true in your stag_Collapsed() method. Per MSDN, "Marking the event handled will limit the visibility of the routed event to listeners along the event route".

private void stag_Collapsed(object sender, RoutedEventArgs e)
{
    object tag = (sender as TreeViewItem).Tag;
    if (SamplesExpandedTags.Contains(tag))
        SamplesExpandedTags.Remove(tag);
    e.Handled = true;
}
iCode
  • 1,254
  • 1
  • 13
  • 16
  • It's a [Routed Event](https://msdn.microsoft.com/en-us/library/ms742806(v=vs.100).aspx) (hence the `RoutedEventArgs` in the method signature). That means that whatever you need it to do at runtime, it does something else, in the wrong context, and then does it several times more in different wrong contexts. – 15ee8f99-57ff-4f92-890c-b56153 Mar 29 '16 at 13:19
  • Is it somewhere well documented @EdPlunkett ? – Vojtěch Dohnal Mar 29 '16 at 16:06
  • 1
    @VojtěchDohnal There's the [MSDN Routed Events Overview](https://msdn.microsoft.com/en-us/library/ms742806(v=vs.100).aspx) that I linked above. The important point for your particular question is in iCode's answer: The event will fire on the element, then its parent, then that parent, an so on to the root of the visual tree, UNTIL somebody sets `e.Handled = true`. Then it stops. So your handler needs to set `e.Handled = true`. – 15ee8f99-57ff-4f92-890c-b56153 Mar 29 '16 at 16:13
  • @EdPlunkett Does not work well when I have multiple handlers of one event. – Vojtěch Dohnal Mar 30 '16 at 07:20
  • @VojtěchDohnal No, I don't imagine it would. If you do, it would be necessary to find a way to get by with only one. Why do you need multiple handlers? If you must, one solution (ugly, but this all is) might be to define a new event on the code behind class, put the multiple handlers on that, and have a single handler in the real event which just fires that one and sets `e.Handled = true`. – 15ee8f99-57ff-4f92-890c-b56153 Mar 30 '16 at 09:00
  • 1
    @VojtěchDohnal You're borrowing trouble doing this all in code behind. Bind collections via HierarchicalDataTemplate, bind IsExpanded to some property on the collection items. Every time I see someone trying to do everything in code behind with WPF, it's a horror show. – 15ee8f99-57ff-4f92-890c-b56153 Mar 30 '16 at 09:03
  • Ok, this one I did few years ago when I was beginning with XAML. – Vojtěch Dohnal Mar 30 '16 at 10:12