Page view counter

Silverlight TreeView Advanced Scenarios (TreeViewExtended)

Hi Folks,

One of the most important tenants of Silverlight Controls development we picked up in Microsoft is WPF Compatibility.

That means, that if we end up building FooControl in Silverlight and FooControl exists in WPF we’ll end up with the same API and behaviour.

Why is that good for you? It allows you to reuse to code from Silverlight in WPF. If we spin up new API or have differentiating behaviours you really won’t be able to to reuse your XAML and Code in WPF.

Why could this introduce problems? Well, The WPF API and Behaviours aren’t a divine decree. Some scenarios might not be covered by the WPF control.

We’ve hit this issue pretty hard when it comes to TreeView. The WPF TreeView doesn’t fully meet all user requirements, but we still want to be WPF Subset compat.
Hard choice, eh?

So, allow me to introduce a TreeViewExtended class that solves some of this issues.

 

Download code @ http://silverlight.net/blogs/justinangel/BlogStorage/TreeViewExtended.zip 
(To get TreeViewExtended and extension methods to work, include TreeViewExtended.cs in your project and go crazy.)

 

Prologue: Setting up a Hello World TreeView

Prerequisites for this article:
1. Silverlight Toolkit: TreeView, TreeViewItem & HierarchalDataTemplate 

If you don’t know how TreeView works, please read the afore mentioned article.

 

Here’s our basic TreeView:

<UserControl x:Class="TreeViewExtended.MainPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"

   xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"

   Width="400" Height="300">

    <UserControl.Resources>

        <common:HierarchicalDataTemplate x:Key="myHierarchicalTemplate" ItemsSource="{Binding Items}" >

            <TextBlock Text="{Binding myString}" />

        </common:HierarchicalDataTemplate>

    </UserControl.Resources>

    <StackPanel x:Name="LayoutRoot" Background="White" VerticalAlignment="Stretch">

        <controls:TreeView VerticalAlignment="Stretch" 

                          x:Name="trv"

                          ItemTemplate="{StaticResource myHierarchicalTemplate}" />

    </StackPanel>

</UserControl>

Nothing too fancy, Just a TreeView with a HierarchichalDataTemplate.

Here’s some sample data we’ll use in this solution:

public partial class MainPage : UserControl

{

    ObservableCollection<myItem> itemsSource =

      new ObservableCollection<myItem>()

      {

          new myItem("Hello",

              new myItem("World"),

              new myItem("Foo"),

              new myItem("Bar")),

          new myItem("Moo",

              new myItem("Boo",

                new myItem("Goo"))),

      };

 

    public MainPage()

    {

        InitializeComponent();

 

        trv.ItemsSource = itemsSource;

    }

}

And here’s the C# type we’ll use:

public class myItem

{

    public myItem(string myString, params myItem[] myItems)

    {

        ObservableCollection<myItem> itemsObservableCollection = new ObservableCollection<myItem>();

        foreach (var item in myItems)

            itemsObservableCollection.Add(item);

        Items = itemsObservableCollection;

    }

 

    public string myString { get; set; }

    public ObservableCollection<myItem> Items { get; set; }

}

 

When we run this simple TreeView sample we’ll see the following TreeView:

image

And Fully expanded:

image

 

 

Chapter #1: Finding a TreeViewItem based on a Business Class instance

Let’s create a Button that once Clicked it changes the “Moo” TreeViewItem to FontSize=20.

To do that, we’ll use the TreeView.ItemContainerGenerator shown here:

image

The ContainerFromItem allows us to find a TreeViewItem based on a specific Business class.

image

image

So, Let’s add the button:

<Button VerticalAlignment="Bottom" Content="Change Moo TreeViewItem FontSize" Click="Button_Click" />

And Let’s write the code needed to get the TreeViewItem:

ObservableCollection<myItem> itemsSource =

  new ObservableCollection<myItem>()

  {

      new myItem("Hello",

          new myItem("World"),

          new myItem("Foo"),

          new myItem("Bar")),

      new myItem("Moo",

          new myItem("Boo",

            new myItem("Goo"))),

  };

 

private void Button_Click(object sender, RoutedEventArgs e)

{

    myItem mooElement = itemsSource[1];

    TreeViewItem mootreeViewItem =

        (TreeViewItem)trv.ItemContainerGenerator.ContainerFromItem(mooElement);

 

    mootreeViewItem.FontSize = 20;

}

Let’s run this sample:

image

And click the sample:

image 

Now let’s change the sample to change the TreeViewItem for ‘Boo’

ObservableCollection<myItem> itemsSource =

  new ObservableCollection<myItem>()

  {

      new myItem("Hello",

          new myItem("World"),

          new myItem("Foo"),

          new myItem("Bar")),

      new myItem("Moo",

          new myItem("Boo",

            new myItem("Goo"))),

  };

 

private void Button_Click(object sender, RoutedEventArgs e)

{

    myItem booElement = itemsSource[1].Items[0];

    TreeViewItem bootreeViewItem =

        (TreeViewItem)trv.ItemContainerGenerator.ContainerFromItem(booElement);

 

    bootreeViewItem.FontSize = 20;

}

What do you think that’ll happen when we run this sample?

image

 

But why is BooTreeViewItem empty? Well, it’s because an ItemContainerGenerator only knows the Items of the same level.

image

So, The TreeViewItem itself has an ItemContainerGenerator we’ll need to access.

private void Button_Click(object sender, RoutedEventArgs e)

{

    myItem mooElement = itemsSource[1];

    TreeViewItem mootreeViewItem =

        (TreeViewItem)trv.ItemContainerGenerator.ContainerFromItem(mooElement);

 

    myItem booElement = mooElement.Items[0];

    TreeViewItem bootreeViewItem =

        (TreeViewItem)mootreeViewItem.ItemContainerGenerator.ContainerFromItem(booElement);

 

    bootreeViewItem.FontSize = 20;

}

And when we run this sample:

image 

So, first we had to get the ‘Moo’ TreeViewItem and than the ‘Boo’ TreeViewItem..

You can see how this gets a bit tricky if you’re just looking for any element in the TreeView.

 

Introducing TreeViewWorkarounds.
Based on the principles shown in this article up until now, you can easily create a method to iterate over all TreeViewItem and search for items.

public static class TreeViewWorkarounds

{

    public static TreeViewItem ContainerFromItem(this TreeView treeView, object item)

    {

        TreeViewItem containerThatMightContainItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(item);

        if (containerThatMightContainItem != null)

            return containerThatMightContainItem;

        else

            return ContainerFromItem(treeView.ItemContainerGenerator, treeView.Items, item);

    }

 

    private static TreeViewItem ContainerFromItem(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, object item)

    {

        foreach (object curChildItem in itemCollection)

        {

            TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

            if (parentContainer == null)

                return null;

            TreeViewItem containerThatMightContainItem = (TreeViewItem)parentContainer.ItemContainerGenerator.ContainerFromItem(item);

            if (containerThatMightContainItem != null)

                return containerThatMightContainItem;

            TreeViewItem recursionResult = ContainerFromItem(parentContainer.ItemContainerGenerator, parentContainer.Items, item);

            if (recursionResult != null)

                return recursionResult;

        }

        return null;

    }

 

    public static object ItemFromContainer(this TreeView treeView, TreeViewItem container)

    {

        TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)treeView.ItemContainerGenerator.ItemFromContainer(container);

        if (itemThatMightBelongToContainer != null)

            return itemThatMightBelongToContainer;

        else

            return ItemFromContainer(treeView.ItemContainerGenerator, treeView.Items, container);

    }

 

    private static object ItemFromContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, TreeViewItem container)

    {

        foreach (object curChildItem in itemCollection)

        {

            TreeViewItem parentContainer = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

            if (parentContainer == null)

                return null;

            TreeViewItem itemThatMightBelongToContainer = (TreeViewItem)parentContainer.ItemContainerGenerator.ItemFromContainer(container);

            if (itemThatMightBelongToContainer != null)

                return itemThatMightBelongToContainer;

            TreeViewItem recursionResult = ItemFromContainer(parentContainer.ItemContainerGenerator, parentContainer.Items, container) as TreeViewItem;

            if (recursionResult != null)

                return recursionResult;

        }

        return null;

    }

}

Using these extension methods isn’t hard at all:

private void Button_Click(object sender, RoutedEventArgs e)

{

    //myItem mooElement = itemsSource[1];

    //TreeViewItem mootreeViewItem =

    //    (TreeViewItem)trv.ItemContainerGenerator.ContainerFromItem(mooElement);

 

    //myItem booElement = mooElement.Items[0];

    //TreeViewItem bootreeViewItem =

    //    (TreeViewItem)mootreeViewItem.ItemContainerGenerator.ContainerFromItem(booElement);

 

    //bootreeViewItem.FontSize = 20;

 

    trv.ContainerFromItem(itemsSource[1].Items[0]).FontSize = 20;

}

Let’s run this sample:

image

Much easier to use, eh?

Don’t worry about the code for the extension methods, you can freely use it.

 

 

Chapter #2: Searching for any TreeViewItems based on predicates

Based on the code from the previous extension methods, we can build a TreeView search based on predicates.

Essentially, we’d like to enable using search lambda predicates:

trv.FindContainer(t => ((myItem) t.Header).myString == "Boo").FontSize = 20;

 

We’ll add the FindContainer extension method to iterate over all TreeView and check the predicate.

public static class TreeViewWorkarounds

{

 

    public static TreeViewItem FindContainer(this TreeView treeView, Predicate<TreeViewItem> condition)

    {

        return FindContainer(treeView.ItemContainerGenerator, treeView.Items, condition);

    }

 

    private static TreeViewItem FindContainer(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection, Predicate<TreeViewItem> condition)

    {

        foreach (object curChildItem in itemCollection)

        {

            TreeViewItem containerThatMightMeetTheCondition= (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

 

            if (containerThatMightMeetTheCondition == null)

                return null;

 

            if (condition(containerThatMightMeetTheCondition))

                return containerThatMightMeetTheCondition;

 

            TreeViewItem recursionResult = FindContainer(containerThatMightMeetTheCondition.ItemContainerGenerator, containerThatMightMeetTheCondition.Items, condition);

            if (recursionResult != null)

                return recursionResult;

        }

        return null;

    }

 

Let’s run this code sample:

image

 

We can also use this method to look for parent TreeViewItems.

trv.FindContainer(t => ((myItem)t.Header).Items.Any(i => i.myString == "World")).FontSize = 20;

In this code, we’re looking for any TreeViewItem that has a Child with the word “World” in it.

image

 

Really, once you get the power of lambda search predicates, you can do a lot with the FindContainer extension method.

 

 

Chapter #3: Exposing a TreeView.Containers collection

Sometimes, you just want to have a collection of all the TreeViewItems in the TreeView.
Maybe you want to check something, or manually search those items.

We’ll inherit from the normal TreeView to create TreeViewExtended:

public class TreeViewExtended : TreeView

{

    public TreeViewExtended()

    {

        this.DefaultStyleKey = typeof(TreeView);

    }

}

Next, we’ll change our sample to work with this new TreeView:

<UserControl x:Class="TreeViewExtended.MainPage"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"

   xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls"

   xmlns:TreeViewExtended="clr-namespace:System.Windows.Controls"

   Width="400" Height="300">

    <UserControl.Resources>

        <common:HierarchicalDataTemplate x:Key="myHierarchicalTemplate" ItemsSource="{Binding Items}" >

            <TextBlock Text="{Binding myString}" />

        </common:HierarchicalDataTemplate>

    </UserControl.Resources>

    <StackPanel x:Name="LayoutRoot" Background="White" VerticalAlignment="Stretch">

        <!--<controls:TreeView VerticalAlignment="Stretch"  x:Name="trv" ItemTemplate="{StaticResource myHierarchicalTemplate}"  />-->

        <TreeViewExtended:TreeViewExtended VerticalAlignment="Stretch" x:Name="trv" ItemTemplate="{StaticResource myHierarchicalTemplate}" />

        <Button VerticalAlignment="Bottom" Content="Do something" Click="Button_Click" />

    </StackPanel>

</UserControl>

 

And obviously, everything works as we expected:

image

 

Now, let’s implement our collection.

 

public class TreeViewExtended : TreeView

{

    public TreeViewExtended()

    {

        this.DefaultStyleKey = typeof(TreeView);

    }

 

    public IEnumerable<TreeViewItem> Containers

    {

        get

        {

            return GetTreeViewItems(this.ItemContainerGenerator, this.Items);

        }

    }

 

    private static IEnumerable<TreeViewItem> GetTreeViewItems(ItemContainerGenerator parentItemContainerGenerator, ItemCollection itemCollection)

    {

        foreach (object curChildItem in itemCollection)

        {

            TreeViewItem container = (TreeViewItem)parentItemContainerGenerator.ContainerFromItem(curChildItem);

 

            if (container != null)

                yield return container;

 

            foreach (var treeViewItem in GetTreeViewItems(container.ItemContainerGenerator, container.Items))

                yield return treeViewItem;

        }

    }

}

 

And let’s print out the TreeViewItems when clicking the button:

    foreach (TreeViewItem treeViewItem in trv.Containers)

    {

        Debug.WriteLine("TreeViewItem " + ((myItem)treeViewItem.Header).myString);

    }

 

Let’s run this sample app and click the button:

image

And in Visual Studio “Output” window we can see:

image

 

Chapter #4: Aggregating the TreeViewItem Expanded & Collapsed on TreeView

We’d like to get an event to fire on our TreeView whenever a TreeViewItem.Expanded/Collapsed event fires.

Think with me here, if we want to do that – the TreeViewItem needs to know who TreeView is so it can tell the TreeView “Hey, the TreeViewItem event fired!”.

For that, we’ll need to create our own TreeViewItemExtended.

Now, look closely, this is how we override the 2 methods on an ItemsControl/TreeView to do that.

public class TreeViewExtended : TreeView

{

    protected override bool IsItemItsOwnContainerOverride(object item)

    {

        return item is TreeViewExtended;

    }

 

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new TreeViewItemExtended();

    }

 

    …

}

 

public class TreeViewItemExtended : TreeViewItem

{

    protected override bool IsItemItsOwnContainerOverride(object item)

    {

        return item is TreeViewExtended;

    }

 

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new TreeViewItemExtended();

    }

}

 

We’ll override these 2 methods so our TreeView and TreeViewItems will always generated our TreeVieWItemExtended.

Next, we’ll create a public property on TreeViewItem that points to the TreeView it belongs to.

public class TreeViewItemExtended : TreeViewItem

{

    protected override bool IsItemItsOwnContainerOverride(object item)

    {

        return item is TreeViewExtended;

    }

 

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new TreeViewItemExtended();

    }

 

    public TreeViewExtended ParentTreeView { set; get; }

}

 

We’ll populate that property on the prepare method.

public class TreeViewExtended : TreeView

{

    protected override bool IsItemItsOwnContainerOverride(object item)

    {

        return item is TreeViewExtended;

    }

 

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new TreeViewItemExtended();

    }

 

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

    {

        TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended) element;

        treeViewItemExtended.ParentTreeView = this;

 

        base.PrepareContainerForItemOverride(element, item);

    }

 

And in the TreeViewItem for each child TreeViewItem:

public class TreeViewItemExtended : TreeViewItem

{

    protected override bool IsItemItsOwnContainerOverride(object item)

    {

        return item is TreeViewExtended;

    }

 

    protected override DependencyObject GetContainerForItemOverride()

    {

        return new TreeViewItemExtended();

    }

 

    public TreeViewExtended ParentTreeView { internal set; get; }

 

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

    {

        TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended)element;

        treeViewItemExtended.ParentTreeView = this.ParentTreeView;

 

        base.PrepareContainerForItemOverride(element, item);

    }

}

 

Next, we’ll expose a TreeViewExtended event for Expanded/Collapsed.

public class TreeViewExtended : TreeView

{

   …

 

    public event RoutedEventHandler ContainerExpanded;

    public event RoutedEventHandler ContainerCollapsed;

}

 

And we’ll add appropriate methods for TreeViewItemExtended to invoke these events.

    public event RoutedEventHandler ContainerExpanded;

    internal void InvokeContainerExpanded(object sender, RoutedEventArgs e)

    {

        RoutedEventHandler expanded = ContainerExpanded;

        if (expanded != null) expanded(sender, e);

    }

 

    public event RoutedEventHandler ContainerCollapsed;

    internal void InvokeContainerCollapsed(object sender, RoutedEventArgs e)

    {

        RoutedEventHandler collapsed = ContainerCollapsed;

        if (collapsed != null) collapsed(sender, e);

    }

 

Finally, we’ll have the TreeViewItem invoke these event invoke methods.

public class TreeViewItemExtended : TreeViewItem

{

    public TreeViewItemExtended()

    {

        this.Expanded += new RoutedEventHandler(TreeViewItemExtended_Expanded);

        this.Collapsed += new RoutedEventHandler(TreeViewItemExtended_Collapsed);

    }

 

    void TreeViewItemExtended_Collapsed(object sender, RoutedEventArgs e)

    {

        ParentTreeView.InvokeContainerCollapsed(sender, e);

    }

 

    void TreeViewItemExtended_Expanded(object sender, RoutedEventArgs e)

    {

        ParentTreeView.InvokeContainerExpanded(sender, e);

    }

 

Let’s use these events:

public MainPage()

{

    InitializeComponent();

 

    trv.ItemsSource = itemsSource;

 

    trv.ContainerExpanded += (s, e) => PrintTreeViewMessage(s, "Expanded");

    trv.ContainerCollapsed += (s, e) => PrintTreeViewMessage(s, "Collapsed");

 

}

 

private void PrintTreeViewMessage(object sender, string description)

{

    TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended) sender;

    myItem item = (myItem) treeViewItemExtended.Header;

 

    textBlock.Text = string.Format("{0} TreeView was {1}", item.myString, description);

}

And when we run this sample, we can see that:

image

image

 

image

 

 

Chapter #5: Exposing the TreeViewItem.MouseLwftButtonDown event

For reasons of WPF subset Compatibility the Silverlight TreeViewItem does not raise the MouseLeftButtonDown event when clicked.
Normally, you should use the “Selected” event for that.

But, we can now expose a new TreeViewExtended.ContainerMouseLeftButtonDown event that does just that.

public class TreeViewExtended : TreeView

{

    public event MouseButtonEventHandler ContainerMouseButtonEventHandler;

    internal void InvokeContainerMouseButtonEventHandler(object sender, MouseButtonEventArgs e)

    {

        MouseButtonEventHandler handler = ContainerMouseButtonEventHandler;

        if (handler != null) handler(sender, e);

    }

public class TreeViewItemExtended : TreeViewItem

{

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)

    {

        base.OnMouseLeftButtonDown(e);

        ParentTreeView.InvokeContainerMouseButtonEventHandler(this, e);

    }

 

And now, we’ll sign up for that event.

public MainPage()

{

    InitializeComponent();

 

    trv.ItemsSource = itemsSource;

 

    trv.ContainerMouseButtonEventHandler += (s, e) => PrintTreeViewMessage(s, "MouseLeftButtonDown");

}

 

And when we run this sample:

image

image

 

 

Chapter #6: Exposing a PreparingContainer event

So, one of the biggest thing you should know about TreeView – it’s virtualized (in a way).
It won’t create TreeViewItem until they’re really needed.

Which makes a lot of the work we’ve done up until now for not. Let’s have a look at this code snippet:

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            trv.ContainerFromItem(itemsSource[1].Items[0].Items[0]).FontSize = 20;

        }

Basically, we’re looking for the TreeViewItem with the word ‘Goo’.

What do you think happens if we click the button when TreeView is fully collapsed?

image

We’ll get a “OMG, This TreeView doesn’t exist” exception:

image

But, if we do the same thing when the TreeView is expanded:

image

It will work:

image

 

Why? Because the ‘Goo’ TreeViewItem doesn’t exist until it’s needed.

And the TreeViewItem isn’t available until that Visual Tree has been built, which happens at somewhat random points.

So, let’s start by exposing an event that’ll happen every time a TreeViewItem is created.

public class TreeViewExtended : TreeView

{

    public event EventHandler<ContainerPreparedEventArgs> ContainerPrepared;

    internal void InvokeContainerPrepared(TreeViewItemExtended sender, object item)

    {

        EventHandler<ContainerPreparedEventArgs> prepared = ContainerPrepared;

        if (prepared != null) prepared(sender, new ContainerPreparedEventArgs(sender, item));

    }

 

 

public class ContainerPreparedEventArgs : EventArgs

{

    public ContainerPreparedEventArgs(TreeViewItemExtended container, object item)

    {

        Container = container;

        Item = item;

    }

 

    public TreeViewItemExtended Container { get; set; }

    public object Item { get; set; }

}

 

Next, whenever a TreeViewItem is prepared we’ll invoke this event.

public class TreeViewExtended : TreeView

{

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

    {

        TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended) element;

        treeViewItemExtended.ParentTreeView = this;

 

        base.PrepareContainerForItemOverride(element, item);

 

        InvokeContainerPrepared(treeViewItemExtended, item);

    }

 

    public event EventHandler<ContainerPreparedEventArgs> ContainerPrepared;

    internal void InvokeContainerPrepared(TreeViewItemExtended sender, object item)

    {

        EventHandler<ContainerPreparedEventArgs> prepared = ContainerPrepared;

        if (prepared != null) prepared(sender, new ContainerPreparedEventArgs(sender, item));

    }

 

}

 

 

public class TreeViewItemExtended : TreeViewItem

{

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

    {

        TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended)element;

        treeViewItemExtended.ParentTreeView = this.ParentTreeView;

 

        base.PrepareContainerForItemOverride(element, item);

 

        ParentTreeView.InvokeContainerPrepared(treeViewItemExtended, item);

    }

 

 

}

 

Now, Let’s print out when each TreeViewItem has been created:

public MainPage()

{

    InitializeComponent();

 

    trv.ItemsSource = itemsSource;

 

    trv.ContainerPrepared += (s, e) => AddTreeViewMessage(s, "Created");

}

 

 

Let’s start our application:

image

We can clearly that only TreeViewItems have been created up until now.

Let’s expand the ‘Hello’ TreeViewItem:

image

And once that TreeViewItem has expanded the TreeViewItems were created.

We’ll Expand the rest of the Tree:

image image

 

So the PreparingContainer event is pretty useful considering it lets us know when TreeViewItems are ready.

 

 

Chapter #7: Implementing Delayed Expanded TreeViewItems

This feature isn’t 100% useful before we get to the next Chapter.
What I’d like for us to do (and take me on faith here) is implement a method that :
1. Will expand TreeViewItem corresponding to Items
2. Will be able to work in a delayed manner (even if they are created after the method was called)

Expanding TreeViewItems creates more TreeViewItems, so this might be very interesting.

 

Here’s our method:

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

    public void ExpandDelayItems(params object[] ItemsCorrespondingToTreeViewItemsToExpand)

    {

        itemsToDelayExpand.AddRange(ItemsCorrespondingToTreeViewItemsToExpand);

 

        foreach (object itemtoTryAndExpand in ItemsCorrespondingToTreeViewItemsToExpand)

        {

            TreeViewItem treeViewItem = this.ContainerFromItem(itemtoTryAndExpand);

            if (treeViewItem != null)

            {

                treeViewItem.IsExpanded = true;

                itemsToDelayExpand.Remove(itemtoTryAndExpand);

            }

        }

    }

We’ll take a collection of Items and add those to a bigger list of all Items we’re waiting for.
Than we’ll go over the TreeView looking for these items and if they have TreeViewItems – we’ll expand those.

If they don’t have TreeViewItems, we’ll listen to the prepared event and wait for them to be created.

    internal void InvokeContainerPrepared(TreeViewItemExtended sender, object item)

    {

        EventHandler<ContainerPreparedEventArgs> prepared = ContainerPrepared;

        if (prepared != null) prepared(sender, new ContainerPreparedEventArgs(sender, item));

 

        if (itemsToDelayExpand.Contains(item))

        {

            sender.IsExpanded = true;

            itemsToDelayExpand.Remove(item);

        }

    }

 

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

    public void ExpandDelayItems(params object[] ItemsCorrespondingToTreeViewItemsToExpand)

    {

        itemsToDelayExpand.AddRange(ItemsCorrespondingToTreeViewItemsToExpand);

 

        foreach (object itemtoTryAndExpand in ItemsCorrespondingToTreeViewItemsToExpand)

        {

            TreeViewItem treeViewItem = this.ContainerFromItem(itemtoTryAndExpand);

            if (treeViewItem != null)

            {

                treeViewItem.IsExpanded = true;

                itemsToDelayExpand.Remove(itemtoTryAndExpand);

            }

        }

    }

 

Let’s test our method:

trv.ExpandDelayItems(itemsSource[1], itemsSource[1].Items[0]);

 

And if we run our application:

image

Let’s click the button:

image

You can see that ‘Moo’ TreeViewItem was expanded, and than the ‘Boo’ TreeViewItem was expanded.

 

 

Chapter #8: Set TreeView.SelectedItem with TreeView.SetSelectItem method

By now, you can see how using the ExpandDelayItems with the ContainerPrepared event let’s us set a selected item.

    private object SelectedItemDelayed = null;

    public void SetSelectedItem(object SelectedItem, params object[] SelectedItemParents)

    {

        ContainerPrepared += new EventHandler<ContainerPreparedEventArgs>(ContainerPrepared_LookForSelectedItem);

        SelectedItemDelayed = SelectedItem;

        ExpandDelayItems(SelectedItemParents);

    }

 

    private void ContainerPrepared_LookForSelectedItem(object sender, ContainerPreparedEventArgs e)

    {

        if (e.Item == SelectedItemDelayed)

        {

            e.Container.IsSelected = true;

            SelectedItemDelayed = null;

            ContainerPrepared -= new EventHandler<ContainerPreparedEventArgs>(ContainerPrepared_LookForSelectedItem);

        }

    }

Basically, all we did here is expand all Parent TreeViewItems and sit comfortably waiting for our TreeViewItem to create itself.

 

Let’s test our SetSelectedItem method.

trv.SetSelectedItem(itemsSource[1].Items[0].Items[0],

                    itemsSource[1].Items[0],

                    itemsSource[1]);

And when we’ll run the app we can see:

image

image

 

Now, the TreeView could start looking around for the Parent Items itself, but consider a scenario with millions of nested elements.
So if you call SetSelectedItem with the correct parents, it’ll end up setting your SelectedItem.

 


Chapter #9: Adding a Parent TreeViewItem property

One last tiny feature we’ll add is a TreeViewItem.ParentTreeViewItem property.

 

We’ll start by adding the property.

public class TreeViewItemExtended : TreeViewItem

{

    public TreeViewItemExtended ParentTreeViewItem { internal set; get; }

 

And during the Prepare Container for it’s nested elements we’ll set their Parent property to this TreeViewItem.

public class TreeViewItemExtended : TreeViewItem

{

 

    public TreeViewExtended ParentTreeView { internal set; get; }

    public TreeViewItemExtended ParentTreeViewItem { internal set; get; }

 

    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)

    {

        TreeViewItemExtended treeViewItemExtended = (TreeViewItemExtended)element;

        treeViewItemExtended.ParentTreeView = this.ParentTreeView;

        treeViewItemExtended.ParentTreeViewItem = this;

 

        base.PrepareContainerForItemOverride(element, item);

 

        ParentTreeView.InvokeContainerPrepared(treeViewItemExtended, item);

    }

 

Let’s use this to change the parent background of any TreeViewItem with children:

            trv.ContainerExpanded += (s, args) =>

                {

                    TreeViewItemExtended parent = ((TreeViewItemExtended) s).ParentTreeViewItem;

                    if (parent != null)

                    {

                        parent.Background = new SolidColorBrush(Colors.Blue);

                    }

                };

Let’s run this application:

image

And once we expand ‘Hello’ TreeViewItem:

image

And ‘Moo’ TreeViewItem:

image

 

 

Epilogue

The TreeView control has a lot of extensibility points to overcome some innate WPF compatible features.

If you’d like to see TreeViewItemExtended ship in the Silverlight Toolkit, please feel free to vote on the issue here:
http://silverlight.codeplex.com/WorkItem/View.aspx?WorkItemId=3039

 

Sincerely,

-- Justin Angel

Microsoft Silverlight Program Manager

Silverlight Unit Testing, RhinoMocks, Unity and Resharper.

Hi Folks,

As most of you who’re familiar with my work know – I’m a huge supporter of the open source community.  
I also hold firm to a belief that the Microsoft .Net ecosystem is truly great due to community and vendors innovation.

During this article I’d like for us to go over 4 open source initiatives: The Microsoft’s Silverlight Unit Testing framework, RhinoMocks , Microsoft Unity and the Silverlight unit testing plug-in for Resharper.

 

Here are some links to get you started:

1. Microsoft Silverlight Unit testing framework
2. RhinoMocks
3. Microsoft Unity
4. Silverlight Unit Test Runner for Resharper 


You can grab the application we’ve developed from: SL2_UnitTestingRhinoMocksUnityResharper.zip

 

The obligatory Hello World sample

In order to introduce these 4 unit testing oriented projects we’ll start of from the canonical hello world sample.
After that we’ll move to something a bit more Interesting.

We’ll start a new Silverlight project which we’ll for our sample:
image

We’ll use a Textblock with “Hello World!” as text:

<TextBlock Text="Hello World!" x:Name="txt" x:FieldModifier="public" />

When we run our application we’ll see:

image

 

 

 

Microsoft Silverlight Unit Testing framework – “Hello World”

Since Silverlight can’t load Desktop .Net CLR assemblies and the Desktop CLR can’t load Silverlight controls – We can’t use existing unit testing frameworks.
So, no MbUnit, no NUnit, no xUnit. Sad.

In comes developer extraordinaire Jeff Wilcox and develops the Microsoft Silverlight Unit testing framework.

1. Download the latest Binaries from Microsoft Silverlight Unit Test Framework.
image
2. Create a project used for unit testing.
image

3. Add a reference to the 2 Unit testing DLLs to our testing project.
image 


4. From the testing project we’ll add a reference to the runtime project.
image image

 

5. Set your Application.Root in App.xaml.cs to a unit testing runner:

namespace SL2_UnitTestingRhinoMocksUnityRes_Tests

{

    public partial class App : Application

    {

        private void Application_Startup(object sender, StartupEventArgs e)

        {

            this.RootVisual = UnitTestSystem.CreateTestPage();

        }

Now we’ve got a basic unit testing project setup.

 

Next, we’ll create a new class to test our actual Silverlight “Page” class.

image

 

And we’ll put the appropriate attributes to run a test:

using Microsoft.VisualStudio.TestTools.UnitTesting;

using SL2_UnitTestingRhinoMocksUnityResharper;

 

namespace SL2_UnitTestingRhinoMocksUnityRes_Tests

{

    [TestClass]

    public class Page_Tests : SilverlightTest

    {

        [TestMethod]

        public void Page_Initialized_ShouldHaveAextBlock()

        {

            Page page = new Page();

 

            Assert.IsTrue(???);

        }

    }

}

 

In this test, we’d like to make sure that when Page has been initialized. And there is indeed a textblock with the Text hello world in there.

The problem with that is that we’ll need to add Page to the visual tree and wait for the loaded event.
Well, we can’t do that from a synchronous test. We’ll have to use an Async test format.

        [TestMethod]

        [Asynchronous]

        public void Page_Initialized_ShouldHaveATextBlock()

        {

            Page page = new Page();

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => Assert.IsTrue(page.txt != null));

 

            EnqueueTestComplete();

        }


Let’s go line by line here.

We’ll declare an async test method.

        [TestMethod]

        [Asynchronous]

        public void Page_Initialized_ShouldHaveATextBlock()

We’ll create a new page class to be tested.

            Page page = new Page();

We’ll wait for the Page.Loaded event after it has been added to the visual tree.

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

Next, we’ll tell the async testing engine that “after loaded has happened” we need to check if the page.txt public field (the TextBlock) has been initialized.

            EnqueueCallback(() => Assert.IsTrue(page.txt != null));

And than we’ll tell the async testing engine that we’re done testing.

            EnqueueTestComplete();

 

When we run our testing project we’ll see this:

image

 

Now, I’d like to confess that the “WaitFor” method isn’t something the comes with the Silverlight unit testing framework. It’s a little helper method I use. 
Here’s the syntax, though it’s not important:

protected void WaitFor<T>(T objectToWaitForItsEvent, string eventName)

{

    EventInfo eventInfo = objectToWaitForItsEvent.GetType().GetEvent(eventName);

 

    bool eventRaised = false;

 

    if (typeof(RoutedEventHandler).IsAssignableFrom(eventInfo.EventHandlerType))

        eventInfo.AddEventHandler(objectToWaitForItsEvent, (RoutedEventHandler)delegate { eventRaised = true; });

    else if (typeof(EventHandler).IsAssignableFrom(eventInfo.EventHandlerType))

        eventInfo.AddEventHandler(objectToWaitForItsEvent, (EventHandler)delegate { eventRaised = true; });

 

    EnqueueConditional(() => eventRaised);

}

There are plans to address the need for a WaitFor method in one form or another in future versions of the unit testing framework. 

 

Next, we’d like to test that the TextBlock has the correct text.

        [TestMethod]

        [Asynchronous]

        public void PageWithTextBlock_Initialized_ShouldHaveHelloWorldText()

        {

            Page page = new Page();

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Hello World!"));

 

            EnqueueTestComplete();

        }

 

And if we can run our test we’ll see that this test succeeded:

image

 

 

RhinoMocks – Hello World

1. Download RhinoMocks for silverlight.
image
2. Add a reference to these files from the Testing project:

image

As we move more towards real-world examples we need better tools to handle testing.
We’ll start off by creating a ViewModel for page:

    public class PageViewModel

    {

        private string _helloText = "Hello World!";

        public virtual string HelloText

        {

            get { return _helloText; }

            set { _helloText = value; }

        }

    }

It’s important to note that this is not a ViewModel article and I’m not following best MVVM practices.
MVVM and Unit testing are complimentary. but in the interest of keeping this article simple I’ll keep the ViewModel simple.

Next, we’ll create a ViewModel property on Page:

    public partial class Page : UserControl

    {

        private PageViewModel _viewModel = new PageViewModel();

        public PageViewModel ViewModel

        {

            get { return _viewModel; }

            set { _viewModel = value; }

        }

 

        public Page()

        {

            InitializeComponent();

        }

    }

And we’ll set the Page.DataContext to the ViewModel.

    public partial class Page : UserControl

    {

        private PageViewModel _viewModel = new PageViewModel();

        public PageViewModel ViewModel

        {

            get { return _viewModel; }

            set { _viewModel = value; }

        }

 

        public Page()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Page_Loaded);

        }

 

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            this.DataContext = ViewModel;

        }

    }

 

Let’s run our tests first to make sure we didn’t break anything:

image

We didn’t break anything, we’re good.

 

Now, let’s add another test class for PageViewModel.

    [TestClass]

    public class PageViewModel_Tests : SilverlightTest

    {

        [TestMethod]

        public void PageViewModel_ByDefault_ShouldHaveHelloWorldTest()

        {

            PageViewModel pageViewModel = new PageViewModel();

 

            Assert.IsTrue(pageViewModel.HelloText == "Hello World!");

        }

    }

And we’ll run our tests:

image

All green.

 

Now it’s time to hook up our TextBlock to the ViewModel.

<UserControl x:Class="SL2_UnitTestingRhinoMocksUnityResharper.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Width="400" Height="300">

    <Grid x:Name="LayoutRoot" Background="White">

        <TextBlock x:Name="txt" x:FieldModifier="public" Text="{Binding HelloText}" />

    </Grid>

</UserControl>

And let’s run our test again to make sure everything still works:

image

 

Now, we’d like to test that PageViewModel.HelloText does get invoked by Page.
Finally, we can use RhinoMocks.

Here’s one way to write that test:

        [TestMethod]

        [Asynchronous]

        public void Page_Loaded_ShouldUsePageViewModelHelloTextOnce()

        {

            Page page = new Page();

            page.ViewModel = new PageViewModel() { HelloText = "Bonjour Monde!" };

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Bonjour Monde!"));

 

            EnqueueTestComplete();

        }

Right now this test would run, but it has 2 main issues:
1. We’re testing PageViewModel when really we want to test Page invoking PageViewModel.
2. We’re testing the entire Page Lifecycle in the UI, which we don’t want to do.

image

This test is supposed to test the red arrow “Invokes”, but instead we’re testing this entire diagram.
Which makes our test brittle and poorly written.

Let’s write a test that only checked the “Invokes” arrow.

        [TestMethod]

        [Asynchronous]

        public void Page_Loaded_ShouldUsePageViewModelHelloTextOnce()

        {

            Page page = new Page();

            MockRepository mocks = new MockRepository();

            PageViewModel pageViewModel = (PageViewModel) mocks.StrictMock(typeof(PageViewModel));

            page.ViewModel = pageViewModel;

 

            using(mocks.Record())

            {

                pageViewModel.Expect(p => p.HelloText).Return(string.Empty);

            }

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => mocks.VerifyAll());

 

            EnqueueTestComplete();

        }

Let’s go line-by-line here.

            MockRepository mocks = new MockRepository();

            PageViewModel pageViewModel = (PageViewModel) mocks.StrictMock(typeof(PageViewModel));

We’ve initialized a mock repository which is a magical doohickey that creates mocks and checks expectations.
We used it to create a mock for PageViewModel.

            using(mocks.Record())

            {

                pageViewModel.Expect(p => p.HelloText).Return(string.Empty);

            }

We’ve recorded an expectation that someone should invoke PageViewModel.HelloText. (And it should return a String.Empty)

            EnqueueCallback(() => mocks.VerifyAll());

And in the end of the test, in stead of checking the UI we’ll check the expectation have been met.

 

Let’s run our test:

image

And now let’s run it again if we temporarily delete the binding:

<TextBlock x:Name="txt" x:FieldModifier="public" Text="{Binding HelloText}" Text="Hello World!" />

image

We can clearly see that if the PageViewModel.HelloText property’s getter isn’t called, RhinoMocks will fail the test.

image

 

Let’s add another test to test the whole workflow without testing the PageViewModel itself:

        [TestMethod]

        [Asynchronous]

        public void Page_Loaded_ShouldUsePageViewModelHelloTextInTextBlock()

        {

            Page page = new Page();

            MockRepository mocks = new MockRepository();

            PageViewModel pageViewModel = (PageViewModel)mocks.StrictMock(typeof(PageViewModel));

            page.ViewModel = pageViewModel;

 

            using (mocks.Record())

            {

                pageViewModel.Expect(p => p.HelloText).Return("Bonjour Monde!");

            }

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Bonjour Monde!"));

            EnqueueCallback(() => mocks.VerifyAll());

 

            EnqueueTestComplete();

        }

 

And again, all of our tests pass:

image

 

The advantage of testing with Mocks – we test only what we need to test – we didn’t the actual PageViewModel class in this test.

 

 

Microsoft Unity – Hello World

Unity is an Inversion of Control framework which let’s us easily reduce the complexity of our object model.
Big words, eh? Let’s get down to the code.

 

1. Download and install Unity for Silverlight.

image

2. Add references to the Unity DLL to both our runtime and testing project.

image image

 

Now, we’ll remove the Page.ViewModel property.

    public partial class Page : UserControl

    {

        private PageViewModel _viewModel = new PageViewModel();

        public PageViewModel ViewModel

        {

            get { return _viewModel; }

            set { _viewModel = value; }

        }

And we’ll add a property that will get automatically populated by Unity:

    public partial class Page : UserControl

    {

        [Dependency]

        public PageViewModel ViewModel { get; set; }

Basically the [DependencyAttribute] says “Hey, Unity! I’m here! put a value into this property.”

 

Now, we’ll create a static IoC used from everywhere in our App:

    public static class IoC

    {

        static IoC()

        {

            Current = new UnityContainer();

        }

 

        public static UnityContainer Current { get; set; }

    }

In our runtime project App.xaml.cs we’ll register “PageViewModel” type as the correct type to fill for any property of type PageViewModel.

    public static class IoC

    {

        static IoC()

        {

            Current = new UnityContainer();

            Current.RegisterType<PageViewModel, PageViewModel>();

        }

 

        public static UnityContainer Current { get; set; }

    }

Not the best of examples, I’ll admit that. But if we used a IPageViewModel this piece of code would have more meaning, and would look like this:

    public static class IoC

    {

        static IoC()

        {

            Current = new UnityContainer();

            Current.RegisterType<IPageViewModel, PageViewModel>();

        }

 

        public static UnityContainer Current { get; set; }

    }

Which basically tells “Hey, Unity! Whenever you need to fill a property of type IPageViewModel use PageViewModel.”
Since we’re not using such an interface, we’ll go back to the original syntax.

 

One last thing we have to do is tell Unity to populate our type:

    public partial class Page : UserControl

    {

        [Dependency]

        public PageViewModel ViewModel { get; set; }

 

        public Page()

        {

            InitializeComponent();

           IoC.Current.BuildUp(this);

            this.Loaded += new RoutedEventHandler(Page_Loaded);

        }

 

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            this.DataContext = ViewModel;

        }

    }

 

Now, let’s run our app:

image

It still shows “Hello world!”.
Let’s go over how that happened:

image

How does that help us write better code?
Well, right now we only have a dependency between 2 classes. So it doesn’t help us a lot.
image

Becomes:

image

But when dealing with real-world object graphs with a more complicated Object graph it becomes obvious why IoC containers are good:

image

When using IoC containers:

image

In a real-world app, everyone registers with the IoC container and resolve dependencies with it.

 

Now, let’s use Unity in on our unit tests and re-write our existing tests to use Unity to resolve these dependencies:

        [TestMethod]

        [Asynchronous]

        public void Page_Loaded_ShouldUsePageViewModelHelloTextOnce()

        {

            Page page = new Page();

            MockRepository mocks = new MockRepository();

            PageViewModel pageViewModel = (PageViewModel) mocks.StrictMock(typeof(PageViewModel));

            page.ViewModel = pageViewModel;

 

            using(mocks.Record())

            {

                pageViewModel.Expect(p => p.HelloText).Return(string.Empty);

            }

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => mocks.VerifyAll());

 

            EnqueueTestComplete();

        }

we’ll re-write the test to use our Unity container:

        [TestMethod]

        [Asynchronous]

        public void Page_Loaded_ShouldUsePageViewModelHelloTextOnceWithUnity()

        {

            MockRepository mocks = new MockRepository();

            IoC.Current.RegisterInstance(typeof(PageViewModel), mocks.StrictMock(typeof(PageViewModel)));

            Page page = IoC.Current.Resolve<Page>();

 

            using (mocks.Record())

            {

                IoC.Current.Resolve<PageViewModel>().Expect(p => p.HelloText).Return(string.Empty);

            }

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => mocks.VerifyAll());

 

            EnqueueTestComplete();

        }

 

Let’s go line-by-line here:

IoC.Current.RegisterInstance(typeof(PageViewModel), mocks.StrictMock(typeof(PageViewModel)));

Basically we’re telling Unity that whenever it needs to resolve PageViewModel it should use a PageViewModel mock from RhinoMocks.

Page page = IoC.Current.Resolve<Page>();

Instead of initializing a type with “new myClass();” we’re using Unity to get a class that has all of it’s dependencies resolved. In our case, a Page with a PageViewModel Mock.

            using (mocks.Record())

            {

                IoC.Current.Resolve<PageViewModel>().Expect(p => p.HelloText).Return(string.Empty);

            }

In stead of keeping a local copy of the Mock we created for Unity, we’re using Unity’s Resolve method to test get the mock back.

 

And when we run our tests:

image

 

Let’s rewrite this test with Unity:

[TestMethod]

[Asynchronous]

public void Page_Loaded_ShouldUsePageViewModelHelloTextInTextBlock()

{

    Page page = new Page();

    MockRepository mocks = new MockRepository();

    PageViewModel pageViewModel = (PageViewModel)mocks.StrictMock(typeof(PageViewModel));

    page.ViewModel = pageViewModel;

 

    using (mocks.Record())

    {

        pageViewModel.Expect(p => p.HelloText).Return("Bonjour Monde!");

    }

 

    WaitFor(page, "Loaded");

    TestPanel.Children.Add(page);

 

    EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Bonjour Monde!"));

    EnqueueCallback(() => mocks.VerifyAll());

 

    EnqueueTestComplete();

}

 

Becomes:

[TestMethod]

[Asynchronous]

public void Page_Loaded_ShouldUsePageViewModelHelloTextInTextBlockWithUnity()

{

    MockRepository mocks = new MockRepository();

    IoC.Current.RegisterInstance(typeof(PageViewModel), mocks.StrictMock(typeof(PageViewModel)));

    Page page = IoC.Current.Resolve<Page>();

 

    using (mocks.Record())

    {

        IoC.Current.Resolve<PageViewModel>().Expect(p => p.HelloText).Return("Bonjour Monde!");

    }

 

    WaitFor(page, "Loaded");

    TestPanel.Children.Add(page);

 

    EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Bonjour Monde!"));

    EnqueueCallback(() => mocks.VerifyAll());

 

    EnqueueTestComplete();

}

 

And when we run our tests:

image

 

Some slight of hand I used here is to change this test:

        [TestMethod]

        [Asynchronous]

        public void PageWithTextBlock_Initialized_ShouldHaveHelloWorldText()

        {

            Page page = IoC.Current.Resolve<Page>(); //new Page();

 

            WaitFor(page, "Loaded");

            TestPanel.Children.Add(page);

 

            EnqueueCallback(() => Assert.IsTrue(page.txt.Text == "Hello World!"));

 

            EnqueueTestComplete();

        }

We no longer initialize types with “new” rather through the IoC container.

And I added this Piece of code to initialize our IoC between each test:

[TestInitialize]

public void Init()

{

    IoC.Current = new UnityContainer();

}

 

 

 

Silverlight Unit Test Runner for Resharper – Hello World

Running our tests breaks the flow of development.

We have to exit Visual studio, run the test harness and wait for it to run.
In comes Einar Ingebrigtsen’s Resharper SIlverlight Unit test runner.

1. Install R# (Resharper) from http://www.jetbrains.com/resharper/download/index.html 
R# is a commercial plugin for Visual studio, but has 30 first days free.

2. Download the latest version of the R# Silverlight Unit test runner from Einar’s blog.
image

3. Drag & Drop the 3 DLLs into the machine’s GAC. (On my computer at: c:\Windows\Assembly)
image 

4. Copy the 3 DLLs and the “Website” directory into a subdirectory under the Resharper plugin directory.
(On my computer: C:\Program Files\JetBrains\ReSharper\v4.1\Bin\Plugins\C:\Program Files\JetBrains\ReSharper\v4.1\Bin\Plugins\)

image

5. Restart Visual studio and when prompted for a website address put in the file path for the ‘website’ directory.
image

 

with our testing project open, open up the Resharper Unit Testing explorer.

image

And you can see our 7 Silverlight unit tests In the R# Unit test explorer.
We’ll press the “run” button to run all the tests.

image

A new R# Test session starts up and we’ll have to wait 5 seconds the first time for tests to start running. 

image

And we can see that all test pass: 

image

 

And finally, it’s just easier to run our tests by pressing “Run all tests from solution” (and maybe assigning a shortcut for it).

image

 

 

Summary – Hello World

Hopefully this short blog post is enough to get you on your way in the exciting world of unit testing.

None of these solutions are the sole frameworks in their respective field, but they are a good start.
The article doesn’t talk about best practices like Unity AutoMocker or ViewModels, but it does give you a good start.
I’ll try to cover other Silverlight IoC containers and Silverlight Mocking solutions in future blog posts.

 

Sincerely,

-- Justin Angel

Microsoft Silverlight Toolkit Program Manager

Tour around Silverlight Contrib

I’m a huge supporter of open source software development and I'd like to take this opportunity to go over the Silverlight Contrib O/S project.

Fair disclosure first though, my current role is Program Manager for the Silverlight Toolkit team which ships on codeplex (at http://www.codeplex.com/Silverlight). 
I’ve got a very high quality bar and I expect nothing less from any O/S project that I ask customers to put into their technology stack.

Standard Microsoft disclaimer: KGB Assassins will be hired toWait, other Disclaimer: I do not speak for my employer.

 

Getting Started

OK, this is straight forward and well-organized.

Codeplex website @ http://www.codeplex.com/SilverlightContrib.
I went to the Release Tab and downloaded the latest Alpha 3 release from December 15th.

Official Website @ http://SilverlightContrib.org.

And live demo @ http://silverlightcontrib.org/demo/.

Silverlight Contrib Demo App

First impressions is really important, especially in a open source projects. A real problem that plagues open source initiatives is the lack of proper documentation and support.
Silverlight Contrib has an excellent first impression. Excellent website organization, solid sample app, and it even comes with a CHM file!

image

Unfortunately some of the subjects in the CHM file clearly say they’re missing documentation:

image

 

OK, Let’s start a new SIlverlight Application and add the (3?) Silverlight Contrib DLLs.

image

image 

image

And here’s how our solution looks like after we added the DLLs:

image

 

Inside the XAML editor, we’ll go to “Choose Items” and add the “SilverlightContrib.Controls.dll” to the Visual Studio Toolbox:

image    --> image

 

 

Color Picker

Ok, enough fooling around. Let’s build something. We’ll start from the Color Picker.

Based on the CHM file here’s what we’ve got In ColorPicker:

image

We’ve got a ColorSelected event and a SelectedColor property.

Let’s build a simple app that has a color picker and a rectangle that uses that colour as fill.

<UserControl x:Class="SL_Contrib.Page"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   xmlns:SilverlightContrib="clr-namespace:SilverlightContrib.Controls;assembly=SilverlightContrib.Controls"

   Width="400" Height="200">

    <StackPanel x:Name="LayoutRoot" Background="White">

        <SilverlightContrib:ColorPicker x:Name="colorPicker" Width="200" Height="200" SelectedColor="Green" />

        <Rectangle x:Name="myRect" Fill="Green" Width="100" Height="100" />

    </StackPanel>

</UserControl>

Hmm… I was writing the event registration there for a second and got confused… Why is the Property called “SelectedColor” and the change event called “ColorChanged”? Shouldn’t it be “SelectedColorChanged”? Minor annoyance, but still.

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

            colorPicker.ColorSelected += new ColorSelectedHandler(colorPicker_ColorSelected);

        }

 

        private void colorPicker_ColorSelected(object sender, ColorSelectedEventArgs e)

        {

            myRect.Fill.As<SolidColorBrush>().Color = e.SelectedColor;

        }

    }

 

    public static class Extensions

    {

        public static T As<T>(this object obj)

        {

            return (T) obj;

        }

    }

(I’ve added my As<T> extension method)

Let’s run this sample:

image

Truly a master-piece app.

Yep, and when we select another color the rectangle gets updated.

image image image

It looks like the ColorPicker isn’t invoking the ColorChanged event when you just drag & drop…

image image

You can see that the in the first picture I’ve started dragging and the second picture is while still dragging.
The control visuals get updated, but there’s no external event you can use to pick up on that until the user drops and release the mouse capture.

 

The TextBlock on the bottom left isn’t editable, so we’ll create another TextBox and try to change the selected colour.

<SilverlightContrib:ColorPicker x:Name="colorPicker" Width="200" Height="200" SelectedColor="Green" />

<TextBox x:Name="tbxColour" />

<Button x:Name="btnCommit" Content="Change colour" />

<Rectangle x:Name="myRect" Fill="Green" Width="100" Height="100" />

   

    public Page()

    {

        InitializeComponent();

        colorPicker.ColorSelected += new ColorSelectedHandler(colorPicker_ColorSelected);

        btnCommit.Click += new RoutedEventHandler(btnCommit_Click);

    }

 

    void btnCommit_Click(object sender, RoutedEventArgs e)

    {

        byte alpha = Convert.ToByte(tbxColour.Text.Substring(0, 2), 16);

        byte red = Convert.ToByte(tbxColour.Text.Substring(2, 2), 16);

        byte green = Convert.ToByte(tbxColour.Text.Substring(4, 2), 16);

        byte blue = Convert.ToByte(tbxColour.Text.Substring(6, 2), 16);

        colorPicker.SelectedColor = Color.FromArgb(alpha, red, green, blue);

    }

And when we run our sample:

imageimage

Works like a charm.

 

I’ve opened the application in Blend to try and change the default layout of the ColorPicker.

image

Now, I changed the default visual tree for this control to move the Hue selector from the left to the right and viola:

image

Still works like a charm:

image

One cool thing though that I’ve noticed in Blend is how the nice spectrum is built:

image

It’s basically a Gradient with a bunch of GradientStops. cool stuff. I keep being amazed by how people use the basic 2d drawing abilities of Silverlight.

 

 

 

All and all, this is a very cool control.

Pros:

  1. Very cool visuals appearance. Main color selection palette, side spectrum selection palette and an area that shows the currently selected color and it’s Hex.
  2. Solid control.

Minor cons:

  • The event naming for “ColorChanged” is not consistent with the property name “SelectedColor”.
  • No event “ColorChanging” that fired while the eye dropper is being dragged.
  • The Hex textbox isn’t editable

 

 

Cool Menu

From the sample app:

image

 

Here’s the CHM file doc on this control:

image

So we’ve got Items, MaxItemHeight, MaxItemWidth, MenuIndexChanged, MenuItemClicked  & MenuItemClickEffect.

 

Let’s build a quick cool menu with some items.
I’ll use Dobby the penguin from Jose Farajdo’s blog - http://advertboy.wordpress.com/blendcandy/.

   <SilverlightContrib:CoolMenu Height="200">

            <SilverlightContrib:CoolMenu.Items>

                <SilverlightContrib:CoolMenuItemCollection>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem>

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                </SilverlightContrib:CoolMenuItemCollection>

            </SilverlightContrib:CoolMenu.Items>

        </SilverlightContrib:CoolMenu>

I’m not crazy about this syntax.

First, I have to specify I’m using the Items collectiom, than the specific collection CollMenuItemCollection, than a CoolMenuItem, than Content and finally I can specify the actual Items. Why not support a much simpler syntax?

        <SilverlightContrib:CoolMenu Height="200">

            <SL_Contrib:PenguinControl />

            <SL_Contrib:PenguinControl />

            <SL_Contrib:PenguinControl />

            <SL_Contrib:PenguinControl />

            <SL_Contrib:PenguinControl />

        </SilverlightContrib:CoolMenu>

 

Anyway, let’s run our app with the extended syntax:

image

When we mouse over one of the items:

image

image

This menu is pretty cool.

 

Oh, this is cool: When I click Dobby he drops down and up in a nice elastic bounce motion.

image

image

image

image

Pretty cool.

 

OK, Now I’d like to grab a click on one of the menu items.
We’ll name the menu and add a Text property to the MenuItem.

        <SilverlightContrib:CoolMenu Height="200" x:Name="menu">

            <SilverlightContrib:CoolMenu.Items>

                <SilverlightContrib:CoolMenuItemCollection>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #1">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #2">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #3">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #4">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #5">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                    <SilverlightContrib:CoolMenuItem Text="Penguin #6">

                        <SilverlightContrib:CoolMenuItem.Content>

                            <SL_Contrib:PenguinControl />

                        </SilverlightContrib:CoolMenuItem.Content>

                    </SilverlightContrib:CoolMenuItem>

                </SilverlightContrib:CoolMenuItemCollection>

            </SilverlightContrib:CoolMenu.Items>

        </SilverlightContrib:CoolMenu>

 

    public partial class CoolMenuPage : UserControl

    {

        public CoolMenuPage()

        {

            InitializeComponent();

            menu.MenuItemClicked += new SilverlightContrib.Controls.MenuIndexChangedHandler(menu_MenuItemClicked);

        }

 

        void menu_MenuItemClicked(object sender, SilverlightContrib.Controls.SelectedMenuItemArgs e)

        {

            txt.Text = e.Item.Text;

        }

    }

 

image

image

 

Nice.

 

Pros:

  1. Pretty cool all and all. The resizing effect, the nice elastic bounce on click and how the rest of the items move. All of these create a solid User eXprience.

Cons:

  • The menu only ships with 2 types of effects “Bounce” and “None”. There’s no extensible mechanisms to add your own effect. Not even through VSM. 
  • The menu should inherit from ItemsControl, which it doesn’t. I can’t use any Item templating goodness with this control. I’d like to be able to run this code:

menu.ItemSource = new List<Person>()

           {

               new Person(Name = "Justin", Picture = "Awesomeness.jpg"),

               new Person(Name = "RJ", Picture = "YaImADutch.jpg"),

               new Person(Name = "Dobby", Picture = "Penguin.jpg"),

           };

        <SilverlightContrib:CoolMenu>

            <SilverlightContrib:CoolMenu.ItemTemplate>

                <DataTemplate>

                    <StackPanel>

                        <Image Source="{Binding Picture}" />

                        <TextBlock Text="{Binding Name}" />

                    </StackPanel>

                </DataTemplate>

            </SilverlightContrib:CoolMenu.ItemTemplate>

        </SilverlightContrib:CoolMenu>

 

 

 

Tweening

From Wikipedia:

“Inbetweening or tweening is the process of generating intermediate frames between two images to give the appearance that the first image evolves smoothly into the second image. Inbetweens are the drawings between the keyframes which help to create the illusion of motion. Inbetweening is a key process in all types of animation, including computer animation.” – copied from http://en.wikipedia.org/wiki/Tweening 

Basically, Tweening is creating cool effects :)

Let’s see a basic example of this.

Here’s a basketball I draw in a few seconds in Blend:

image

(Yes, It’s a Basketball. Prove that it isn’t.)

Now, I’d like for it to bounce down and up and down again in a realistic fashion.

image

 

In order to do that, We’ll add a storyboard in Blend.

image

 

At 0.0 we want the basketball to be at the initial position.

image

At 0.5 seconds we want it at the bottom.

image

At 0.7 seconds we want it to back up a bit.

image

And down again in 0.8 seconds:

image

And on load we’ll start the stortboard:

        void SilverlightControl1_Loaded(object sender, RoutedEventArgs e)

        {

            Resources["Storyboard1"].As<Storyboard>().Begin();

        }

 

 

Than once we run the page we’ll see the full animation.

However, we might not want to specify each possible frame to build realistic effects.
It’s hard, and it’s a very hard task.

In comes the Tweening library.
First, we’ll delete all keyframes besides the last one.

image

Than, back in Visual studio we’ll have a look at that last keyframe.

        <Storyboard x:Name="Storyboard1">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="canvas" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">

                <SplineDoubleKeyFrame KeyTime="00:00:00.8000000" Value="186"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

We’ll start by deleting even that last animation and copying it’s Keyframe time to the Duration.

        <Storyboard x:Name="Storyboard1">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

                                          Duration="00:00:00.8000000"

                                          Storyboard.TargetName="canvas"

                                          Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" />

        </Storyboard>

Next, We’ll add some Tweening attached properties.

image

Now, let’s set the initial value, the target value, the number of frames we’d like generated for us and the Transition type. .

image image image

And here’s our final storyboard XAML:

<Storyboard x:Name="Storyboard1">

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

                                  Duration="00:00:00.8000000"

                                  Storyboard.TargetName="canvas"

                                  Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)"

                                  Tweener:Tween.From="0"

                                  Tweener:Tween.To="186"

                                  Tweener:Tween.Fps="20"

                                  Tweener:Tween.TransitionType="EaseInOutBounce"

                                  />

</Storyboard>

 

And once we run our app we can see the ball falls down, bounces up a bit and falls back down.

 

Pro:

  1. Tweening is a well established concept in the designer audience and a very productive way of creating complex animations quickly.

Con:

  • No blend support.

 

 

Clipboard support

At times you might want to have Silverlight access directly into your clipboard. Which isn’t natively supported in Silverlight.

<StackPanel x:Name="LayoutRoot" Background="White">

    <TextBox x:Name="tbx" />

    <Button Click="Copy_Click" Content="Copy TextBox to clipboard" />

    <Button Click="Paste_Click" Content="Paste clipboard to TextBox" />

    <Button Click="Clear_Click" Content="Clear clipboard" />

</StackPanel>

 

Here’s how this page looks like when we run it.

image

 

We’d like the copy button to copy the Text from the TextBox to the clipboard.

First, we’ll create a property that will use lazy initialization to Initialize a ClipboardHelper class.

private ClipboardHelper _clipboard = null;

ClipboardHelper Clipboard

{

    get

    {

        try

        {

            if (_clipboard == null)

                _clipboard = new ClipboardHelper();

        }

        catch (InvalidOperationException)

        {

            MessageBox.Show("Current browser isn't supported for clip board opearations.");

        }

        return _clipboard;

    }

}

This class has resource implications and browser support implications, so it’s best to cache it once initialized.

 

Now, once the Copy button is clicked we’d like to copy the contents of the TextBox into the Clipboard.

        private void Copy_Click(object sender, RoutedEventArgs e)

        {

            if (Clipboard != null)

                Clipboard.SetData(tbx.Text);

        }

Let’s run this application and put some text into the TextBox:

image

 

Once we click the button the user will be prompted to allow the browser app to access the clipboard:

image

The user must click “Allow Access”.

 

Now let’s paste this outside our browser:

image

 

So we can use the Clipboard helper from SIlverlight contrib to cross through browsers boundaries.

 

Now, let’s implement all buttons.

        private void Copy_Click(object sender, RoutedEventArgs e)

        {

            if (Clipboard != null)

                Clipboard.SetData(tbx.Text);

        }

 

        private void Paste_Click(object sender, RoutedEventArgs e)

        {

            if (Clipboard != null && !string.IsNullOrEmpty(Clipboard.GetData()))

                tbx.Text = Clipboard.GetData();

        }

 

        private void Clear_Click(object sender, RoutedEventArgs e)

        {

            if (Clipboard != null)

                Clipboard.ClearData();

        }

And once we run our application we see that we can copy information from outside the browser inside our Silverlight Application.

image

image

 

And we can clear the clipboard and paste again to see it’s been emptied out.

image

 

Pros:

  1. Clipboard support is something that can make or break certain apps.
  2. This is an excellent API for something that is basically a huge managed wrapper around Javascript. Having a Dispose method on the ClipboardHelper shows the Contrib team really did a good job here.
  3. This could enable a range of scenarios (copying whole files from outside the browser?)

Con:

  • The current implementation is not supported in FireFox.

image

 

 

Sum up

The Silverlight Contrib is a top notch open source project with some excellent controls, utilities and helpers you should definitely take a look at.

Go get it at http://codeplex.com/SilverlightContrib.

 

 

-- Justin Angel 

Microsoft Silverlight Toolkit Program Manager

Silverlight DLLs on the desktop CLR

I’m a dinosaur and it’s possible I’m the only who didn’t know this – But you can use Silverlight DLLs on the full .Net desktop CLR.

See, I’ve been working with Silverlight since Mix07 (March 2007) when we were all excited about Silverlight 1.0 and Silverlight 1.1. 
First I was working as a senior .Net consultant on a variety of projects, and for the last 6 months I’ve been working on the Silverlight Toolkit for the evil empire Microsoft. 

So believe me when I say – I’m an aging dinosaur to miss something this big. In Silverlight 2.0 you can share DLLs between the desktop and the Silverlight CLR.
I know for sure it wasn’t possible in Silverlight 1.1, and just assumed it wasn’t possible in Silverlight 2.

From all the articles I’ve read in the last few months, It seems I’m not the only one who doesn’t know this:
http://pagebrooks.com/archive/2008/10/11/sharing-code-between-.net-and-silverlight-platforms.aspx
http://petesbloggerama.blogspot.com/2008/12/referencing-non-silverlight-assembly-in.html
http://stackoverflow.com/questions/208123/what-is-the-best-practice-for-compiling-silverlight-and-wpf-in-one-project

All of these fairly recent articles say the same – You need to use the “Add as link” hackery to share source between Silverlight CLR and the full .Net CLR.

image

Well, Apparently that’s not true. You can add a reference from a full .Net CLR to a Silverlight assembly.

 

Let’s see how to do that:

1. Let’s start a new Silverlight Application:
image

Here’s the Solution we got:

image

2. Let’s add a Silverlight Class Library to hold our Business logic.

image

Here’s our solution now:

image

3. Let’s add a class with some business logic.

public class Person

{

    private string _firstName;

    public string FirstName

    {

        get { return _firstName; }

        set { _firstName = UpperCaseFirstLetter(value); }

    }

 

    private string _lastName;

    public string LastName

    {

        get { return _lastName; }

        set { _lastName = UpperCaseFirstLetter(value); }

    }

 

    private string UpperCaseFirstLetter(string str)

    {

        if (string.IsNullOrEmpty(str) || str.Length == 0)

            return str;

 

        return str[0].ToString().ToUpper() + str.Substring(1, str.Length - 1);

    }

 

    public string FullName

    {

        get

        {

            return string.Format("{0} {1}", FirstName, LastName);

        }

    }

}

This class has 3 interesting things in it:

  1. It has 2 properties called “FirstName” and “LastName” that have a backing field.
  2. When setting the First and last name, we store these with the first character as uppercase. Property setter business logic.
  3. We have a property that combines that value of the first two properties. Pure business logic.

 

4. We’ll add a new WPF project that runs of the desktop .Net CLR. (We could have used the ASP.Net project already in the solution, but I’d like to demo WPF)

image

5. Now, we’ll add a reference from the WPF project to the Silverlight Class Library.

image

image

image

 

Using the Business logic in the Silverlight application

First, I’ll add a reference from the Silverlight Application to the Silverlight Class Library.

image

Next, I’ll add a reference to the Silverlight Toolkit (http://codeplex.com/Silverlight) mainly because we need the WrapPanel.

image

Next, we’ll add this little form to our default page:

<Border x:Name="LayoutRoot" Background="White" Margin="20" CornerRadius="5" BorderBrush="LightGray" BorderThickness="1" >

    <controls:WrapPanel Width="150" >

        <TextBlock Width="75" Text="First Name:" />

        <TextBox Width="75" Text="{Binding FirstName, Mode=TwoWay}" TabIndex="1" />

        <TextBlock Width="75" Text="Last Name:" />

        <TextBox Width="75" Text="{Binding LastName, Mode=TwoWay}" TabIndex="2" />

        <TextBlock Width="75" Text="Full Name:" />

        <TextBox Width="75" x:Name="txtFullName" IsReadOnly="True" InputMethod.IsInputMethodEnabled="False" />

        <Button Content="Update full name" Click="Button_Click" />

    </controls:WrapPanel>

</Border>

This might look lie a lot, but it’s really not.
It’ just the following form:

image

It’s only 3 textblocks, 2 editable textboxes, 1 readonly TextBox and a button.

Now let’s add some code to our code behind:

using BL;

 

namespace SilverlightApp

{

    public partial class Page : UserControl

    {

        public Page()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Page_Loaded);

        }

 

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

            this.DataContext = new Person();

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            txtFullName.Text = ((Person) this.DataContext).FullName;

        }

    }

}

Basically, we set the DataContext for the whole form to a Person class and once the button is clicked we manually update the txtFullName.
We could have used Dependency Properties to update this for us, but I’d rather keep this demo simple :)

 

If we run our application we’ll see the following form:

image

We’ll put a very sexy and rugged first name and last name.

image

Next we’ll click the Update button:

image

Well, that’s pretty straight forward Silverlight. (And if we added Dependency Properties it would have been much better, but this isn’t a Silverlight demo)

 

 

Using the Silverlight Class Library / Business Logic in the WPF application

We’ll create a similar yet not identical version of the form in WPF:

<Window x:Class="WPFApp.Window1"

   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Title="Window1" Height="300" Width="300">

    <Grid>

        <WrapPanel Width="150" >

            <TextBlock Width="75" Text="First Name:" />

            <TextBox Width="75" Text="{Binding FirstName}" TabIndex="1" />

            <TextBlock Width="75" Text="Last Name:" />

            <TextBox Width="75" Text="{Binding LastName}" TabIndex="2" />

            <TextBlock Width="75" Text="Full Name:" />

            <TextBox Width="75" x:Name="txtFullName" IsReadOnly="True" InputMethod.IsInputMethodEnabled="False" />

            <Button Content="Update Full Name" Click="Button_Click" />

        </WrapPanel>

    </Grid>

</Window>

And here’s the pretty almost identical code behind:

using BL;

 

namespace WPFApp

{

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);

        }

 

        void Window1_Loaded(object sender, RoutedEventArgs e)

        {

            this.DataContext = new Person();

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            txtFullName.Text = ((Person) this.DataContext).FullName;

        }

    }

}

Let’s run this app.

image

We’ll put in the ruggedly manly name we used before.

image

Click the update button.

 

image

And yep, the same result – the Silverlight code runs on both the desktop CLR and the SIlverlight CLR.

 

One picture is worth an entire blog post

image

 

How does this even work? 

The System.String from Silverlight is a completely different System.String from the desktop .Net. So how does this even work? I’m still looking into this, but before I got the results of my research I thought it’s definitely worth sharing.

 

This has limitations, probably a lot

Using the full abilities of the Silverlight runtime is obviously not possible from our Silverlight class library when it runs on the desktop. There’s no Application.Current, no dispatcher and a host of other things are missing. This limit us to really sharing only business logic in this method, not UI.
For one, I’m not sure it’s not a good thing that we’re being forced to keep best practices of layer separation.

 

Source code available at: http://silverlight.net/blogs/justinangel/BlogStorage/SharingCode.zip

 

-- Justin Angel

Microsoft Silverlight Toolkit Program Manager

Posted by JustinAngel | 4 comment(s)
Filed under:

Custom VSM VisualStateManagers in Silverlight 2.0

Hi Folks

I’ve been getting some questions through the usual backchannels (Twitter, my J@JustinAngel.Net email and mail pigeons) about creating custom VSM managers.

If you’re unfamiliar with VSM I strongly suggest you take 30 minutes and watch Steve White talking in his ‘ohhh-so-British' voice about it:

Watch these 4 videos on the Blend 2.5 website which should provide you with deep technical insight into the inner workings of Templating Parts & VSM States from Blend: http://expression.microsoft.com/en-ca/cc643423.aspx

Let’s try and conceptually sum up what VSM does:

image 

Well, we could have just said this instead:

image

 

 

Point is – the VisualStateManager class is the driving force behind the actual change between states.

 

Let’s see how that comes into play.

Here are all the states for a Button: (snapshot from Blend)

image

Let’s put a normal button on our form, run the app and see it’s MouseOver state:

<Button x:Name="myButton" Content="myButton" Width="100" Height="100" />

image image

We can see that once the mouse enters the button area, the MouseOver state is fired, a storyboard executes and everyone’s happy.

But how executes the state change? VSM of course.

Let’s see how we can “interfere” with the normal operations of VSM.

        public SilverlightControl2536()

        {

            InitializeComponent();

            myButton.MouseEnter += new MouseEventHandler(myButton_MouseEnter);

        }

 

        private void myButton_MouseEnter(object sender, MouseEventArgs e)

        {

            VisualStateManager.GoToState(myButton, "Disabled", true);

        }

Over here I’ve registered to the MouseEnter event and told VSM that “On MouseEnter go to the Disabled State”.

image

We can definitely see that mouseovering the button clearly puts it in Disabled state.

And we caused this little change through talking directly to the VSM static class.
I’ll just point out that the button isn’t really disabled and is still clickable. All we did here is cause a VSM state change, which caused a storyboard to begin, which changed the Visual UI of the button.

 

As for demos – the disabled button one was short, to the point and utterly useless. Let’s get our hands dirty with some custom VSM goodness.

 

Writing a Custom VisualStateManager – Getting the current states

We just demonstrated and explained that the VisualStateManager is an actual class which drives state management at the control level.
We can also create our own VisualStateManager that has additional functionality and logic in that stage.

public class CustomVisualStateManager : VisualStateManager

{

    protected override bool GoToStateCore(Control control, FrameworkElement templateRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions)

    {

this.

        return base.GoToStateCore(control, templateRoot, stateName, group, state, useTransitions);

    }

}

This basic class doesn’t do a lot. We inherit from VisualStateManager and override the only method we can – GoToStateCore.

We can see that we get some important data:

  1. What control is changing it’s state
  2. What’s it’s root template element
  3. What group are we changing state to
  4. What state are we changing state to
  5. And should we use Transitions

Now, if we were the original VisualStateManager class this is the place to actually do state changes, fire storyboards, raise events and what not.
Luckily, we’re not. So we’ll call the base method and that would take care of actually running states. Which leaves us as free to do whatever we want.

And here’s what we want – We want to be able to query the VisualStateManager and get a string the represents the control’s current state in each state group.

public class QueryableVisualStateManager : VisualStateManager

{

    protected override bool GoToStateCore(Control control, FrameworkElement templateRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions)

    {

        return base.GoToStateCore(control, templateRoot, stateName, group, state, useTransitions);

    }

 

    public static string QueryState(Control controlToFindItsState)

    {

 

    }

}

Now, at this point we’ll add a lot of plumbing to store the last fired states in each group for each control.

public class QueryableVisualStateManager : VisualStateManager

{

    /// <summary>

    /// Dictionary of controls --> Dictionary of group names and state names

    /// </summary>

    private static Dictionary<WeakReference, KeyValuePair<string, string>> controlStates =

        new Dictionary<WeakReference, KeyValuePair<string, string>>();

 

    protected override bool GoToStateCore(Control control, FrameworkElement templateRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions)

    {

        bool ReturnValue = base.GoToStateCore(control, templateRoot, stateName, group, state, useTransitions);

 

        if (ReturnValue)

        {

            var existingPair = controlStates.SingleOrDefault(pair => pair.Key.IsAlive

                                                          && pair.Key.Target == control

                                                          && pair.Value.Key == group.Name);

            if (existingPair.Key != null)

            {

                controlStates.Remove(existingPair.Key);

            }

 

            controlStates.Add(new WeakReference(control), new KeyValuePair<string, string>(group.Name, stateName));

        }

        return ReturnValue;

    }

 

    public static string QueryState(Control controlToFindItsState)

    {

        var existingValues = controlStates.Where(pair => pair.Key.IsAlive

                                               && pair.Key.Target == controlToFindItsState);

 

        string ReturnValue = string.Empty;

        foreach (var existingValue in existingValues)

        {

            ReturnValue += existingValue.Value.Key + "." + existingValue.Value.Value + " ";

        }

        return ReturnValue;

    }

}

This code really isn’t interesting. All it does is store controls, state groups and last fired states in that state group. Than, it queries against that data and returns the current state in a string form.

There’s really nothing interesting about the implementation, just the fact that we’re getting some data, we’re storing it and later on query it.

 

Next, we’ll want to use this custom Visual State Manager with the Button, CheckBox and Expander control.

So, we’ll start by putting these on our form:

    <StackPanel x:Name="LayoutRoot" Background="White">

        <Button x:Name="myButton" />

        <CheckBox x:Name="myCheckBox" />

        <controls:Expander x:Name="myExpander" />

    </StackPanel>

In order to change the VSM manager, we need to edit the template for each of these controls.

We’ll open up Blend on this form.

image

We’ll right click on each of the controls –> “Edit Control Parts (Template)” –> Edit Copy –> Ok.

image

After getting local copies of the templates for all the controls, we’ll need to set the VisualStateManager.CustomVisualStateManager Attached property in their templates:

                           <vsm:VisualStateManager.CustomVisualStateManager>

                                <SL_RTM_VS:QueryableVisualStateManager />

                            </vsm:VisualStateManager.CustomVisualStateManager>

 

image

image

image

 

Here’s a method that uses the static method we defined on our custom visual state manager and updates their content:

       private void updateContentFromVSM(object sender, EventArgs e)

        {

            myButton.Content = QueryableVisualStateManager.QueryState(myButton);

            myCheckBox.Content = QueryableVisualStateManager.QueryState(myCheckBox);

            myExpander.Header = QueryableVisualStateManager.QueryState(myExpander);

        }

Let’s sign this method up for some events that may cause visual state changes:

        public SilverlightControl2536()

        {

            InitializeComponent();

            myButton.MouseEnter += new MouseEventHandler(updateContentFromVSM);

            myButton.MouseLeave += new MouseEventHandler(updateContentFromVSM);

            myButton.MouseLeftButtonDown += new MouseButtonEventHandler(updateContentFromVSM);

            myButton.MouseLeftButtonUp += new MouseButtonEventHandler(updateContentFromVSM);

            myCheckBox.MouseEnter += new MouseEventHandler(updateContentFromVSM);

            myCheckBox.MouseLeave += new MouseEventHandler(updateContentFromVSM);

            myCheckBox.MouseLeftButtonDown += new MouseButtonEventHandler(updateContentFromVSM);

            myCheckBox.MouseLeftButtonUp += new MouseButtonEventHandler(updateContentFromVSM);

            myCheckBox.Checked += new RoutedEventHandler(updateContentFromVSM);

            myCheckBox.Unchecked += new RoutedEventHandler(updateContentFromVSM);

            myExpander.MouseEnter += new MouseEventHandler(updateContentFromVSM);

            myExpander.MouseLeave += new MouseEventHandler(updateContentFromVSM);

            myExpander.MouseLeftButtonDown += new MouseButtonEventHandler(updateContentFromVSM);

            myExpander.MouseLeftButtonUp += new MouseButtonEventHandler(updateContentFromVSM);

            myExpander.Expanded += new RoutedEventHandler(updateContentFromVSM);

            myExpander.Collapsed += new RoutedEventHandler(updateContentFromVSM);

        }

Now, let’s run our app.

image

This is how the app looks like initially once we mouseover elements and cause them to fire their initial states.

Now, What happens if we mouseover the Button?

image

And Click it?

image

What happens if we mouse over the CheckBox?

image

And click the CheckBox?

image

And than Click the Expander?

image

All and all, we can see that through a Custom VisualStateManager we can store the state changes and query them at a later phase.

 

 

One last change – Getting the initial state

Through some sleight of hand I was able to hide the fact that our form look like so when it first load up:

image

If we want to have the controls initialized with their states, we need to make sure the Visual Tree (which setups the custom VSM) is up and running.
In order to do that, we’ll register for a one-time call to LayoutUpdated on each control and initialize state there.

myButton.LayoutUpdated += new EventHandler(myButton_LayoutUpdated);

void myButton_LayoutUpdated(object sender, EventArgs e)

{

    myButton.LayoutUpdated -= myButton_LayoutUpdated;

    myButton.Content = QueryableVisualStateManager.QueryState(myButton);

}

And now in our UI we can see the following initial state:

image

Now, let’s add this initialization step for our two other controls:

 

            myButton.LayoutUpdated += new EventHandler(myButton_LayoutUpdated);

            myCheckBox.LayoutUpdated += new EventHandler(myCheckBox_LayoutUpdated);

            myExpander.LayoutUpdated += new EventHandler(myExpander_LayoutUpdated);

 

void myButton_LayoutUpdated(object sender, EventArgs e)

{

    myButton.LayoutUpdated -= myButton_LayoutUpdated;

    myButton.Content = QueryableVisualStateManager.QueryState(myButton);

}

 

void myCheckBox_LayoutUpdated(object sender, EventArgs e)

{

    myCheckBox.LayoutUpdated -= myCheckBox_LayoutUpdated;

    myCheckBox.Content = QueryableVisualStateManager.QueryState(myCheckBox);

}

 

void myExpander_LayoutUpdated(object sender, EventArgs e)

{

    myExpander.LayoutUpdated -= myExpander_LayoutUpdated;

    myExpander.Content = QueryableVisualStateManager.QueryState(myExpander);

}

image

And you can get the source at: http://silverlight.net/blogs/justinangel/BlogStorage/SilverlightCustomVSM.zip 

 

 

-- Justin Angel
Microsoft Silverlight Toolkit Program Manager

Silverlight Toolkit: TreeView, TreeViewItem & HierarchalDataTemplate

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.

image2_thumb2

 

2. Add a reference to the Silverlight Controls assembly (Microsoft.Windows.Controls.dll) which can be downloaded at http://codeplex.com/Silverlight.

image10_thumb2   image_thumb3313

 

3. Look under "Custom Controls" In the Blend Asset Library.

image13_thumb2

 

4. Add a TreeView to the Page.

image

 

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:

image

 

We’ll right click on TreeView –> Add TreeViewItem.

image

And we can see that we indeed got a new TreeViewItem nested under or TreeView:

image

 

Next, we’ll go to the TreeViewItem’s properties and set Header to “Sally” and IsExpanded to true.

image 

 

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.

image

And we got another nested note inside the previous TreeViewItem:

image

 

We’ll edit it’s Header to “John” and we’ll keep IsExpanded to false, because it has no nested nodes.

image

 

We’ll keep it up until we get the following TreeView:

image

 

Let’s run our sample:

image

And we can start collapsing, expanding & Selecting TreeViewItems:

image

(In the above sample, Greg is collapsed, and John is selected)

 

And here’s the XAML Blend generated for us:

<controls:TreeView Height="200" Width="120">

    <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"/>

        </controls:TreeViewItem>

    </controls:TreeViewItem>

</controls:TreeView>

 

 

 

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:

image 

 

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.

image

 

And in a few second we’ll see this screen:

image

 

We’ll start off by changing the first TreeViewItem:

<controls:TreeViewItem IsExpanded="True" Header="Sally">

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 IsExpanded="True">

    <controls:TreeViewItem.Header>

        <TextBlock Text="Sally" />

    </controls:TreeViewItem.Header>

And our TreeView still looks exactly the same:

image

 

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>.

<controls:TreeViewItem.Header>

    <StackPanel Orientation="Horizontal">

        <TextBlock Text="Sally" />

    </StackPanel>

</controls:TreeViewItem.Header>

Now that we have a container in the Header, we’ll add the <Image> tag.

<controls:TreeViewItem.Header>

    <StackPanel Orientation="Horizontal">

        <Image Source="Alien1.png" />

        <TextBlock Text="Sally" />

    </StackPanel>

</controls:TreeViewItem.Header>

 

In our Visual Studio preview windows we can see the following image:

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">

 

image

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:

image

 

here’s our XAML up until now:

<controls:TreeView FontSize="22" Height="350" Width="250" Margin="84,101,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">

    <controls:TreeViewItem IsExpanded="True">

       <controls:TreeViewItem.Header>

            <StackPanel Orientation="Horizontal">

                <Image Source="Alien1.png" />

                <TextBlock Text="Sally" />

            </StackPanel>

        </controls:TreeViewItem.Header>

        <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"/>

        </controls:TreeViewItem>

    </controls:TreeViewItem>

</controls:TreeView>

 

We’ll repeat the process of editing an Image for the other 6 TreeViewItems. Than we’ll run our application.

image

 

here’s the XAML we wrote:

<controls:TreeView FontSize="22" Height="350" Width="250" Margin="84,101,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">

    <controls:TreeViewItem IsExpanded="True">

        <controls:TreeViewItem.Header>

            <StackPanel Orientation="Horizontal">

                <Image Source="Alien1.png" />

                <TextBlock Text="Sally" />

            </StackPanel>

        </controls:TreeViewItem.Header>

                <controls:TreeViewItem>

                    <controls:TreeViewItem.Header>

                        <StackPanel Orientation="Horizontal">

                            <Image Source="Alien2.png" />

                            <TextBlock Text="John" />

                        </StackPanel>

                    </controls:TreeViewItem.Header>

                </controls:TreeViewItem>

                <controls:TreeViewItem IsExpanded="True">

                    <controls:TreeViewItem.Header>

                        <StackPanel Orientation="Horizontal">

                            <Image Source="Alien3.png" />

                            <TextBlock Text="Greg" />

                        </StackPanel>

                    </controls:TreeViewItem.Header>

                    <controls:TreeViewItem>

                        <controls:TreeViewItem.Header>

                            <StackPanel Orientation="Horizontal">

                                <Image Source="Alien4.png" />

                                <TextBlock Text="Linda" />

                            </StackPanel>

                        </controls:TreeViewItem.Header>

                    </controls:TreeViewItem>

                    <controls:TreeViewItem>

                        <controls:TreeViewItem.Header>

                            <StackPanel Orientation="Horizontal">

                                <Image Source="Alien5.png" />

                                <TextBlock Text="Darren" />

                            </StackPanel>

                        </controls:TreeViewItem.Header>

                    </controls:TreeViewItem>

                    </controls:TreeViewItem>

        <controls:TreeViewItem IsExpanded="True">

                    <controls:TreeViewItem.Header>

                        <StackPanel Orientation="Horizontal">

                            <Image Source="Alien6.png" />

                            <TextBlock Text="Allice" />

                        </StackPanel>

                    </controls:TreeViewItem.Header>

                    <controls:TreeViewItem>

                        <controls:TreeViewItem.Header>

                            <StackPanel Orientation="Horizontal">

                                <Image Source="Alien7.png" />

                                <TextBlock Text="Neil" />

                            </StackPanel>

                        </controls:TreeViewItem.Header>

                    </controls:TreeViewItem>

        </controls:TreeViewItem>

    </controls:TreeViewItem>

</controls:TreeView>

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.

image

And clear all the Items from the TreeViewItem by selecting the first TreeViewItem and deleting it.

image

image

 

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.

image

 

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.

image

We’ll call our new Template AlienTemplate.

image

We can see that we have an empty DataTemplate:

image

 

First, we’ll Change the Grid to a Horizontal StackPanel.

Right Click Grid –> Change Layout Type –> StackPanel.

image image

 

Next we’ll add an Image control.

image

And we’ll want to DataBind it’s Source Property to Alien.Picture property.

Click Advanced Property options next to Source.

image

Select “Custom Expression”.

image

And put in “{Binding Picture}”.

image

 

Next we’ll add a TextBlock and Bind it’s Text property to “{Binding Name}”.

image --> image –>

image --> image

 

Let’s run this sample:

image

 

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">

        <StackPanel Orientation="Horizontal">

            <Image Source="{Binding Path=Picture}"/>

            <TextBlock Text="{Binding Path=Name}"/>

        </StackPanel>

    </DataTemplate>

</UserControl.Resources>

Here’s our Code-behind:

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"),

                                };

}

And our current 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; }

}

 

 

 

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 class Alien

    {

        public Alien(string name, string pictureUrl, params Alien[] children)

        {

            Name = name;

            Picture = new BitmapImage(new Uri(pictureUrl, UriKind.Relative));

           Children = children;

        }

 

        public string Name { get; set; }

        public BitmapImage Picture { get; set; }

       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:

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")

                            )

                          )

                        };

}

 

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:

<DataTemplate x:Key="AlienTemplate">

    <StackPanel Orientation="Horizontal">

        <Image Source="{Binding Path=Picture}"/>

        <TextBlock Text="{Binding Path=Name}"/>

    </StackPanel>

</DataTemplate>

Let’s change it to a HierarchicalDataTemplateL:

<controls:HierarchicalDataTemplate x:Key="AlienTemplate">

    <StackPanel Orientation="Horizontal">

        <Image Source="{Binding Path=Picture}"/>

        <TextBlock Text="{Binding Path=Name}"/>

    </StackPanel>

</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}">

    <StackPanel Orientation="Horizontal">

        <Image Source="{Binding Path=Picture}"/>

        <TextBlock Text="{Binding Path=Name}"/>

    </StackPanel>

</controls:HierarchicalDataTemplate>

 

Let’s run the sample:

image

 

Here’s our XAML code for the TreeView (hasn’t changed during this sample):

<controls:TreeView FontSize="22" Height="350" Width="250" x:Name="trvAliens" ItemTemplate="{StaticResource AlienTemplate}"/>

Here’s our updated ItemTemplate:

<UserControl.Resources>

    <controls:HierarchicalDataTemplate x:Key="AlienTemplate" ItemsSource="{Binding Children}">

        <StackPanel Orientation="Horizontal">

            <Image Source="{Binding Path=Picture}"/>

            <TextBlock Text="{Binding Path=Name}"/>

        </StackPanel>

    </controls:HierarchicalDataTemplate>

</UserControl.Resources>

Here’s our updated Alien class:

public class Alien

{

    public Alien(string name, string pictureUrl, params Alien[] children)

    {

        Name = name;

        Picture = new BitmapImage(new Uri(pictureUrl, UriKind.Relative));

        Children = children;

    }

 

    public string Name { get; set; }

    public BitmapImage Picture { get; set; }

    public Alien[] Children { get; set; }

}

And our updated Code-behind:

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")

                    )

                  )

                };

}

 

 

 

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:

image

 

So, first, we’ll add a big TextBlock saying “Selected Alien:” in big bold letter.

image

And another empty TextBlock called “txtSelectedAlienName” with the same font properties.

image

 

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:

image

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;

}

 

Let’s run our sample:

 image

And if we select “John”:

image

 

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:

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)

{

    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:

void trvAliens_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

{

    txtSelectedAlienName.Text = ((Alien)e.NewValue).Name;

}

 

is equivalent to this code:

void trvAliens_SelectedItemChanged(object sender,

                                    RoutedPropertyChangedEventArgs<object> e)

{

    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”.

image

And now with this change, SelectedValue isn’t Alien anymore, but the value of Alien.Name.

so, we can re-write our handler to:

void trvAliens_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

{

    txtSelectedAlienName.Text = trvAliens.SelectedValue.ToString();

}

 

And we get the exact same UI:

image

 

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:

void trvAliens_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)

{

    txtSelectedAlienName.Text = trvAliens.SelectedValue.ToString();

}

 

 

 

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:

 image  - Expanded Icon

image  - Collapsed Icon

We’d like to change those to:

image - Collapsed Icon

image  - Expanded Icon

 

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”.

image

And we’ll call the new Style “AlienItemStyle”.

image

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”.

image

 

Here’s what we see:

image

 

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.

image

 

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.

image

 

And here’s what we see:

image

 

Apparently, the TreeViewItem ExpanderButton has two visuals: “CheckedVisual” image  and “UncheckedVisual” image .

We’ll need to replace those with our new Visuals.

 

First, I’ll draw a whole ellipse.

image

And on top of it I’ll draw a rectangle.

image

I’ll select the Ellipse with the Rectangle. Right Click –> Combine –> Subtract.

image

And we’ll get this path:

image

I’ll repeat the process and we’ll also get this path:

image

 

Now, In XAML we’ll cut & paste the names of “CheckedVisual” and “UncheckedVisual” to these new elements.

image

becomes

image

 

Next, we’ll delete the old CheckVisual and UncheckedVisual and place our new ones into the correct position.

image

 

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.

image

Select the “UncheckedVisual” and set it’s opacity to 0.

image

 

Now if we run our sample:

image

 

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">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" Storyboard.TargetName="select" Storyboard.TargetProperty="Opacity" To=".75"/>

                                </Storyboard>

                            </vsm:VisualState>

                            <vsm:VisualState x:Name="SelectedInactive">

                                <Storyboard>

                                    <DoubleAnimation Duration="0" Storyboard.TargetName="inactive" Storyboard.TargetProperty="Opacity" To=".2"/>

                                </Storyboard>

                            </vsm:VisualState>

                        </vsm:VisualStateGroup>

                        <vsm:VisualStateGroup x:Name="HasItemsStates">

                            <vsm:VisualState x:Name="HasItems"/>

                            <vsm:VisualState x:Name="NoItems">

                                <Storyboard>