by JustinAngel via Justin myJustin = new Microsoft.Silverlight.Justin(); on 11/19/2008 1:53:36 AM
Hi folks, I’d like to go over the TreeView control which is part of the new Silverlight Toolkit (http://www.codeplex.com/Silverlight). We’ll talk about the TreeView control itself, how to style the TreeViewItem and what is HierarchalDataTemplate all about. Recommended reading before reading this article 1. Silverlight Toolkit: HeaderedContentControl & HeaderedItemsControl Setup 1. Create a new project.
Hi folks,
I’d like to go over the TreeView control which is part of the new Silverlight Toolkit (http://www.codeplex.com/Silverlight). We’ll talk about the TreeView control itself, how to style the TreeViewItem and what is HierarchalDataTemplate all about.
Recommended reading before reading this article
1. Silverlight Toolkit: HeaderedContentControl & HeaderedItemsControl
Setup
1. Create a new project.
2. Add a reference to the Silverlight Controls assembly (Microsoft.Windows.Controls.dll) which can be downloaded at http://codeplex.com/Silverlight.
3. Look under "Custom Controls" In the Blend Asset Library.
4. Add a TreeView to the Page.
And here's the XAML Blend generated for us:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="SilverlightControlsNovember2008.TreeViewPage"
d:DesignWidth="640" d:DesignHeight="480"
xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF">
<controls:TreeView Margin="79,98,0,0" VerticalAlignment="Top" Height="200" Width="120" HorizontalAlignment="Left"/>
</Grid>
</UserControl>
Manually Adding New Textual TreeViewItems to a TreeView
Let’s add some TreeViewItems to our TreeView that will reflect a family’s genealogy tree. like so:
We’ll right click on TreeView –> Add TreeViewItem.
And we can see that we indeed got a new TreeViewItem nested under or TreeView:
Next, we’ll go to the TreeViewItem’s properties and set Header to “Sally” and IsExpanded to true.
Here’s the XAML blend generated for us up until now:
<controls:TreeView Height="200" Width="120">
<controls:TreeViewItem IsExpanded="True" Header="Sally"/>
</controls:TreeView>
Now we’d like to add “John” as a child TreeViewItem of “Sally”.
We’ll Right click on “Sally” TreeViewItem –> Add TreeViewItem.
And we got another nested note inside the previous TreeViewItem:
We’ll edit it’s Header to “John” and we’ll keep IsExpanded to false, because it has no nested nodes.
We’ll keep it up until we get the following TreeView:
Let’s run our sample:
And we can start collapsing, expanding & Selecting TreeViewItems:
(In the above sample, Greg is collapsed, and John is selected)
And here’s the XAML Blend generated for us:
<controls:TreeViewItem IsExpanded="True" Header="Sally">
<controls:TreeViewItem Header="John"/>
<controls:TreeViewItem Header="Greg" IsExpanded="True">
<controls:TreeViewItem Header="Linda"/>
<controls:TreeViewItem Header="Darren"/>
</controls:TreeViewItem>
<controls:TreeViewItem Header="Allice" IsExpanded="True">
<controls:TreeViewItem Header="Neil"/>
Editing the TreeViewItem’s Header property in Visual Studio XAML Editor
Now it’s time to uncover the horrible truth – Sally and her children are all aliens. The green “blip blip” kind.
This is how our TreeView should look like when we’re done:
Blend currently does not support editing the Header property in a visual way. So we’ll open up Visual Studio’s XAML editor for that.
Right click on “TreeViewPage.xaml” (which is our page) in the Blend project tab –> Edit In Visual Studio.
And in a few second we’ll see this screen:
We’ll start off by changing the first TreeViewItem:
First, we’ll expand the Header property to allow XAML content:
<controls:TreeViewItem IsExpanded="True">
<controls:TreeViewItem.Header></controls:TreeViewItem.Header>
We’ll fill in the “Sally” content:
<controls:TreeViewItem.Header>
<TextBlock Text="Sally" />
</controls:TreeViewItem.Header>
And our TreeView still looks exactly the same:
Now we’d like to add the image of our Alien, but the Header can only contain a single element. So we’ll group the <Image> and the <TextBlock> in a horizontal <StackPanel>.
<StackPanel Orientation="Horizontal">
</StackPanel>
Now that we have a container in the Header, we’ll add the <Image> tag.
<Image Source="Alien1.png" />
In our Visual Studio preview windows we can see the following image:
Well, The text is too small for our picture. So let’s change the FontSize for the TreeView to 22.
<controls:TreeView FontSize="22" Height="200" Width="120">
We can see the Horizontal and Vertical Scrollbars appear because now our TreeViewItems are bigger than the TreeView. We’ll change the TreeView Width & Height accordingly.
<controls:TreeView FontSize="22" Height="350" Width="250">
Now our TreeView looks much better:
here’s our XAML up until now:
<controls:TreeView FontSize="22" Height="350" Width="250" Margin="84,101,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
We’ll repeat the process of editing an Image for the other 6 TreeViewItems. Than we’ll run our application.
here’s the XAML we wrote:
<controls:TreeViewItem>
<Image Source="Alien2.png" />
<TextBlock Text="John" />
<Image Source="Alien3.png" />
<TextBlock Text="Greg" />
<Image Source="Alien4.png" />
<TextBlock Text="Linda" />
<Image Source="Alien5.png" />
<TextBlock Text="Darren" />
<Image Source="Alien6.png" />
<TextBlock Text="Allice" />
<Image Source="Alien7.png" />
<TextBlock Text="Neil" />
We can definitely see that we’ve did some copy & paste here. Next we’ll see how we can use DataBinding to remove this repeatable code.
Specifying a DataTemplate as the TreeView’s ItemTemplate
Well, now we’d like to remove that duplicated code by using some DataBinding.
We’ve got this Alien class:
public class Alien
{
public Alien(string name, string pictureUrl)
Name = name;
Picture = new BitmapImage(new Uri(pictureUrl, UriKind.Relative));
}
public string Name { get; set; }
public BitmapImage Picture { get; set; }
Pretty simple, we’ve got a Name property, and a Picture property the we’re settings with way Silverlight uses for Image.Source databinding.
We’ll jump back to Blend.
And clear all the Items from the TreeViewItem by selecting the first TreeViewItem and deleting it.
Now that we have an empty TreeView, we’d like to set a x:Name so we can setup the TreeView‘s DataContext from our Code-behind.
We’ll set the TreeView ItemSource to run on a collection of aliens.
void TreeViewPage_Loaded(object sender, RoutedEventArgs e)
trvAliens.ItemsSource = new List<Alien>()
new Alien("Sally", "Alien1.png"),
new Alien("John", "Alien2.png"),
new Alien("Greg", "Alien3.png"),
new Alien("Linda", "Alien4.png"),
new Alien("Darren", "Alien5.png"),
new Alien("Alice", "Alien6.png"),
new Alien("Neil", "Alien7.png"),
};
Now, we can finally get down to business – changing the TreeView’s ItemTemplate.
Right Click on TreeView –> Edit Other Templates –> Edit Generated Items (ItemTemplate) –> Create Empty.
We’ll call our new Template AlienTemplate.
We can see that we have an empty DataTemplate:
First, we’ll Change the Grid to a Horizontal StackPanel.
Right Click Grid –> Change Layout Type –> StackPanel.
Next we’ll add an Image control.
And we’ll want to DataBind it’s Source Property to Alien.Picture property.
Click Advanced Property options next to Source.
Select “Custom Expression”.
And put in “{Binding Picture}”.
Next we’ll add a TextBlock and Bind it’s Text property to “{Binding Name}”.
--> –>
-->
Let’s run this sample:
Here’s the XAML Blend Generated for our TreeView:
<controls:TreeView FontSize="22" Height="350" Width="250" x:Name="trvAliens" ItemTemplate="{StaticResource AlienTemplate}"/>
Here’s the XAML Blend generated for our ItemTemplate:
<UserControl.Resources>
<DataTemplate x:Key="AlienTemplate">
<Image Source="{Binding Path=Picture}"/>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</UserControl.Resources>
Here’s our Code-behind:
And our current Alien Class:
Specifying a HierarchalDataTemplate as a TreeView’s ItemTemplate in Visual Studio XAML Editor
In case you haven’t noticed, our current TreeView is pretty flat. It only has 1 level. And we’d like to get a similar TreeView to the one we previously had, with Nested TreeViewItems.
We’ll start by changing our CLR Alien Type:
public Alien(string name, string pictureUrl, params Alien[] children)
Children = children;
public Alien[] Children { get; set; }
All we did is add a property that is collection of Aliens that are the Children of that Alien.
Now that we’ve got a Hierarchical Alien class, we’ll change our code behind to use it:
new Alien("Sally", "Alien1.png",
new Alien("Greg", "Alien3.png",
new Alien("Darren", "Alien5.png")
),
new Alien("Alice", "Alien6.png",
new Alien("Neil", "Alien7.png")
)
The next part of changing our ItemTemplate is not supported by Blend. so we’re back at editing XAML in Visual Studio XAML Editor.
This is our current DataTemplate:
Let’s change it to a HierarchicalDataTemplateL:
<controls:HierarchicalDataTemplate x:Key="AlienTemplate">
</controls:HierarchicalDataTemplate>
And we’ll need to point to our new Hierarchical CLR property – Children.
We’ll do that by Setting HierarchicalDataTemplate.ItemsSource to “{Binding Children}”.
<controls:HierarchicalDataTemplate x:Key="AlienTemplate" ItemsSource="{Binding Children}">
Let’s run the sample:
Here’s our XAML code for the TreeView (hasn’t changed during this sample):
Here’s our updated ItemTemplate:
Here’s our updated Alien class:
And our updated Code-behind:
Syncing a TreeView SelectedItem with an External Control
We’d like to have a big bold TextBlock on top of our page which says “Selected Alien: <Name of Alien>”. like so:
So, first, we’ll add a big TextBlock saying “Selected Alien:” in big bold letter.
And another empty TextBlock called “txtSelectedAlienName” with the same font properties.
Next we’d like to register to the SelectedItemChanged event.
public TreeViewPage()
// Required to initialize variables
InitializeComponent();
this.Loaded += new RoutedEventHandler(TreeViewPage_Loaded);
trvAliens.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(trvAliens_SelectedItemChanged);
void trvAliens_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
Here are the EventArgs for this event:
We’ll convert e.NewValue to Alien CLR type, and use it’s Name to fill our TextBlock.
void trvAliens_SelectedItemChanged(object sender,
RoutedPropertyChangedEventArgs<object> e)
txtSelectedAlienName.Text = ((Alien) e.NewValue).Name;
And if we select “John”:
Here’s our updated XAML:
<controls:TreeView FontSize="22" Height="350" Width="250" Margin="84,101,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="trvAliens" ItemTemplate="{StaticResource AlienTemplate}"/>
<TextBlock Height="46" Margin="22,38,0,0" VerticalAlignment="Top" FontWeight="Bold" TextWrapping="Wrap" Width="257" HorizontalAlignment="Left"><Run FontSize="30" Text="Selected Alien:"/></TextBlock>
<TextBlock Height="46" Margin="279,38,204,0" VerticalAlignment="Top" TextWrapping="Wrap" FontSize="30" FontWeight="Bold" FontFamily="Portable User Interface" TextDecorations="Underline" x:Name="txtSelectedAlienName"/>
And our updated code-behind:
txtSelectedAlienName.Text = ((Alien)e.NewValue).Name;
Using the TreeView SelectedValue and SelectedValueMember
Let’s refactor our previous code sample so instead of using e.NewValue it would use the TreeView’s SelectedValue.
SelectedValue reflects a specific property on the current SelectedItem based on SelectedValuePath. complicated? not really.
this handler:
is equivalent to this code:
txtSelectedAlienName.Text = ((Alien)trvAliens.SelectedValue).Name;
in stead of asking the what’s the new selected Item, we can just ask the TreeView what’s the currently selected item. By default, SelectedValue is the CLR type behind the currently selected TreeViewItem.
Instead of converting our SelectedValue back to Alien and getting Name, we can set SelectedValuePath to “Name”.
And now with this change, SelectedValue isn’t Alien anymore, but the value of Alien.Name.
so, we can re-write our handler to:
txtSelectedAlienName.Text = trvAliens.SelectedValue.ToString();
And we get the exact same UI:
Here’s the XAML Blend generated for us:
<controls:TreeView FontSize="22" Height="350" Width="250" x:Name="trvAliens" ItemTemplate="{StaticResource AlienTemplate}" SelectedValuePath="Name"/>
And here’s our updated Event Handler:
Changing Visual States – Changing the Collapsed and Extended Visual States
Looking at this last Print screen a bit more closely we can see It uses to types of Icons:
- Expanded Icon
- Collapsed Icon
We’d like to change those to:
In order to do that, we’ll have to edit the Template of the TreeViewItem generated by the TreeView.
To do that, we’ll change the TreeView ItemContainerStyle that gets applied on to each generated TreeViewItem.
We’ll select the TreeView, go to “Object –> Edit Other Styles –> Edit ItemContainerStyle –> Edit Copy”.
And we’ll call the new Style “AlienItemStyle”.
Next, we’ll need to drill into editing the template for the TreeViewItems.
We’ll do that through “Edit Template –> Edit Controls Parts (Template) –> Edit Template”.
Here’s what we see:
There are a few VisualStateManager states here. And we’ve got a template that has a few visual elements in it.
One of those is the Expander button.
In order to change the TreeViewItem Icons we’ll need to edit the Template for the ExpanderButotn.
Right Click on the ExpanderButton –> Edit Template –> Edit Template.
And here’s what we see:
Apparently, the TreeViewItem ExpanderButton has two visuals: “CheckedVisual” and “UncheckedVisual” .
We’ll need to replace those with our new Visuals.
First, I’ll draw a whole ellipse.
And on top of it I’ll draw a rectangle.
I’ll select the Ellipse with the Rectangle. Right Click –> Combine –> Subtract.
And we’ll get this path:
I’ll repeat the process and we’ll also get this path:
Now, In XAML we’ll cut & paste the names of “CheckedVisual” and “UncheckedVisual” to these new elements.
becomes
Next, we’ll delete the old CheckVisual and UncheckedVisual and place our new ones into the correct position.
Now, one last thing we have to change before this runs, is making sure the “UncheckedVisual” is hidden during the “Checked” state.
We’ll go the the “Checked” state.
Select the “UncheckedVisual” and set it’s opacity to 0.
Now if we run our sample:
Here’s the XAML Blend generated for our ListBox:
<controls:TreeView FontSize="22" :Name="trvAliens" ItemTemplate="{StaticResource AlienTemplate}" SelectedValuePath="Name" ItemContainerStyle="{StaticResource AlienItemStyle}" />
Here’s the the Style that was generated for us:
(it’s quite verbose, so the important parts are highlighted)
<Style x:Key="AlienItemStyle" TargetType="controls:TreeViewItem">
<Setter Property="IsTabStop" Value="True"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Cursor" Value="Arrow"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:TreeViewItem">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Header" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="#FF999999"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="SelectionStates">
<vsm:VisualState x:Name="Unselected"/>
<vsm:VisualState x:Name="Selected">
<DoubleAnimation Duration="0" Storyboard.TargetName="select" Storyboard.TargetProperty="Opacity" To=".75"/>
<vsm:VisualState x:Name="SelectedInactive">
<DoubleAnimation Duration="0" Storyboard.TargetName="inactive" Storyboard.TargetProperty="Opacity" To=".2"/>
<vsm:VisualStateGroup x:Name="HasItemsStates">
<vsm:VisualState x:Name="HasItems"/>
<vsm:VisualState x:Name="NoItems">
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpanderButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
<vsm:VisualStateGroup x:Name="ExpansionStates">
<vsm:VisualState x:Name="Collapsed"/>
<vsm:VisualState x:Name="Expanded">
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ItemsHost" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</vsm:VisualStateManager.VisualStateGroups>
<ToggleButton HorizontalAlignment="Stretch" x:Name="ExpanderButton" VerticalAlignment="Stretch">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Grid x:Name="Root" Background="Transparent">
<vsm:VisualState x:Name="MouseOver">
<ColorAnimation Duration="0" Storyboard.TargetName="UncheckedBrush" Storyboard.TargetProperty="Color" To="#FF1BBBFA"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To=".7"/>
<vsm:VisualStateGroup x:Name="CheckStates">
<vsm:VisualState x:Name="Unchecked">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="CheckedVisual" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<vsm:VisualState x:Name="Checked">
<DoubleAnimation Duration="0" Storyboard.TargetName="UncheckedVisual" Storyboard.TargetProperty="Opacity" To="0"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="CheckedVisual" Storyboard.TargetProperty="Opacity" To="1"/>
<Grid HorizontalAlignment="Right" Margin="2 2 5 2">
<Path x:Name="CheckedVisual" Margin="-10.409,15.842,-5.159,6.095" Width="15.568" Fill="#FFFFF6F6" Stretch="Fill" Stroke="#FF000000" StrokeLineJoin="Miter" Data="M0.5,0.5 L15.067377,0.5 L14.694302,2.2247162 C13.935259,3.899652 12.497602,5.241466 10.703027,5.9499054 C9.8057394,6.3041248 8.8192225,6.5 7.7836885,6.5 C5.7126212,6.5 3.8376207,5.7164989 2.4803879,4.449748 C1.8017712,3.8163719 1.2525964,3.0621843 0.87307578,2.2247162 z"/>
<Path x:Name="UncheckedVisual" Margin="-2.875,9.312,-5.125,6.688" Fill="#FFFFF6F6" Stretch="Fill" StrokeLineJoin="Miter" Data="M0.5,0.5 C4.3659902,0.50000048 7.5,3.634006 7.5,7.5 C7.5,11.365994 4.3659887,14.5 0.5,14.5 z">
<Path.Stroke>
<SolidColorBrush Color="#FF989898" x:Name="UncheckedBrush"/>
</Path.Stroke>
</Path>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<Rectangle x:Name="select" Grid.Column="1" IsHitTestVisible="False" Opacity="0" Fill="#FFBADDE9" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="2" RadiusY="2"/>
<Rectangle x:Name="inactive" Grid.Column="1" IsHitTestVisible="False" Opacity="0" Fill="#FF999999" Stroke="#FF333333" StrokeThickness="1" RadiusX="2" RadiusY="2"/>
<Button Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" x:Name="Header" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Grid.Column="1" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" ClickMode="Hover">
<Button.Template>
<ControlTemplate TargetType="Button">
<vsm:VisualState x:Name="Pressed">
<DoubleAnimation Duration="0" Storyboard.TargetName="hover" Storyboard.TargetProperty="Opacity" To=".5"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="content" Storyboard.TargetProperty="Opacity" To=".55"/>
<Rectangle x:Name="hover" IsHitTestVisible="False" Opacity="0" Fill="#FFBADDE9" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="2" RadiusY="2"/>
<ContentPresenter Cursor="{TemplateBinding Cursor}" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}" x:Name="content" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Button.Template>
</Button>
<ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1" Visibility="Collapsed"/>
</Setter.Value>
</Setter>
</Style>
I think it’s an important exercise for us to see how much Expression Blend helps us in not dealing with complex XAML syntaxes like this one.
Changing Visual States – Changing the Disabled Visual State
We’re going to a CheckBox to the form that says '”Allow Alien Selection?” and if it’s not Checked than we’ll disable our TreeView.
And here’s the XAML blend generated for this Checkbox:
<CheckBox Content="Enable Alien Selection?" x:Name="cbxEnableTreeView" IsChecked="True"/>
In our code-behind, we’ll change the TreeView IsEnabled (which drills down to TreeView IsEnabled).
…
cbxEnableTreeView.Checked += new RoutedEventHandler(cbxEnableTreeView_Checked);
cbxEnableTreeView.Unchecked += new RoutedEventHandler(cbxEnableTreeView_Checked);
void cbxEnableTreeView_Checked(object sender, RoutedEventArgs e)
trvAliens.IsEnabled = cbxEnableTreeView.IsChecked.Value;
Well, I personally don’t like this IsEnabled=False display for our TreeViewItems. Our aliens are very bright and still take a lot of attention.
Let’s bring down the TreeViewItem opacity to 50% while it’s Disabled.
Select the Disabled VSM state.
And change the Grid’s opacity to 70%.
Side-by-side with the original disabled state:
Here’s the XAML Blend generated for our Disabled VSM state:
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="grid" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.7"/>
Changing Visual States – Changing the HasItems and No Items Visual States
We’d like to change the Size of TreeViewItems based on whether or not they have children.
We’ll Select the NoItems state.
Select the Header and change it’s scaling to 0.7.
We’ll select the HasItems VSM State and set the Header scaling to 1.1.
Let’s run on our sample:
Here’s a side-by-side with the TreeView from the previous sample:
Here’s the XAML Blend generated for our VSM states:
<vsm:VisualState x:Name="HasItems">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="Header" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1.1"/>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="Header" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
Changing Visual States – Changing the Selected and SelectedInactive Visual State
Our aliens don’t like that the selected Alien has blue background. Being aliens, they asked us to change the background color to green and we’ll oblige.
Here’s the new Select background side by side with the old select background.
To achieve this effect, we’ll start editing the “Selected” VSM State.
We’ll select the “select” element.
And change it’s Fill from Blue
to green
Here’s he XAML blend generated for our VSM state:
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="select" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:00" Value="#FFBFE9BA"/>
</ColorAnimationUsingKeyFrames>
Virtualizing the TreeView with TreeViewItem’s Expanded & Collapsed Events
Let’s start from adding a new TreeView.
We want this TreeView to display all combinations of letters in the world.
Problem is, it’s quite a few Items and TreeView has no built in virtualization. So let’s start from scratch here.
We’ll start from displaying a TreeView of all single charcters.
public TreeViewCharsPage()
this.Loaded +=new RoutedEventHandler(TreeViewCharsPage_Loaded);
private void TreeViewCharsPage_Loaded(object sender, RoutedEventArgs e)
for (int i = 97; i < 122; i++)
TreeViewItem newItem = new TreeViewItem() {Header = (char) i};
trvChars.Items.Add(newItem);
OK, that’s a good start. Now, we’d like to “pretend” that all combinations of “a” + all letters between A to Z are under “a”. Same goes to the rest of the characters.
So, at this point, we can start nesting loops. So the first loop would create 26 characters between A-Z. And the next one would create another 26*26 TreeViewItems, and the one in that would create 26*26*26 ad so on.
We don’t want to waste that much memory because that would burden our application and make it sluggish.
So we’ll use a different strategy – we’ll pretend all TreeViewItems are already there and when the user Expends a TreeViewItem we’ll create them.
First move here is create a mock-TreeViewItem to show the TreeViewItem Icon.
newItem.Items.Add(new TreeViewItem());
But when we expand our TreeViewItem, it’s still empty:
So let’s use the Expanded event to add the new TreeViewItems.
newItem.Expanded += new RoutedEventHandler(newItem_Expanded);
void newItem_Expanded(object sender, RoutedEventArgs e)
The first thing we need to do here is convert sender to a TreeViewItem (which is always the sender of this event)
TreeViewItem expanded = (TreeViewItem) sender;
Next, we’ll loop through the letters in the ABC and add them to the Expanded TreeViewItem:
TreeViewItem newItem = new TreeViewItem() { Header = expanded.Header.ToString() + (char)i };
And we’ll make sure to add this new TreeViewItem to the expanded TreeViewItem.
expanded.Items.Add(newItem);
And now let’s add the final piece of puzzle which is a demi-object and link the new TreeViewItem’s Expanded event to the same handler.
Let’s run our sample and navigate all the way down to “Jhonny”.
If we were to create all this TreeViewItems up front we’ll need to create than 25^1+25^2+25^3+25^4+25^5+25^5 items = 254,313,150 TreeViewItems.
So through using the Expanded event (and the Collapsed event) we can delay creating TreeViewItems until their necessary.
<controls:TreeView x:Name="trvChars" />
Here’s the code we wrote:
(I’m tempted to refactor this code, but let’s leave it like this for example sake)
--- Justin-Josef Angel
Microsoft Silverlight Program Manager
Original Post: Silverlight Toolkit: TreeView, TreeViewItem & HierarchalDataTemplate
The content of the postings is owned by the respective author. Silverlight Feeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on Silverlight Feeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.