OpenLightGroup Blog

rss

Blogs from OpenLightGroup.net


Secrets of the Silverlight Toolkit TreeView Control: Programmatically selecting a Tree Node

*Note* the recent version of the Tree Control removes the SelectItem method. What you must do now is get an instance of the TreeViewItem and selt it's "IsSelected" property.

For example:

 

firstTreeViewItem = (TreeViewItem)this.treeView1.ItemContainerGenerator.ContainerFromItem(this.treeView1.Items[0]);

firstTreeViewItem.IsSelected = true;

While working on the Silverlight File Explorer for SilverlightBridge.net, I "Bing-Googled" a lot regarding the Silverlight Toolkit TreeView control. I was unable to find answers to some of the complex stuff I needed to do, so I decided to blog about some of the stuff I discovered that others may find useful.

Programmatically selecting a Tree Node

image

SilverlightBridge allows users to drag a file from the details pane (on the right hand side of the application) and drop it on a Tree Node in the TreeView (on the left side of the application). The TreeView control will normally, automatically, highlight the Tree Node when the mouse is hovered over it, but it will only set the Tree Node as "selected" if the Tree Node is clicked on (using MouseLeftButtonDown).

When performing a drag of a file, you already have the "Mouse Left Button Down", but in this case you want the Tree Node to be selected (and the details of that folder displayed in the folder details pane) by only hovering over it.

To do this, you will need to programmatically select the Tree Node.

Here is the method:

private void ProgramaticallySelectTreeNode(int intFolderID)
{
    // Get the Root Folders in the Tree
    // This will return the MyComputer folder and the Main server folder
    IEnumerable colFolders =
        (IEnumerable)FolderTree.Items.Cast();
 
    // Search the children of only the Main server folder
    // for a folder with the selected FolderID
    var DropFolder = (from Folders in colFolders.Where(x => x.FolderID > -1).
                          FirstOrDefault().GetAllFolders()
                      where Folders.FolderID == intFolderID
                      select Folders).FirstOrDefault();
 
    if (DropFolder == null)
    {
        // If it's null it must be the Main folder
        DropFolder = colFolders.Where(x => x.FolderID > -1).FirstOrDefault();
    }
 
    // Set the Folder as the selected folder in the tree
    FolderTree.SelectItem(DropFolder);
    FolderTree.UpdateLayout();
}

 

If you look at the above code it appears there is a lot to programmatically selecting a node, but there isn't. It just comes down to these two lines:

    FolderTree.SelectItem(DropFolder);
    FolderTree.UpdateLayout();

So why did this take so long to figure out? The reason I (and others who's posts I found while searching for answers) had a hard time, was that I was looking for some sort of method that basically allowed to me to "set this node as selected". The TreeView wants you to "set this item in the node as the selected item".

The difference is, to set this Tree Node as selected, you will locate the node in the TreeView. To set this item as selected, you will find the item in the collection bound to the TreeView and you will then pass that item to the SelectItem method of the TreeView.

Create a Collection and Bind it

image

With SilverlightBridge, the collection is a List<> of Folder. When the Silverlight control is initially displayed, the root folders are placed in a List<> of Folder, and bound to the TreeView.

In the XAML code for the TreeView, there is a template.

This is what it looks like in Expression Blend

image

And here is the markup:

<controls:TreeView 
SelectedItemChanged="FolderTree_SelectedItemChanged" 
Grid.Row="1" Grid.Column="0" Margin="5,5,3,5" 
x:Name="FolderTree" 
ItemContainerStyle="{StaticResource SelectedItemStyle}" 
d:LayoutOverrides="GridBox">
 <controls:TreeView.ItemTemplate>
  <common:HierarchicalDataTemplate 
    ItemsSource="{Binding SubFolders}" 
    ItemContainerStyle="{StaticResource ExpandedItemStyle}">
   <StackPanel x:Name="TreeFolder" Orientation="Horizontal" 
    Tag="{Binding FolderID}">
    <VisualStateManager.CustomVisualStateManager>
     <ic:ExtendedVisualStateManager/>
    VisualStateManager.CustomVisualStateManager>
    <VisualStateManager.VisualStateGroups>
     <VisualStateGroup x:Name="DragOver"/>
    VisualStateManager.VisualStateGroups>
    <Image HorizontalAlignment="Left" Margin="1,0,0,0" 
      Width="16" Source="images/folder.png" Stretch="Fill"/>
    <TextBlock Text="{Binding FolderName}" Margin="5,0" />
   StackPanel>
  common:HierarchicalDataTemplate>
 controls:TreeView.ItemTemplate>
 
controls:TreeView>

When we look at the run-time of SilverightBridge with Silverlight Spy 3 ...

image

We can see that two TreeViewItems are created. One for each folder. Inside each TreeViewItem is a number of objects including a StackPanel that we have named "TreeFolder" in the XAML markup. We have bound the FolderID to the Tag element of the StackPanel with this code:

<StackPanel x:Name="TreeFolder" Orientation="Horizontal" Tag="{Binding FolderID}">

This will allow us to determine the exact folder number we want to select, but, first we must determine what folder we are hovering over.

When we are dragging a element over the TreeView, we use this code to return the collection of StackPanels that may be underneath the mouse:

        // Build a list of elements at the current mouse position
        List hits = (List)
            System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates
            (tmpPoint, this);
 
        StackPanel tmpStackPanel = new StackPanel();
        var TreeStackPanels = from element in hits
                              where element.GetType() == tmpStackPanel.GetType()
                              select element;

Next, the following code is used to determine if any of the StackPanels is called "TreeFolder". If it is, the FolderID number is extracted from the Tag element of the StackPanel and the ProgramaticallySelectTreeNode method is called, passing the FolderID as the parameter.

        if (objStackPanel.Name == "TreeFolder")
        {
            // We found a StackPanel that is in a Tree
            // This means we have found a Tree Node
            intFolderID = Convert.ToInt32(objStackPanel.Tag.ToString());
 
            // Only show the details of the folder if we are not already showing it
            if (intFolderID != CurrentlySelectedFolderID)
            {
                // Programmatically Select Tree Node
                ProgramaticallySelectTreeNode(intFolderID);
                DisplayFilesInFolder();
            }
        }

In the method, notice that I am not searching for the node in the TreeViews elements, I am searching the collection(List<> of Folder) that is bound to the TreeView:

    IEnumerable colFolders =
        (IEnumerable)FolderTree.Items.Cast();
 
    // Search the children of only the Main server folder
    // for a folder with the selected FolderID
    var DropFolder = (from Folders in colFolders.Where(x => x.FolderID > -1)
                          .FirstOrDefault().GetAllFolders()
                      where Folders.FolderID == intFolderID
                      select Folders).FirstOrDefault();

Only after locating the object do I set that object as selected in the TreeView.

I hope this helps!





Comments are closed.
Showing 1 Comment
Avatar  web designing 8 years ago

Hy, very interesting & informative blog, gud job done. thanks