Skip to main content

Microsoft Silverlight

Answered Question Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded eventRSS Feed

(0)

ksmith3036
ksmith3036

Member

Member

0 points

3 Posts

Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

I have read other postings but can't find a way to hook up the OnExpanded event to TreeViewItem's created dynamically with the HierarchicalDataTemplate.

Here is my XAML code:

 

    <UserControl.Resources>
        <local:HierarchicalOrganization x:Key="Source" />
    </UserControl.Resources>


                            <controls:TreeView Grid.Row="1" x:Name="mainOrganization" 
                                ItemsSource="{Binding Source={StaticResource Source}}">
                                <controls:TreeView.ItemTemplate>
                                    <controls:HierarchicalDataTemplate x:Key="OrgTemplate" ItemsSource="{Binding Children}">
                                        <StackPanel Orientation="Horizontal">
                                            <TextBlock Text="{Binding SdbName}" Margin="5,0" ToolTipService.ToolTip="{Binding OrganizationId}" />
                                        </StackPanel>
                                    </controls:HierarchicalDataTemplate>
                                </controls:TreeView.ItemTemplate>
                            </controls:TreeView>

I can't find where to place the Expanded="eventhandler" in the XAML, and I also can't find anywhere in code to set up the event handler.

I have tried this:

            foreach (HierarchicalOrganizationItem item in mainOrganization.Items) {
                var tvi = mainOrganization.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                tvi.Expanded += new RoutedEventHandler(MainOrganizationItem_Expanded);
            }
but tvi is always null.

I also have tried this:

            foreach (TreeViewItem item in mainOrganization.Items) {
                tvi.Expanded += new RoutedEventHandler(MainOrganizationItem_Expanded);
            }
but, the Items collection is of course a collection of my own HierarchicalOrganizationItem items.

All the samples using event handlers on TreeViewItem's have used static nodes defined in the XAML, so is this an are which are forgotten by the programmers at MS?

All help would be deeply appreciated.

 

<Kåre>

Ted Glaza [MSFT]
Ted Glaz...

Member

Member

50 points

10 Posts

Answered Question

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

Hi Kare,

Unfortunately this scenario isn't directly supported on either Silverlight or WPF.  HierarchicalDataTemplate can work with lots of other controls besides TreeView and doesn't have knowledge of TreeView/TreeViewItem specific events.  It's also not very easy on Silverlight to get generated containers for data-bound items in an ItemsControls.  We can make it work though by getting a little creative.

I've rewritten the TreeViewDataBindingSample.xaml.cs file included with the Silverlight Toolkit's sample project (under Controls.Samples\TreeView\Basics) to change the TreeViewItem's Header when expanded.  The basic technique is to hook up to the generated containers when their parent TreeViewItem is expanded.  The comments should explain the rest.

Thanks,
Ted



using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Microsoft.Windows.Controls.Samples
{
    /// <summary>
    /// Sample demonstrating TreeView data-binding.
    /// </summary>
    [Sample("TreeView/(0)Basics/(4)Data Binding")]
    public partial class TreeViewDataBindingSample : UserControl
    {
        /// <summary>
        /// Initializes a new instance of the TreeViewDataBindingSample class.
        /// </summary>
        public TreeViewDataBindingSample()
        {
            InitializeComponent();

            // Fill the tree with data
            TreeOfLife.ItemsSource = Taxonomy.Life;

            // We can't start adding Expanded handlers until the TreeView adds
            // TreeViewItems to the visual tree.  We'll wait until it has been
            // loaded and then add Expanded handlers.
            TreeOfLife.Loaded += delegate { AddExpandedHandlers(TreeOfLife); };
        }

        /// <summary>
        /// Handle the Expanded event of a TreeViewItem.
        /// </summary>
        /// <param name="sender">The TreeViewItem.</param>
        /// <param name="e">Event arguments.</param>
        private void OnItemExpanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = sender as TreeViewItem;

            // Do whatever we wanted to do when expanded...
            // (for now we'll just change the text to Expanded!)
            item.HeaderTemplate = null;
            item.Header = "Expanded!";

            // Add Expanded handlers to all of the descendent TreeViewItems
            // (especially the child TreeViewItems of the item that was just
            // expanded)
            AddExpandedHandlers(item);
        }

        /// <summary>
        /// Add Expanded event handlers to descendent TreeViewItems that haven't
        /// been hooked up yet.
        /// </summary>
        /// <param name="parent">The parent TreeView or TreeViewItem.</param>
        private void AddExpandedHandlers(ItemsControl parent)
        {
            // Use Dispatcher.BeginInvoke to add our action to the end of the
            // dispatcher's queue.  This is necessary because we are called by
            // TreeViewItem.Expanded which added new items, but they won't show
            // up in the visual tree until a layout pass has happened.
            Dispatcher.BeginInvoke(() =>
            {
                // Walk all the descendent TreeViewItems of the current item
                // (we are walking all descendents to be safe because it's
                // possible for someone to add a TreeViewItem that's already
                // expanded whose children still need to be hooked up)
                foreach (TreeViewItem item in GetTreeViewItems(parent))
                {
                    // Ignore items that have already been hooked up.  We need
                    // to check since we're calling this method in
                    // OnItemExpanded (which can happen as many times as the
                    // user expands/collapses an item).  We could track this in
                    // other ways, but the Tag property is easy and not being
                    // used since we're data binding.
                    if (item.Tag != null)
                    {
                        continue;
                    }

                    // Hook up the Expanded event and tag the item
                    item.Expanded += OnItemExpanded;
                    item.Tag = true;
                }
            });
        }

        /// <summary>
        /// Get all of the descendent TreeViewItems of a parent TreeView or
        /// parent TreeViewItem that have been added to the visual tree.
        /// </summary>
        /// <param name="parent">The parent TreeView or TreeViewItem.</param>
        /// <returns>Sequence of descendent TreeViewItems.</returns>
        private static IEnumerable<TreeViewItem> GetTreeViewItems(ItemsControl parent)
        {
            // Get the ItemContainerGenerator from a TreeView or TreeViewItem
            ItemContainerGenerator generator = null;
            TreeViewItem parentItem = parent as TreeViewItem;
            if (parentItem != null)
            {
                generator = parentItem.ItemContainerGenerator;
            }
            else
            {
                TreeView parentView = parent as TreeView;
                if (parentView != null)
                {
                    generator = parentView.ItemContainerGenerator;
                }
                else
                {
                    throw new ArgumentException("parent is not a TreeView or TreeViewItem!", "parent");
                }
            }
   
            // Yield all of the descendent TreeViewItems
            for (int i = 0; i < parent.Items.Count; i++)
            {
                TreeViewItem item = generator.ContainerFromIndex(i) as TreeViewItem;
                if (item == null)
                {
                    continue;
                }

                yield return item;
                foreach (TreeViewItem descendent in GetTreeViewItems(item))
                {
                    yield return descendent;
                }
            }
        }
    }
}

This posting is provided "AS IS" with no warranties, and confers no rights.

Je8
Je8

Member

Member

54 points

50 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

 Hello Kare

If you add an item manually and then use the ContainerFromItem trick, does it work?

i.e.

HierarchicalOrganizationItem hoi = new HierarchicalOrganization();

mainOrganization.Items.Add(hoi);

TreeViewItem tvi = HierarchicalOrganizationItem.ItemContainerGenerator.ContainerFromItem(hoi) as TreeViewItem;

Is tvi null here too? In order to short-circuit similiar problems with XAML etc I found it much easier to populate the tree programmatically.

HTH

Jerry

Ted Glaza [MSFT]
Ted Glaz...

Member

Member

50 points

10 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

Hi Jerry,

The code snippet you posted will always return null for the TreeViewItemItemContainerGenerator doesn't have access to the TreeViewItem containers until they've been added to the visual tree and a layout pass has completed.  This doesn't happen synchronously.  You could probably make that code work (assuming you're not doing this in the .ctor and the TreeView itself is already in the visual tree) by wrapping it with Dispatcher.BeginInvoke.

Thanks,
Ted

This posting is provided "AS IS" with no warranties, and confers no rights.

ksmith3036
ksmith3036

Member

Member

0 points

3 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

Thank you! 

I guess this would do the trick, but it would not fully fix my problems. Explanation below.

I work in another time zone, so I continued my struggle after my original posting, and didn't get to read the replies until today.

I found that since I was using a TextBlock in the template, I could use the TextBlock's Loaded event to get an event when the node was first expanded. That problem solved, but to no avail.

You see, what I want to accomplish is to first fill the tree with only the top-level nodes, with dummy subnodes below those items that in reality contains sub-nodes. Then, when the user expand a node, I would call a webservice to collect the data for the subnodes and update the ObservableCollection accordingly.

What I found is that even if I remove the dummy sub node from the ObservableCollection, it does not get removed from the generated TreeView. Also, the subnodes added to the data was not added visually to the TreeView. But, if I added the same top-level node again to the top of the ObservableCollection, It would be added to the TreeView with it's newly added subnodes.

This was not very neat, since I ended up having the duplicated top-node, one with the dummy node below and one with the correct nodes below.

So I ended up to program the creation of the TreeViewItem's, because this solved all my problems regarding the Expanded event and removing and replacing sub nodes in the tree.

This solution has in fact fewer lines of code than the proposed fix.

What I doesn't like about manually adding the TreeViewItem nodes, is that I doesn't get the benefit of having a template.

Is there still a way to use a template for the nodes, even if adding them programatically?

 Kåre

Je8
Je8

Member

Member

54 points

50 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

Kare,

Yes you can have the template in the XAML but still add items in code. See this: http://silverlight.net/forums/p/44457/122535.aspx#122535

HTH

Jerry

Je8
Je8

Member

Member

54 points

50 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

That is certainly a valid reason for the snippet to fail Smile I can see some rationale for the way event notifications are shared between treeview and its items. Would it not be simpler if one could set a catch-all event handler in the treeview for events of interest? This makes all of the problems we've explored here go away. It is a much cleaner solution to on-demand loading, for example.

ksmith3036
ksmith3036

Member

Member

0 points

3 Posts

Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

I totally agree with you. having the same events on the TreeView would be a very good solution.

anyeone
anyeone

Participant

Participant

811 points

182 Posts

Re: Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

  Kåre,

I am trying to do the exact same thing you are, would you please expand on what you mean by "programming creation of the treeviewitems" and post more details on your solution?   I am having the same problem you describe - I'm unable to get the "expand" mini button on the top level nodes unless I put a dummy item below them which isn't an ideal solution. 

 My scenario only has two levels - but there could be very many items at both levels so I definitely need just-in-time loading of the child nodes.

 Thanks in advance,

Anye

--
Anye Mercy
AnyeDotNet.blogspot.com

Please "Mark as Answer" the posts that help you - this lets others know the problem has been solved and helps others having the same problem know which solution works. Thanks!

Grinch
Grinch

Member

Member

2 points

1 Posts

Re: Re: Silverlight Toolkit TreeView with HierarchicalDataTemplate and OnExpanded event

It seems I got the same problem. Here is my solution so far.

This is the Callback for the Webservice, which itself is called by the expanded event (t_Expanded).

First I clear all Subitems from the item, and then the received categories are added one by one.

 

void p_getCategoriesByIdCompleted(object sender, getCategoriesByIdCompletedEventArgs e)
{
CategoryElement[] cats = e.Result;

TreeViewItem t = (TreeViewItem)e.UserState;
t.Items.Clear();

foreach (CategoryElement cat in cats)
{
TreeViewItem tmp = new TreeViewItem();
tmp.Header = cat.name;
tmp.Name = "c_" + Convert.ToString(cat.id);
tmp.Items.Add("... I am a dummy ...");
tmp.Expanded += new RoutedEventHandler(t_Expanded);
t.Items.Add(tmp);
}
}

  

 

This works fine so far, but I would like to have an image in front of the text and have no idea how to do it. Got it working with HierarchicalDataTemplate, but then I lose the on Expanded Event.

Is there any way to put an Image and the Text into a TreeViewItem.Header without using HierarchicalDataTemplates? I also tried to override this with a Style, but then I loose the open/close brackets in front of the Item (seems to override some kind of default Style).

 tia
Michael

  • Unanswered Question
  • Answered Question
  • Announcement
Microsoft Communities