Page view counter

December 2008 - Posts

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