Monday, 27 April 2009

I’ve seen quite a few slideshow controls for Silverlight. Mostly they are either retemplated listboxes (buying an easy transition effect for free) or custom controls. I’d like to see how the DomainUpDown control can be used to do a slide show. This post also takes a more indepth look at TransitioningContentControl and along the way shows how to retemplate and inherit/extend other controls.

The DomainUpDown control allows you to visualize a ‘domain’, which can be anything. In our case, our domain will consist of UIElements. The TransitionContentControl is an experimental control that I haven’t featured on this blog yet. It is a ‘convenience’ control, with which I mean to say that the control just takes care of a very common goal in Silverlight development. In this case, it will react to changes in Content by transitioning old content out and transitioning new content in. I’ve talked briefly about the control here.

By using DomainUpDown (henceforth: DUD), we will get a nice navigation system for free (up/down arrows) and the option to show our domain in a cyclic way (in other words, after you reach the end, it will jump to the first item again). Also, we have the option to allow shortcuts into the domain, by having the user type in the control. We will disable that though.

I certainly do not like the default look of the control though, with its big up/down buttons. So, we have some work ahead of us.

I’ve started out with a new SL2 application, adding references to:

  • System.Windows.Controls.Toolkit
  • System.Windows.Controls.Toolkit.Input
  • System.Windows.Controls.Toolkit.Layout

These are part of the Silverlight Toolkit.

I’ve also added a folder called Images and put some images in there. Just making it easy on myself, I’ve hardcoded those images into the Items collection of my DUD. I do not want users to be able to type in the DUD, so I set ‘IsEditable’ to false. I know the images are 300*400, so I even hardcoded the width/height of the control. I end up with this Xaml:

<UserControl
  x:Class="BlogImageSlideshow.Page"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:inputToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit">
  <Grid
    x:Name="LayoutRoot"
    Background="White">
    <inputToolkit:DomainUpDown
      Height="300"
      Width="400"
      IsEditable="False">
      <Image
        Source="Images/3410783929_051d93bc86.jpg" />
      <Image
        Source="Images/3412190495_4099006101.jpg" />
      <Image
        Source="Images/3413898641_f975065242.jpg" />
      <Image
        Source="Images/3414953148_cec704b7b8.jpg" />
      <Image
        Source="Images/3415344995_730b5cc814.jpg" />
    </inputToolkit:DomainUpDown>
  </Grid>
</UserControl>

 

When we view the control though, we are in for a surprise:

image

The buttons are way to big, and our image is way too small. This is caused by the way those buttons are created (with paths, set to scale uniformly). In most situations that will be exactly what you would like, however, in our case it most definitely is not. We’ll want to retemplate our control and are going to do that the ‘easy’ way: through Blend.

Once in Blend, I choose to edit the template of the DUD, which creates a style for me. The Object tab reveals the following parts of DUD:

image

To fix the ugly buttons, let’s repeat the process and retemplate the Spinner (which is a ButtonSpinner). That ButtonSpinner is responsible for hosting our content (in the image that is a Grid with two children: ‘Visualization’ and ‘Text’. It is also responsible for displaying the two buttons.

Editing the Spinner reveals these objects:

image

The unnamed [Button] is used to ‘catch’ the mouseclicks that are done on a disabled button. We don’t need it, so I’ll remove it. Also, I don’t want the buttons on the right side, but I want the previous button on the left and the next button on the right. I actually have a great idea: I’ll overlay the buttons and make them transparent. That way, pressing on the right side of the image will move the slideshow forwards and pressing on the left side will make it go backwards.

I also retemplated the buttons: removing the usual gradient, but having some overlay on ‘mouseOver’ to indicate that we can press. Since I’m no designer, I just used a one white rectangle with an opacity of 0 and a linear gradient opacitymask. On MouseOver, I will animate the opacity to 30%. The effect is a gradual whitening at the border of the image.

This is the style for one of the buttons:

<Style x:Key="RepeatButtonStyle1" TargetType="RepeatButton">
    <Setter Property="Background" Value="#FF1F3B53"/>
    <Setter Property="Foreground" Value="#FF000000"/>
    <Setter Property="Padding" Value="3"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="BorderBrush">
        <Setter.Value>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFA3AEB9" Offset="0"/>
                <GradientStop Color="#FF8399A9" Offset="0.375"/>
                <GradientStop Color="#FF718597" Offset="0.375"/>
                <GradientStop Color="#FF617584" Offset="1"/>
            </LinearGradientBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="RepeatButton">
                <Grid x:Name="Root">
                    <vsm:VisualStateManager.VisualStateGroups>
                        <vsm:VisualStateGroup x:Name="CommonStates">
                            <vsm:VisualState x:Name="Normal"/>
                            <vsm:VisualState x:Name="MouseOver">
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(UIElement.Opacity)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="0.3"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </vsm:VisualState>
                            <vsm:VisualState x:Name="Pressed">
                                <Storyboard/>
                            </vsm:VisualState>
                            <vsm:VisualState x:Name="Disabled">
                                <Storyboard/>
                            </vsm:VisualState>
                        </vsm:VisualStateGroup>
                        <vsm:VisualStateGroup x:Name="FocusStates">
                            <vsm:VisualState x:Name="Focused">
                                <Storyboard/>
                            </vsm:VisualState>
                            <vsm:VisualState x:Name="Unfocused"/>
                        </vsm:VisualStateGroup>
                    </vsm:VisualStateManager.VisualStateGroups>
                    <Rectangle Height="Auto" Margin="0,0,0,0" VerticalAlignment="Stretch" Stroke="#FF000000" Opacity="0" x:Name="rectangle" Fill="#FFFFFFFF">
                        <Rectangle.OpacityMask>
                            <LinearGradientBrush EndPoint="-0.015,0.493" StartPoint="0.99,0.497">
                                <GradientStop Color="#00000000"/>
                                <GradientStop Color="#FFFFFFFF" Offset="1"/>
                            </LinearGradientBrush>
                        </Rectangle.OpacityMask>
                    </Rectangle>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
And now for the fun stuff: Animating it!

Currently the status of the project is that I am able to quickly show a bunch of images and I can mouse over the image and click to show the next/previous image. We’re now going to dive into the TransitioningContentControl and use it to create a nice sliding effect.

First some background: in Silverlight and in WPF, you use a ContentControl to visualize any type of content you might have. When setting a new content, the ContentControl will just replace the current visual with a new visual (representing your new content). It uses a ContentTemplate to determine how to visualize your content.
The only feature that TransitioningContentControl adds to this game is an easy way to start an animation to visualize the actual replacing of content. Although there are some technical difficulties (more on that later), it is meant as a drop-in replacement for ContentControl.

The DomainUpDown also uses a ContentControl to show your content. The templatepart is called ‘Visualization’ and we are going to replace that with a TransitioningContentControl. All I did was replace the ContentControl tag with TransitioningContentControl in the template for DomainUpDown.

Now, here comes the catch: because an element can’t be in the visual tree twice (at the same time) in SL2 this creates a bit of a problem when using the TCC (TransitioningContentControl) with FrameworkElements. In SL3 I will solve this quite easily by creating an image out of the content and using that to ‘transition out’ but in SL2 the only easy work-around is to not use elements with this control. That is actually quite easy, because we can use the ContentTemplate to create our elements on the fly. I’ve changed the way I use DUD as follow:

<inputToolkit:DomainUpDown
    Height="300" Width="400"
    IsEditable="False" Style="{StaticResource SlideShowStyle}">
<inputToolkit:DomainUpDown.ItemTemplate>
  <DataTemplate>
    <Image Source="{Binding}" />
  </DataTemplate>
</inputToolkit:DomainUpDown.ItemTemplate>
<System:String>Images/3410783929_051d93bc86.jpg</System:String>
<System:String>Images/3412190495_4099006101.jpg</System:String>
<System:String>Images/3413898641_f975065242.jpg</System:String>
<System:String>Images/3414953148_cec704b7b8.jpg</System:String>
<System:String>Images/3415344995_730b5cc814.jpg</System:String>
</inputToolkit:DomainUpDown>

Because the TCC actually has a nice default transition, we could call it a day: there is a nice fade-in/fade-out animation going on. I like it!
However, let’s see if we can actually create a slideshow.

To start off, I went ahead and edited the template for TransitioningContentControl. In Blend3 there seems to be a bug that not the complete template is copied (making it rather useless). Blend2 works fine though.
The inevitable object screenshot shows
image

two ‘presentationSites’. Which enables you to see through my trickery: the only thing I do when content is being set, is copy the content inside ‘CurrentContentPresentation’ to ‘PreviousContentPresentation’ and start a storyboard. These are the storyboards that come out of the box:

image

Now, I fully expect you to create / add your own transitions here. To do that, you would go into xaml and add an empty storyboard. At that point you can use the designer again to style it. However, we’ll be fine by restyling the UpTransition and DownTransition.

Currently they do a nice fade and rendertransform. That doesn’t work for me though, so let’s start with the UpTransition, I’d like to have the PreviousContentPresentationSite move to the left and have the Current come in from the right.
That’s pretty easy, I just animated two rendertransforms to do that, not forgetting to set a nice keyspline for the smooth effect.

Next assignment: we’ll need to let TCC know which transition to use. It has no knowledge of the concept of up or down or whatever transitions you came up with.
To be able to tell TCC which transition to play, I expose a DependencyProperty of type string. That indicates the exact name of the transition that should be used the next time the content is changed.

There are many ways to do this. You could hook into the buttons and set the Transition manually. I believe the easiest way though, is to inherit from DomainUpDown and add the functionality yourself:

public class SlidingDomainUpDown : DomainUpDown
{
    private TransitioningContentControl _visualizer;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        _visualizer = GetTemplateChild("Visualization") as TransitioningContentControl;
    }

    protected override void OnDecrement()
    {
        if (_visualizer != null)
        {
            _visualizer.Transition = "DownTransition";
        }
        base.OnDecrement();
    }

    protected override void OnIncrement()
    {

        if (_visualizer != null)
        {
            _visualizer.Transition = "UpTransition";
        }

        base.OnIncrement();
    }
}

 

Interestingly enough, I get asked how to get to templateparts all the time, on the silverlight.net forums. Well, here you go: the simplest possible example there is. I hope this is clear!

At this point, everything works fine for me. Except that the effect is horrible! You see the complete image moving, but you would expect to only see it inside of the frame. What we need is some clipping.
I’m no Blend hero, but I pulled it off by creating a rectangle and right-clicking, choosing Path/make clipping path. Then I chose the TCC element, called ‘Visualization’ to be the clipping target. It ended up creating this xaml:

Clip="M0.5,0.5 L395.5,0.5 L395.5,295.5 L0.5,295.5 z"

We’re now at a point that I’m really happy about! The user has some feedback when he mouses over the image and the effect is quite nice. The user can click too fast though (while the transition is in motion) and I’d like to disable that:

protected override void OnValueChanging(RoutedPropertyChangingEventArgs<object> e)
{
    base.OnValueChanging(e);

    if (_visualizer != null && _visualizer.IsTransitioning)
    {
        e.Cancel = true;
    }
}

It would have been nicer to disable the buttons, but this will do for now.

Taking the extra step: adding direct shortcuts

This is becoming a huge post, but I’m having too much fun.
You have seen slideshows that have little indicators on the buttom that allow you to directly navigate to an image. I’m thinking of the Hulu.com example:

image

Let’s create those!

I started by adding a ListBox to the template of our SlidingDomainUpDown control. I cheated a bit and hardlinked the Items collection to the ItemsSource collection of that ListBox.
Then I went on to restyle the ListBoxItems to show a small preview of our item. Then I hooked up the ListBox.SelectionChanged event. When the selecteditem changes, I just set the Value of the DomainUpDown. Obviously, this could have been more elegantly done by creating a binding between the Value property and the SelectedItem property of the ListBox.  (oh well, it’s my blog! :) )
I also cheated because I did not spend the time looking at the key handling. The listbox actually takes care of some of the keyhandling, and I don’t like it.

I end up with this:

image

I set up the transition to be the DefaultTransition (which is a nice fade in/fade out). So, now the user can slide or go directly to an image. Loving it!

Try it out here and grab the solution here.

Monday, 27 April 2009 17:28:32 (Romance Standard Time, UTC+01:00)  #    Comments [60]  |  Trackback
 Saturday, 11 April 2009

I believe this is going to be the last post in this series, although I bet I will have some loose Accordion posts coming up. This part is going to focus on AccordionButton, and show you how to retemplate it.

Previous posts:
Part 1 – accordion
Part 2 – accordion item
Part 3 – expandable content control
Part 4 – retemplating, real world example

We’ve already hit on a lot of the internals of AccordionItem. Expandable content control was used to easily animate the size of the content. When looking at the header though, it gets complicated all over again.

This is the important part of AccordionItem, defining both a place for the content and a place for the header:

<Border x:Name="Background" 
        Padding="{TemplateBinding Padding}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}" 
        CornerRadius="1,1,1,1">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" x:Name="rd0"/>
            <RowDefinition Height="Auto" x:Name="rd1"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" x:Name="cd0"/>
            <ColumnDefinition Width="Auto" x:Name="cd1"/>
        </Grid.ColumnDefinitions>

        <layoutPrimitivesToolkit:AccordionButton
                    x:Name="ExpanderButton"
          Style="{TemplateBinding AccordionButtonStyle}"
                    Content="{TemplateBinding Header}"
                    ContentTemplate="{TemplateBinding HeaderTemplate}"
                    IsChecked="{TemplateBinding IsSelected}"
                    IsTabStop="True"
                    Grid.Row="0"
                    Padding="0,0,0,0"
                    Margin="0,0,0,0"
                    FontFamily="{TemplateBinding FontFamily}"
                    FontSize="{TemplateBinding FontSize}"
                    FontStretch="{TemplateBinding FontStretch}"
                    FontStyle="{TemplateBinding FontStyle}"
                    FontWeight="{TemplateBinding FontWeight}"
                    Foreground="{TemplateBinding Foreground}"
                    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" 
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch" 
          HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
          Background="{TemplateBinding Background}" />

        <layoutPrimitivesToolkit:ExpandableContentControl
                    x:Name="ExpandSite"
                    Grid.Row="1"
                    IsTabStop="False"
                    Percentage="0"
                    RevealMode="{TemplateBinding ExpandDirection}"
                    Content="{TemplateBinding Content}"
                    ContentTemplate="{TemplateBinding ContentTemplate}"
                    Margin="0,0,0,0"
                    FontFamily="{TemplateBinding FontFamily}"
                    FontSize="{TemplateBinding FontSize}"
                    FontStretch="{TemplateBinding FontStretch}"
                    FontStyle="{TemplateBinding FontStyle}"
                    FontWeight="{TemplateBinding FontWeight}"
                    Foreground="{TemplateBinding Foreground}"
                    HorizontalContentAlignment="Left"
                    VerticalContentAlignment="Top" 
                    HorizontalAlignment="Stretch"
                    VerticalAlignment="Stretch"/>
    </Grid>
</Border>


As you can see, both ExpandableContentControl and AccordionButton are placed in the primitives namespace. More importantly, they are placed in a grid. As I’ve shown in previous posts, that is how we react to the ExpandDirection setting on accordion (so placing the content in the first column and the button in the second would correspond to the ExpandDirection ‘right’).

AccordionButton is an extremely simple class. It inherits from ToggleButton, and its whole purpose in life is to be able to react (once again) to different ExpandDirections. So, if you were to open up the code of AccordionButton, you would see that all it does is this:

switch (ParentAccordionItem.ExpandDirection)
{
    case ExpandDirection.Down:
        VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandDown);
        break;

    case ExpandDirection.Up:
        VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandUp);
        break;

    case ExpandDirection.Left:
        VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandLeft);
        break;

    default:
        VisualStates.GoToState(this, useTransitions, VisualStates.StateExpandRight);
        break;
}

Ofcourse, the template of AccordionButton is quite big. This is the reason I introduced this class, just to simplify the template (considerably).

It is time to look at the important part of AccordionButtons template:

<Border x:Name="background" Background="{TemplateBinding Background}" CornerRadius="1,1,1,1">
    <Grid>
        <Border Height="Auto" Margin="0,0,0,0" x:Name="ExpandedBackground" VerticalAlignment="Stretch" Opacity="0" Background="#FFBADDE9" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1,1,1,1"/>
        <Border Height="Auto" Margin="0,0,0,0" x:Name="MouseOverBackground" VerticalAlignment="Stretch" Opacity="0" Background="#FFBDBDBD" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="1,1,1,1"/>
        <Grid Background="Transparent">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" x:Name="cd0"/>
                <ColumnDefinition Width="Auto" x:Name="cd1"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" x:Name="rd0"/>
                <RowDefinition Height="Auto" x:Name="rd1"/>
            </Grid.RowDefinitions>
            <Grid Height="19" HorizontalAlignment="Center" x:Name="icon" VerticalAlignment="Center" Width="19" RenderTransformOrigin="0.5,0.5" Grid.Column="0" Grid.Row="0">
                <Grid.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform Angle="-90"/>
                        <TranslateTransform/>
                    </TransformGroup>
                </Grid.RenderTransform>
                <Path 
                    Height="Auto" 
                    HorizontalAlignment="Center" 
                    Margin="0,0,0,0" x:Name="arrow" 
                    VerticalAlignment="Center" 
                    Width="Auto" 
                    RenderTransformOrigin="0.5,0.5" 
                    Stroke="#666" 
                    StrokeThickness="2" 
                    Data="M 1,1.5 L 4.5,5 L 8,1.5">
                    <Path.RenderTransform>
                        <TransformGroup>
                            <ScaleTransform/>
                            <SkewTransform/>
                            <RotateTransform/>
                            <TranslateTransform/>
                        </TransformGroup>
                    </Path.RenderTransform>
                </Path>
            </Grid>
            <layoutToolkit:LayoutTransformer
                FontFamily="{TemplateBinding FontFamily}" 
                FontSize="{TemplateBinding FontSize}" 
                FontStretch="{TemplateBinding FontStretch}" 
                FontStyle="{TemplateBinding FontStyle}" 
                FontWeight="{TemplateBinding FontWeight}" 
                Foreground="{TemplateBinding Foreground}" 
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                Margin="6,6,6,0" 
                x:Name="header" 
                Grid.Column="0"
                Grid.Row="0" 
                Grid.RowSpan="1" 
                Content="{TemplateBinding Content}" 
                ContentTemplate="{TemplateBinding ContentTemplate}"/>
        </Grid>
    </Grid>
</Border>

There are two parts here. We have a grid (named ’icon’) and a LayoutTransformer. Again, we see the old trick of a grid with 2 columns and 2 rows: it is used to place the icon relative to the header.

Layouttransformer: it is a class that my teammate Delay has developed and was able to ship with the toolkit. He has a whole host of blog posts about it, and his latest may be of most interest to you: it shows how to animate the layoutTransformer.
LayoutTransformer is a control that allows me to rotate the header and keeping the layout engine happy. In other words, I rotate the header 90 degrees, and the width and height of the header is correct. That would not be the case if I had used RenderTransform.
I will not focus on this class any more, please checkout Delays posts on it!

By now, you should have enough understanding of what is going on to be able to retemplate AccordionButton:

  1. It holds both the Header and an icon
  2. It reacts to expanddirection
  3. It re-arranges the location of the button and header accordingly

Actually, it also rotates the icon. So when you are in ExpandDirection ‘right’, the arrow in the button will still point towards the content, even though its location is completely different from ExpandDirection ‘left’.

Templating the AccordionButton

We have hidden AccordionButton from the designer surface. We did that because we feel that, as a primitive, there is no value in having AccordionButton clutter up the design toolbox. However, in order to template it, it is easiest to use blend with only an AccordionButton there.. So I used this Xaml in Blend:

<UserControl x:Class="AccordionBlogSamplesSL2.HorizontalImages"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit" 
    xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" xmlns:System_Windows_Controls_Primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Layout.Toolkit" 
    >
  <Grid x:Name="LayoutRoot" Background="White">
      <System_Windows_Controls_Primitives:AccordionButton />
  </Grid>
</UserControl>

That gives me a nice design surface with an AccordionButton on it. When I attempt to ‘Edit template’, Blend will create the style and shows the objects inside of AccordionButton:

image

Not only that, it also shows me these states:

image

When pressing any of the ExpandDirectionStates, you will notice the icon being rotated and moved to different gridcells. It is easiest to remain in the Base state to do your work.

I’m not a terribly good designer (one would argue I am in fact, a terribly bad designer), so I would even attempt to make this look good. I will just change the template slightly so I have something to show you!

The design surface looks like this:

image

I will change the path (called arrow) to this:

image

I’ve also changed the MouseOverBackground to something random and the expanded background.
Quite a few people have asked me how to change those properties, well, here they are. They are part of the AccordionButton template.

Bringing it all together

AccordionItem luckily exposes an AccordionButtonStyle, so in order to restyle our AccordionItems to accept this AccordionButtonStyle, we simply apply it.

<layoutToolkit:Accordion>
    <layoutToolkit:AccordionItem Content="AccordionItem" AccordionButtonStyle="{StaticResource AccordionButtonStyle1}"/>
    <layoutToolkit:AccordionItem Content="AccordionItem" AccordionButtonStyle="{StaticResource AccordionButtonStyle1}"/>
</layoutToolkit:Accordion>

As I’ve been getting some questions on my blog about styles and templates, I will also show you how to do this with an AccordionItemContainerStyle:

<layoutToolkit:Accordion>
<layoutToolkit:Accordion.ItemContainerStyle>
    <Style TargetType="layoutToolkit:AccordionItem">
     <Setter Property="AccordionButtonStyle" Value="{StaticResource AccordionButtonStyle1}" />
    </Style>
</layoutToolkit:Accordion.ItemContainerStyle>
    <layoutToolkit:AccordionItem Content="AccordionItem" />
    <layoutToolkit:AccordionItem Content="AccordionItem" />
</layoutToolkit:Accordion>

(The latter approach is best, imho)

We end up with:

image

I hope that helps!

Saturday, 11 April 2009 21:42:21 (Romance Standard Time, UTC+01:00)  #    Comments [155]  |  Trackback
 Monday, 06 April 2009

Mehdi pointed me to a great accordion on this site. It is a horizontal accordion that only uses images. In this post I’ll walk us through creating something similar.

This is what the accordion looks like:image

There is a snazzy effect where the headline pops in from below when you open the item. Go look at it and play a bit, it’s pretty cool!

I started out by adding references to the highlighted 3 dll’s. All 3 are necessary!

image

Then I created my accordion and gave it some fixed size accordion items. I used this Xaml:

    <layoutToolkit:Accordion ExpandDirection="Right" Margin="100">
      <layoutToolkit:AccordionItem>
        <Rectangle Width="150" Height="80" Fill="Blue" />
      </layoutToolkit:AccordionItem>
      <layoutToolkit:AccordionItem>
        <Rectangle Width="150" Height="80" Fill="Red" />
      </layoutToolkit:AccordionItem>
      <layoutToolkit:AccordionItem>
        <Rectangle Width="150" Height="80" Fill="Green" />
      </layoutToolkit:AccordionItem>
      <layoutToolkit:AccordionItem>
        <Rectangle Width="150" Height="80" Fill="Yellow" />
      </layoutToolkit:AccordionItem>
      <layoutToolkit:AccordionItem>
        <Rectangle Width="150" Height="80" Fill="PowderBlue" />
      </layoutToolkit:AccordionItem>
    </layoutToolkit:Accordion>

As you can see, I set the ExpandDirection to “Right”, so the accordion opens up in the same way as our sample. I have not set a specific Width on the Accordion, which means that the Accordion will only take what it needs.

image

I get a lot of questions on how the Width of the Accordion determines sizing on AccordionItems. You need to remember that the Items will always divide the space equally they get. In our example, setting a fixed Width of 500 on the Accordion would have yielded this result:

image

Now, our AccordionItems exist always of a header and content. In our sample though, we do not see a header at all. So, we will need to retemplate AccordionItem in order to remove that header.
There is a headerTemplate property on Accordion, but keep in mind that that will allow you to determine the look of what goes into the header! In our case, we will completely remove the header from AccordionItem. So, I went into Blend and selected the first AccordionItem and opened up its template:

image

This created a styleResource that is being applied to the first item.
In the template, I removed every element that I don’t care about. The AccordionButton is the little arrow that moves. Leaving me with this:

image

However, we do actually need a header, since it will be the element a user can click to navigate the accordion. So, let’s look at the Grid that hosts ExpandSite:

<Grid>
<Grid.RowDefinitions>
    <RowDefinition Height="Auto" x:Name="rd0"/>
    <RowDefinition Height="Auto" x:Name="rd1"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" x:Name="cd0"/>
    <ColumnDefinition Width="Auto" x:Name="cd1"/>
</Grid.ColumnDefinitions>
<System_Windows_Controls_Primitives:ExpandableContentControl 
        HorizontalAlignment="Stretch" 
        Margin="0,0,0,0" 
        x:Name="ExpandSite" 
        VerticalAlignment="Stretch" 
        Grid.Row="1" 
        FontFamily="{TemplateBinding FontFamily}" 
        FontSize="{TemplateBinding FontSize}" 
        FontStretch="{TemplateBinding FontStretch}" 
        FontStyle="{TemplateBinding FontStyle}" 
        FontWeight="{TemplateBinding FontWeight}" 
        Foreground="{TemplateBinding Foreground}" 
        HorizontalContentAlignment="Left" 
        IsTabStop="False" 
        VerticalContentAlignment="Top" 
        Content="{TemplateBinding Content}" 
        ContentTemplate="{TemplateBinding ContentTemplate}" 
        Percentage="0" 
        RevealMode="{TemplateBinding ExpandDirection}"/>
</Grid>

You can see that the grid has 2 rows and 2 columns. This is all that is needed to position the header and content in all the possible expandDirections. Just for fun, I’ll show you the VisualState “ExpandedRight”:

<vsm:VisualState x:Name="ExpandRight">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.ColumnSpan)">
            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.RowSpan)">
            <DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.Row)">
            <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.Column)">
            <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="rd0" Storyboard.TargetProperty="Height">
            <DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="cd1" Storyboard.TargetProperty="Width">
            <DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</vsm:VisualState>

As you can see, it is happily placing items in the correct location and setting heights and widths on the grid cells. Since we do want the header to have a width, I’ll give the first column (where our header would have been) a minWidth. I will also remove all the Expand visualstates since I don’t need those and they only serve to bloat the Xaml at this point.

Since we removed AccordionButton, we have nothing to press and open the Item. But that’s not what we want anyway, we want to open items as we mouse over them. So I’ve introduced an eventhandler for the MouseEnter event on AccordionItem:

void item_MouseEnter(object sender, MouseEventArgs e)
{
    AccordionItem item = (AccordionItem) sender;
    item.IsSelected = true;
}

Now we’re getting somewhere! The items behave quite similar to our sample. Still need to figure out the header and content parts though. The ExpandSite will always contract to 0 width when it is collapsed (not selected). That can’t be what we want.

We removed the header because we did not want it, and want a part of the content to be visible. There are two animations that govern the expand and collapse movements. They are here:

<vsm:VisualStateGroup x:Name="ExpansionStates">
    <vsm:VisualStateGroup.Transitions>
        <vsm:VisualTransition GeneratedDuration="0"/>
    </vsm:VisualStateGroup.Transitions>
    <vsm:VisualState x:Name="Collapsed">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(ExpandableContentControl.Percentage)">
                <SplineDoubleKeyFrame KeySpline="0.2,0,0,1" KeyTime="00:00:00.3" Value="0"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </vsm:VisualState>
    <vsm:VisualState x:Name="Expanded">
        <Storyboard>
            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(ExpandableContentControl.Percentage)">
                <SplineDoubleKeyFrame KeySpline="0.2,0,0,1" KeyTime="00:00:00.3" Value="1"/>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </vsm:VisualState>
</vsm:VisualStateGroup>

Let me explain what is going on there. An AccordionItem can be in either Collapsed or Expanded state. I have no way of determining how big an item should be when it is expanded. Remember, it is the accordion that calculates such things at runtime (given the amount of items, and the width of the accordion itself). When the accordion sets a ‘targetsize’, we also calculate a percentage. When we are collapsed, that percentage is 0, when we are fully expanded, that percentage is 1.

The storyboards that you can see here are being started after the AccordionItem has been collapsed or expanded. In other words, they actually do the revealing or hiding of the content. The only thing they have to animate is the percentage, and this way I allow designers to come in and create different kind of animations (changing the keyspline for instance).

The important value here is 0, in the Collapsed storyboard. Turns out we don’t want it to collapse to 0, but to something like 0.2! If we change that storyboard, the item will still be partially visible, even though the accordion feels it is completely collapsed.

image

That was easy. So, let’s start working on some cool headline action.
I’d like to have a headline come in from below as we expand.

So, let’s create a ContentControl to display our header:

                        <Grid>
                            <System_Windows_Controls_Primitives:ExpandableContentControl 
                                HorizontalAlignment="Stretch" 
                                Margin="0,0,0,0" 
                                x:Name="ExpandSite" 
                                VerticalAlignment="Stretch" 
                                HorizontalContentAlignment="Left" 
                                IsTabStop="False" 
                                VerticalContentAlignment="Top" 
                                Content="{TemplateBinding Content}" 
                                ContentTemplate="{TemplateBinding ContentTemplate}" 
                                Percentage="0" 
                                RevealMode="{TemplateBinding ExpandDirection}"/>
                            <ContentControl x:Name="header" 
                                Content="{TemplateBinding Header}" 
                          VerticalAlignment="Bottom" 
                          Visibility="Collapsed" RenderTransformOrigin="0.5,0.5" >
                                <ContentControl.RenderTransform>
                                    <TransformGroup>
                                        <ScaleTransform/>
                                        <SkewTransform/>
                                        <RotateTransform/>
                                        <TranslateTransform/>
                                    </TransformGroup>
                                </ContentControl.RenderTransform>
                            </ContentControl>
                        </Grid>
                    </Border>
                </Grid>

And have Blend generate some nice animation effects:

<vsm:VisualState x:Name="Expanded">
<Storyboard>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(ExpandableContentControl.Percentage)">
        <SplineDoubleKeyFrame KeySpline="0.2,0,0,1" KeyTime="00:00:00.3" Value="1"/>
    </DoubleAnimationUsingKeyFrames>
    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="header" Storyboard.TargetProperty="(UIElement.Visibility)">
        <DiscreteObjectKeyFrame KeyTime="00:00:00.2000000">
            <DiscreteObjectKeyFrame.Value>
                <Visibility>Visible</Visibility>
            </DiscreteObjectKeyFrame.Value>
        </DiscreteObjectKeyFrame>
    </ObjectAnimationUsingKeyFrames>
    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="header" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
        <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" Value="60"/>
        <SplineDoubleKeyFrame KeyTime="00:00:00.4000000" Value="0" KeySpline="0,0,0.46900001168251,1"/>
    </DoubleAnimationUsingKeyFrames>
</Storyboard>

You can see that the header starts out with Visibility=Collapsed, this way it won’t take up any space and mess up the AccordionItem.
When the item expands, that visibility is toggled and a rendertransform is used to bring it into view.

Let’s finish it off with some images, to make our accordion look nice! I grabbed some images and created a somewhat better header:

  <layoutToolkit:AccordionItem Style="{StaticResource AccordionItemStyle1}" MouseEnter="item_MouseEnter">
  <layoutToolkit:AccordionItem.Header>
    <Border Background="#aa000000" Width="400" Height="80">
      <StackPanel Margin="10">
        <TextBlock FontFamily="Verdana" FontSize="20" Foreground="White">Seamonster</TextBlock> 
        <TextBlock FontFamily="Verdana" FontSize="12" Foreground="White">by Proudlove</TextBlock> 
      </StackPanel>
    </Border>
  </layoutToolkit:AccordionItem.Header>
  <Image Source="/Images/3410783929_051d93bc86.jpg"  />
</layoutToolkit:AccordionItem>

I also changed the animations slightly, to be somewhat slower. The result looks like this:

image

image

I hope this little walkthrough helped.

See the live sample here, and download the project here.
Let me know what you think!

Monday, 06 April 2009 04:37:46 (Romance Standard Time, UTC+01:00)  #    Comments [52]  |  Trackback
 Sunday, 29 March 2009

We continue our exploration of Accordion. This time we’ll take a look at some of the template parts, namely ExpandableContentControl. See part 1 and part 2 if you didn’t yet.

ExpandableContentControl

This control is part of AccordionItem and is used to display the content of an accordionItem. It sits in the Primitives namespace, which means it will be used primarily as a part of a template. It solves an important problem: being able to resize (expand/contract) a piece of content, without triggering resize actions on the content itself. With that I mean: if your AccordionItem would use a Grid or a DockPanel as it’s content and I would just be animating the width or the height, you would see the panel changing as it tries to keep up with the new sizes. Ofcourse, that would not be the case if you had set an explicit width or height to the panel, but AccordionItem does not force you to do so.So, during the resize of our AccordionItem, I don’t want the content to have to resize.

Furthermore, it would be nice to make it really easy for you to determine how the animation should look like. Do you want a gradual easein/out, a bump or something different altogether? I want to be able to design my animation in Blend, using the nifty keyspline features!

I accomplish the above goals by throwing in a ScrollViewer and defining a DependencyProperty: Percentage.

ScrollViewer allows Accordion to just determine the size the item will eventually be (after expand action) and set it only once. At that moment the content of the scrollviewer is going to resize itself accordingly. Then, as we animate the width or height of the scrollviewer, its content is none-the-wiser. Hence, no ugly resize actions going on.

Making the actual animation be easily defined inside of Blend, is done by the above-mentioned Percentage DP.
Accordion only determines the size the AccordionItem will eventually be, I call it the TargetSize. ExpandableContentControl will calculate and set its actual size using to the following simple function: actualsize = percentage * TargetSize.

Accordion also triggers a visualstate called either Expanded or Contracted on AccordionItem. Those animate the percentage property on ExpandableContentControl. See this Xaml in the template of AccordionItem:

   62 <vsm:VisualState x:Name="Collapsed">

   63     <Storyboard>

   64         <DoubleAnimationUsingKeyFrames

   65             BeginTime="00:00:00"

   66             Storyboard.TargetName="ExpandSite"

   67             Storyboard.TargetProperty="(ExpandableContentControl.Percentage)">

   68             <SplineDoubleKeyFrame

   69                 KeyTime="00:00:00.3"

   70                 KeySpline="0.2,0,0,1"

   71                 Value="0" />

   72         </DoubleAnimationUsingKeyFrames>

   73     </Storyboard>

   74 </vsm:VisualState>

   75 <vsm:VisualState x:Name="Expanded">

   76     <Storyboard>

   77         <DoubleAnimationUsingKeyFrames

   78             BeginTime="00:00:00"

   79             Storyboard.TargetName="ExpandSite"

   80             Storyboard.TargetProperty="(ExpandableContentControl.Percentage)">

   81             <SplineDoubleKeyFrame

   82                 KeyTime="00:00:00.3"

   83                 KeySpline="0.2,0,0,1"

   84                 Value="1" />

   85         </DoubleAnimationUsingKeyFrames>

   86     </Storyboard>

   87 </vsm:VisualState>

This makes it possible to design your animation at the correct level (AccordionItem) and makes it a very design friendly experience, because Blend allows you to edit keysplines as follows:

image

(SL3 is adding a nice collection of other spline types that makes elastic AccordionItems even easier!)

I’ll discuss the remaining points of interest.

RevealMode

Since I expose one property (Percentage) for you to animate, the control needs to know whether that responds to the vertical or horizontal direction. Thus, AccordionItem will set the correct RevealMode, corresponding to its own ExpandDirection.

TargetSize

Already mentioned, setting the TargetSize will set the size of the ScrollViewer. It will also recalculate the percentage. Let’s say I have an item that has a height of 100. Now another item is collapsed, so the Accordion decides the remaining items should have a height of 160. The percentage that was once at 1 is recalculated to be 0.625.
This way we don’t have to restart the full animation, but we can just resize nicely.


I hope that helps understanding this vital piece of the template. In the future, I will either remove it (if the outlined problems can be solved more gracefully in another way) or expose the style of ExpandableContentControl in AccordionItem, so it can be more easily styled.

Sunday, 29 March 2009 01:01:18 (Romance Standard Time, UTC+01:00)  #    Comments [63]  |  Trackback
 Thursday, 26 March 2009

I’m glad to see accordion getting the excitement that it’s getting. I’ve gotten great feedback, keep it coming!

Part 1 was concerned with Accordion itself, we will now focus on the individual parts of Accordion.

AccordionItem

AccordionItem is to Accordion as is ListboxItem to Listbox. Accordion will only work with AccordionItems, and that is why, if you feed Accordion an item that is not of type AccordionItem, it will wrap it inside of one.

AccordionItem has several important jobs:

  • It needs to display a header and content.
  • It needs to be able to ‘open’ and ‘close’
  • It needs to be able to work in several ExpandDirections (Left, Right, Top, Bottom)

AccordionItem mimicks Expander in many ways (although I changed the template considerably) but inherits from HeaderedContentControl. Therefore, it has two important properties it gains from HeaderedContentControl: HeaderTemplate and ContentTemplate.
These templates are used to allow you to customize the header and content.

I will use KeyValue pairs to easily create a few AccordionItems:

            acc.ItemsSource = new[]
                                  {
                                          new KeyValuePair<string, string>("A header", "And the content of the accordion item"),
                                          new KeyValuePair<string, string>("Hello", "World")
                                   };

Let’s take a look at an Accordion and it’s Xaml:

image

ExpandDirection

An AccordionItem has the same ExpandDirection property as Accordion. When contained within an Accordion, AccordionItem will get the correct ExpandDirection from Accordion and will not allow you to change it individually.

ExpandDirection may only be set by Accordion, to prevent from weird mixes of ExpandDirections. Unfortunately, that does limit a few scenario’s (as in horizontal Accordion layout with vertical AccordionItems). If this turns out to be a common featurerequest, I’ll look into opening this up.

Accordion.ItemContainerGenerator

Accordion exposes an ItemContainerGenerator which has very helpful methods, such as ‘ContainerFromItem’ and even ContainerFromIndex’. So it is really easy to go from AccordionItem, to Item and vice versa.
This code might make you happy:

            for (int i = 0; i < acc.Items.Count; i++)
            {
                AccordionItem item = acc.ItemContainerGenerator.ContainerFromIndex(i) as AccordionItem;
             }
Locking mechanism

In certain SelectionModes, Accordion must make sure that at least one item is selected. It does so by locking an item if it is the last one open. If you somehow force it to close (through code), Accordion will just open the first AccordionItem in the list. However, that is not that simple, since an AccordionItem may not be unselected, while it is locked.

Since AccordionItem will actually throw an exception when it is unselected while locked, I expose a boolean ‘IsLocked’ that you can use to make sure you don’t accidently do this.
I expose a VisualState that allows you to visualize the lock if you’d like.

The best way to unselect the AccordionItem while in such a mode, is to select a different item.

ExpandableContentControl and AccordionButton

These are two template parts on the AccordionItem. The first takes care of opening and closing in a nice fashion and the latter makes it easier to template the header. It is not necessary, but adds a nice touch. I will talk more about templating them in a follow up post.
In the meantime, it is noteworthy that there is a property AccordionButtonStyle that you can use to style the button more easily.

Selected and Unselected events

Subscribe to these events to know when the user has selected an AccordionItem. Alternatively, you can use the SelectionChanged event on Accordion.

Layout

Under the covers, there is a lot of layout action going on! The item needs to know how to open itself, and also needs to be told _when_ to do so. The actual opening and closing does not correspond to the IsSelected state. In other words: IsSelected will be set whenever an item is selected, which could mean the AccordionItem is still closed. Accordion will instruct AccordionItem to actually open to visualize the new IsSelected state.

Templating

The most important part of the template is:

  1           <Border x:Name="Background" 
  2 			      Padding="{TemplateBinding Padding}" 
  3 			      BorderBrush="{TemplateBinding BorderBrush}" 
  4 			      BorderThickness="{TemplateBinding BorderThickness}" 
  5 			      CornerRadius="1,1,1,1">
  6               <Grid>
  7                   <Grid.RowDefinitions>
  8                       <RowDefinition Height="Auto" x:Name="rd0"/>
  9                       <RowDefinition Height="Auto" x:Name="rd1"/>
 10                   </Grid.RowDefinitions>
 11                   <Grid.ColumnDefinitions>
 12                       <ColumnDefinition Width="Auto" x:Name="cd0"/>
 13                       <ColumnDefinition Width="Auto" x:Name="cd1"/>
 14                   </Grid.ColumnDefinitions>
 15 
 16                   <layoutPrimitivesToolkit:AccordionButton
 17 					          x:Name="ExpanderButton"
 18                     Style="{TemplateBinding AccordionButtonStyle}"
 19 					          Content="{TemplateBinding Header}"
 20 					          ContentTemplate="{TemplateBinding HeaderTemplate}"
 21 					          IsChecked="{TemplateBinding IsSelected}"
 22 					          IsTabStop="True"
 23 					          Grid.Row="0"
 24 					          Padding="0,0,0,0"
 25 					          Margin="0,0,0,0"
 26 					          FontFamily="{TemplateBinding FontFamily}"
 27 					          FontSize="{TemplateBinding FontSize}"
 28 					          FontStretch="{TemplateBinding FontStretch}"
 29 					          FontStyle="{TemplateBinding FontStyle}"
 30 					          FontWeight="{TemplateBinding FontWeight}"
 31 					          Foreground="{TemplateBinding Foreground}"
 32 					          VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" 
 33 					          HorizontalAlignment="Stretch"
 34 					          VerticalAlignment="Stretch" 
 35                     HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
 36                     Background="{TemplateBinding Background}" />
 37 
 38                   <layoutPrimitivesToolkit:ExpandableContentControl
 39 					          x:Name="ExpandSite"
 40 					          Grid.Row="1"
 41 					          IsTabStop="False"
 42 					          Percentage="0"
 43 					          RevealMode="{TemplateBinding ExpandDirection}"
 44 					          Content="{TemplateBinding Content}"
 45 					          ContentTemplate="{TemplateBinding ContentTemplate}"
 46 					          Margin="0,0,0,0"
 47 					          FontFamily="{TemplateBinding FontFamily}"
 48 					          FontSize="{TemplateBinding FontSize}"
 49 					          FontStretch="{TemplateBinding FontStretch}"
 50 					          FontStyle="{TemplateBinding FontStyle}"
 51 					          FontWeight="{TemplateBinding FontWeight}"
 52 					          Foreground="{TemplateBinding Foreground}"
 53 					          HorizontalContentAlignment="Left"
 54 					          VerticalContentAlignment="Top" 
 55 					          HorizontalAlignment="Stretch"
 56 					          VerticalAlignment="Stretch"/>
 57               </Grid>
58 </Border>

Lines 7 through 14 create a grid with 2 columns and 2 rows.
Line 16 shows the AccordionButton, which is the little arrow + header.The arrow always points to the content and is in different locations, depending on the ExpandDirection.
Line 38 is the ExpandableContentControl, which takes care of the content.

An AccordionItem has, amongst others, 4 visual states for ExpandDirection. I will show the ExpandLeft state:

  1                   <vsm:VisualState x:Name="ExpandLeft">
  2                       <Storyboard>
  3                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpanderButton" Storyboard.TargetProperty="(Grid.ColumnSpan)">
  4                               <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
  5                           </ObjectAnimationUsingKeyFrames>
  6                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.ColumnSpan)">
  7                               <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
  8                           </ObjectAnimationUsingKeyFrames>
  9                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpanderButton" Storyboard.TargetProperty="(Grid.RowSpan)">
 10                               <DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
 11                           </ObjectAnimationUsingKeyFrames>
 12                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.RowSpan)">
 13                               <DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
 14                           </ObjectAnimationUsingKeyFrames>
 15 
 16                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpanderButton" Storyboard.TargetProperty="(Grid.Column)">
 17                               <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
 18                           </ObjectAnimationUsingKeyFrames>
 19                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="ExpandSite" Storyboard.TargetProperty="(Grid.Row)">
 20                               <DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
 21                           </ObjectAnimationUsingKeyFrames>
 22                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="rd0" Storyboard.TargetProperty="Height">
 23                               <DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
 24                           </ObjectAnimationUsingKeyFrames>
 25                           <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="cd0" Storyboard.TargetProperty="Width">
 26                               <DiscreteObjectKeyFrame KeyTime="0" Value="*"/>
 27                           </ObjectAnimationUsingKeyFrames>
 28                       </Storyboard>
29 </vsm:VisualState>

Everything in there is actually repositioning the AccordionButton (header) and the ExpandableContentControl (content) inside different grid cells.

In this case, you can imagine the header being in column 1 and the content taking up column 2.

You should not need to have to style an accordionItem itself that much. Most of the template is just about positioning the Header versus the Content. The only reason I see for restyling accordionItem is if you are unhappy with the defaults (easily changed) or with the position of the header and content. All the other styling would be done in either AccordionButton or ExpandableContentControl. Let me know if this is not the case for your scenario.

Thursday, 26 March 2009 06:05:36 (Romance Standard Time, UTC+01:00)  #    Comments [33]  |  Trackback
 Monday, 23 March 2009

Accordion is a fun control with a lot of properties and options. I had great fun building it and I hope you will have great fun while using it. This post gives an overview of the control. Next parts will look into other controls that are used with Accordion.

image

You can see a live demo here, or go to our sample browser (SL2, SL3) to play around with the control. I have a little ‘studio’ there, that will allow you to set some options and see how the control behaves.

The Accordion shown in the picture above is build like this:

  1     <layoutToolkit:Accordion x:Name="acc" Grid.Column="0" >
  2       <layoutToolkit:AccordionItem Content="item 1" Header="A"  />
  3       <layoutToolkit:AccordionItem Content="item 2" Header="B -  long header" />
  4       <system:String>regular string item 3</system:String>
5 </layoutToolkit:Accordion>

As you can see, three items are added. The first two are AccordionItems, allowing us to set a header and content. The last one is a String, which Accordion will wrap in an AccordionItem behind the scenes.

Accordion

Accordion can be best described as an ItemsControl that displays Expanders. The items shown are actually of type AccordionItem and can expand or collapse.
We use the paradigm of Selection, instead of Expanding/Collapsing, so from now on you will here me talk about a selected AccordionItem, which amounts to an expanded AccordionItem.

The Accordion class manages these AccordionItems and is responsible for the following:

  • Easily styling the AccordionItems (ItemContainerStyle)
  • Setting a default ContentTemplate and HeaderTemplate
  • Setting the ExpandDirection
  • Single select and multi select. Actually, Accordion allows a couple of SelectionModes that I will talk about
  • Managing a SelectedItem and SelectedIndex
  • Managing SelectedItems and SelectedIndices
  • Scheduling when a selected item is expanded or contracted (!)

Accordion manages AccordionItems just as ListBox would be managing ListBoxItems. Let’s first look at the concepts that you know from other ItemControls and see how Accordion handles them.

ItemTemplate versus ContentTemplate and HeaderTemplate

When an item is added to an ItemsControl, Accordion will check if it is already an instance of type AccordionItem. If it is not, the item will be wrapped inside an AccordionItem.
You might expect to be able to define how your item will look with an ItemTemplate, but that does not make that much sense, given that we expect you to want to set a header and a content separately.

Instead, Accordion exposes a ContentTemplate and HeaderTemplate DP. There is some talk to drop the ContentTemplate and just use the ItemTemplate for it, let me know what your thoughts on that are.

I do make sure that both Content and Header datacontext is set appropriately, so you might do something like this:

        <layoutToolkit:Accordion>
          <layoutToolkit:Accordion.ContentTemplate>
            <DataTemplate>
              <StackPanel Orientation="Horizontal">
                <TextBlock Text="Content:" />
                <TextBlock Text="{Binding}" />
              </StackPanel>
            </DataTemplate>
          </layoutToolkit:Accordion.ContentTemplate>
          
          <layoutToolkit:Accordion.HeaderTemplate>
            <DataTemplate>
              <StackPanel Orientation="Horizontal">
                <TextBlock Text="Header:" />
                <TextBlock Text="{Binding}" />
              </StackPanel>
            </DataTemplate>
          </layoutToolkit:Accordion.HeaderTemplate>
          
          <System:String>hello</System:String>
          <System:String>world</System:String>
</layoutToolkit:Accordion>

You would end up with an accordion that looks like:

image

In my opinion there are two main scenario’s to use an itemscontrol like Accordion:

  1. Bind to businessobjects: use ContentTemplate and HeaderTemplate to determine what is shown
  2. Set items inline: use AccordionItem directly.
Selection

An Accordion allows for the following SelectionModes: One, OneOrMore, ZeroOrOne and ZeroOrMore.

Since there can be more than 1 item selected, Accordion supplies a few properties (and events to match) to let you know what’s up:

  • SelectedItem: The selected item. If Accordion allows multiple items, this property will have the last selected item
  • SelectedIndex: The index of the selected item. This is necessary because you could have multiple AccordionItems based on the same item.
  • SelectedItems: A collection of all the items that are selected.
  • SelectedIndices: A collection of indexes.

By setting a SelectionMode, the behavior of Accordion is changed quite profoundly. When SelectionMode One or OneOrMore is chosen, Accordion will always make sure there is at least one AccordionItem selected. It does so by making it impossible to unselect an item if it is the last one selected. If no item is selected when the mode is set to One*, the first item in the list will be auto-selected.

ExpandDirection

You can specify in which direction the Accordion should open by setting an ExpandDirection (Left, Right, Up, Down). I’ll go in-depth in how you can template your AccordionItems appropriately. In the default template there is a little funky arrow that points to the content, that arrow is repositioned according to the ExpandDirection.

SelectionSequence

When the user selects an item, Accordion might have unselect another item (in the ZeroOrOne or One modes). The de-selection will happen immediate, however, Accordion gives you the option to influence when the actual animations happen. The SelectionSequence property takes values of 'CollapseBeforeExpand’ and ‘Simultaneous’.

The CollapseBeforeExpand mode will actually schedule collapse animations before expands. The effect is quite nice.

Please let us know if you are interested in different scheduling schema’s. I can for instance think of an Accordion that starts the next animation when it is halfway through the first one. Not sure if it looks good though!

The big one: Layout

Accordion will behave completely different when it has a fixed size versus no size. In the first case, the items will share all the space amongst each other that the parent has. In the latter, each item will take the space it desires.

I will revisit the layout topic more in-depth, because there are a few intricacies in how it works.

--------

Next parts will look at AccordionItem, ExpandableContentControl and LayoutTransformer.

Monday, 23 March 2009 02:24:20 (Romance Standard Time, UTC+01:00)  #    Comments [50]  |  Trackback
 Friday, 20 March 2009

In this post I’d like to take an in-depth look at DomainUpDown. We’ll look at the control from a code-centric point of view and follow along some samples to showcase its features.

image

You can see DomainUpDown live here. The sample page is (currently) SL3, but the control itself works fine under both SL2 and SL3.
Jesse Liberty even did a video on it, which can be seen here.

A DomainUpDown is a control that ‘spins’ over a domain of items. As such it inherits from our UpDownBase and will not make assumptions on the type of your domain.

Items and ItemsControl

Let’s start by looking at how we instantiate a DomainUpDown in Xaml with a simple domain.

    <inputToolkit:DomainUpDown

     MinWidth="120"

     HorizontalAlignment="Left">

      <system:String>11111</system:String>

      <system:String>22222</system:String>

      <system:String>33333</system:String>

      <system:String>aaaaa</system:String>

      <system:String>bbbbb</system:String>

    </inputToolkit:DomainUpDown>

This is a weird domain of strings that we create directly through Xaml.
I would think that a more useful scenario is to bind Items straight to a collection, so the following might be used:

<inputToolkit:DomainUpDown
  ItemsSource="{Binding}"
  MinWidth="120"
  HorizontalAlignment="Left" />

In this case, apparently the DataContext is set to be a collection, let’s check the codebehind:

IEnumerable airports = Airport.SampleAirports;
DataContext = airports;

As we expected, the DataContext is set to a collection, that we get from some static class. Another sample is more direct about it:

CultureInfo[] cultures = new[]
                             {
                                 new CultureInfo("zh-Hans"),    // chinese simplified
                                 new CultureInfo("da"),         // danish
                                 new CultureInfo("nl-NL"),      // dutch
                                 new CultureInfo("en-US"),      // english us
                                 new CultureInfo("fr"),         // french
                                 new CultureInfo("de"),         // german
                                 new CultureInfo("he"),         // hebrew
                                 new CultureInfo("it"),         // italian
                                 new CultureInfo("ru"),         // russian
                                 new CultureInfo("es-ES")       // spanish
                             };
cultureList.ItemsSource = cultures;

Our domain exists of Cultures and we set the ItemsSource to point to it directly.

ButtonSpinner

Once you instantiate the DUD, you will notice that it exists of a TextBox and a ButtonSpinner. The ButtonSpinner consists of two buttons going Up and Down. I have introduced visual states on ButtonSpinner like so:

[TemplateVisualState(Name = VisualStates.StateIncreaseEnabled, GroupName = VisualStates.GroupIncrease)]
[TemplateVisualState(Name = VisualStates.StateIncreaseDisabled, GroupName = VisualStates.GroupIncrease)]

[TemplateVisualState(Name = VisualStates.StateDecreaseEnabled, GroupName = VisualStates.GroupDecrease)]
[TemplateVisualState(Name = VisualStates.StateDecreaseDisabled, GroupName = VisualStates.GroupDecrease)]
public abstract partial class Spinner : Control

and a DP to set influence them:

/// <summary>
/// Gets or sets the spin direction that is currently valid.
/// </summary>
public ValidSpinDirections ValidSpinDirection
{
    get { return (ValidSpinDirections)GetValue(ValidSpinDirectionProperty); }
    set { SetValue(ValidSpinDirectionProperty, value); }
}

ValidSpinDirections is an enum that will allow the ButtonSpinner to trigger the correct VisualState and disable buttons.

So, once you reach the edge of your domain, the correct button will be disabled.

IsCyclic

DUD introduces a DP called IsCyclic that will change the above behavior. If set to Cycle the DUD will not disable any buttons.

ItemTemplate

A DUD is templateable, and in order to do so, exposes a DP called ItemTemplate.

The ItemTemplate can be used to define the way an item should look in displaymode (see next paragraph).

<inputToolkit:DomainUpDown
      ItemsSource="{Binding}"
      HorizontalAlignment="Left"
      Height="Auto"
      FontSize="34">
      <inputToolkit:DomainUpDown.ItemTemplate>
        <DataTemplate>
          <Grid MinWidth="370">
            <Grid.Background>
              <SolidColorBrush Color="#aa000000" />
            </Grid.Background>
            <TextBlock
              Foreground="#22ffffff"
              Margin="4+0,2+0"
              FontSize="34"
              Text="{Binding CodeFaa}" />
            <StackPanel
              HorizontalAlignment="Right"
              Margin="0, 0, 8, 0">
              <TextBlock
                HorizontalAlignment="Right"
                Foreground="White"
                FontSize="12"
                Text="{Binding LimitedName}"
                Padding="2" />
              <TextBlock
                HorizontalAlignment="Right"
                Foreground="White"
                FontSize="14"
                Text="{Binding City}"
                Padding="2" />
              <TextBlock
                HorizontalAlignment="Right"
                Foreground="White"
                FontSize="14"
                Text="{Binding State}"
                Padding="2" />
            </StackPanel>
          </Grid>
        </DataTemplate>
      </inputToolkit:DomainUpDown.ItemTemplate>
    </inputToolkit:DomainUpDown>

Binding against our airlines sample data, this produces the following DUD:

image

The ItemTemplate is what gives our DUD most of its usefulness, as you will see in a while.

EditMode and DisplayMode

DUD introduces the concept of being in Display mode and in Edit mode:

[TemplateVisualState(Name = VisualStates.StateEdit, GroupName = VisualStates.GroupInteractionMode)]
[TemplateVisualState(Name = VisualStates.StateDisplay, GroupName = VisualStates.GroupInteractionMode)]

DisplayMode hides the TextBox and show a ContentControl, and EditMode will do the opposite.

The airlines DUD that you see above this paragraph is in DisplayMode. Going to EditMode is simple, by clicking your mouse inside the TextBox or tabbing into the control. You can use escape to cancel an edit and enter to commit an edit.It looks like this:

image

Edit mode can be used to quickly select an item. This allows rapid selection out of a huge list.
Once the user has typed some text, the default behavior is to check the domain for items that match using their ToString() representation. You can be sure that Airport has overridden ToString() to give back the “CodeFaa” property.

Please be mindful of your scenario. If you feel you wish to guide your users in their selection, please use an AutoCompleteBox.

That said, there are possibilities of going beyond a simple ToString comparison. Read on!

Converter, ConverterParameter and ConverterCulture

[disclaimer: lately other toolkit controls have been moving away from this strategy and we might decide introducing memberPaths is the best way forward for DUD as well.]

In order to be more sophisticated about how an item should be presented in a String format, you can plug-in a Converter.

Let’s see an example:

<inputToolkit:DomainUpDown
     Converter="{StaticResource BorderToStringConverter}"
     MinWidth="120"
     Height="Auto"
     HorizontalAlignment="Left">
     <Border
       Background="Red"
       Margin="4"
       BorderThickness="1"
       BorderBrush="Black"
       Width="80"
       Height="50" />
     <Border
       Background="Green"
       Margin="4"
       BorderThickness="1"
       BorderBrush="Black"
       Width="80"
       Height="50" />
     <Border
       Background="Blue"
       Margin="4"
       BorderThickness="1"
       BorderBrush="Black"
       Width="80"
       Height="50" />
   </inputToolkit:DomainUpDown>

That Xaml creates this visual:

image

In EditMode, we’ll see this:

image

As you can imagine, the ToString of Border is not ‘Green’. The DUD defines a Converter on line 2 and that BorderToStringConverter allows DUD to make a more informed decision on how to present the item in the TextBox and how to do a comparison.

The converter used here is quite simple:

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    // expecting a border
    Border element = value as Border;
    if (element != null)
    {
        SolidColorBrush b = element.Background as SolidColorBrush;

        if (b != null)
        {
            // use the colors class to find a friendly name for this color.
            string colorname = (from c in typeof(Colors).GetProperties(BindingFlags.Public | BindingFlags.Static)
                                where c.GetValue(null, new object[] { }).Equals(b.Color)
                                select c.Name).FirstOrDefault();

            // no friendly name found, use the rgb code.
            if (String.IsNullOrEmpty(colorname))
            {
                colorname = b.Color.ToString();
            }
            return colorname;
        }
    }
    return String.Empty;
}

The converter is only used to determine a string representation for the items in the domain. What happens if a user types a string that can’t be matched?

ParseError

The ParseError event would be raised.

Not handling that event will trigger a couple of behaviors that I will discuss in the next paragraph.
By handling this event, we could possibly add an item to the domain on the fly and select it. One of the samples does just that:

private void DomainUpDown_ParseError(object sender, UpDownParseErrorEventArgs e)
{
    DomainUpDown dud = (DomainUpDown)sender;

    // get the text that was unable to parse.
    string text = e.Text;

    ………… do ugly stuff here

    if (backgroundColor != null)
    {
        dud.Items.Add(new Border
        {
            Width = 120,
            Height = 80,
            Background = backgroundColor,
            BorderBrush = new SolidColorBrush(Colors.Yellow),
            BorderThickness = new Thickness(4)
        });
        dud.CurrentIndex = dud.Items.Count - 1;
    }
}

In that sample, the user could type Yellow and an item would be added that is a Border with a fill of Yellow.

[In case you were wondering why I did not just use an ItemTemplate and the Strings ‘Red’, ‘Green’, ‘Blue’ instead of all this Converter work: that wouldn’t make a good sample would it! But yes, it would have been easier!]

Invalid Input: InvalidInputAction and FallbackItem

Let’s pretend you did not handle the ParseError and the user typed something that can not be matched. There are several ways you might configure DUD to handle this situation.

The InvalidInputAction is an enum with two options: UseFallbackItem and TextBoxCannotLoseFocus.

When UseFallbackItem is set, DUD will look at the FallbackItem and select it. If there is no FallbackItem set, DUD will just ignore the edit and move back to DisplayMode (as if you pressed Escape).
If we set TextBoxCannotLoseFocus, the Error VisualState is triggered and DUD will attempt to set focus back to DUD when it loses focus.

image

This behavior can not be guaranteed (ofcourse) and should be used with great care. I would love feedback on this behavior, because it is quite daring indeed! If we get positive feedback on this, we might consider implementing it in other controls.

CurrentIndex

The DUD can be managed by setting the CurrentIndex DP.
If you set an index larger than the amount of Items, I will revert back to the old index and present you with an exception.

During Initialization from Xaml this is allowed though, and the index you wanted will be remembered and set as soon as possible.

IsEditable

You can make your DUD always be in DisplayMode by setting IsEditable to false. This enables fun slideshow scenarios.

Conclusion

A DomainUpDown is a very simple control that comes in handy when screen real-estate is at a premium, or when items are presented that can not easily be matched by a string representation or when you just want a page through data.

Pretty soon we’ll build an image slide show with it.

Friday, 20 March 2009 22:21:29 (Romance Standard Time, UTC+01:00)  #    Comments [25]  |  Trackback
 Thursday, 19 March 2009

Announcing the release of our latest Silverlight Toolkit: March 2009 edition.

I just checked when I blogged last and that was in December 2008, when we released the December edition. Since then I have not posted a single thing…  You can be sure that it was because I was hard at work on the toolkit. In it, has gone an enormous amount of work by the team to stabilize code, add new features, provide an actual installer, hugely improve the design time experience using either Blend or Visual Studio and actually create VB samples for our sample app!
If you have SL3 installed, do check out the changelist that has quite a few inline ‘live’ samples.

Also, since you now have SL3 installed anyway, go and look at our sample application, which showcases the controls.

There are six new controls: Accordion, DomainUpDown, LayoutTransformer, TimePicker, TimeUpDown, TransitioningContentControl, and you will be able to blame me for any and all bugs that you find in 5 of those.

(Well, please blame Justin Angel as well, I worked closely with him on these controls and he sure does like it a lot when I share credit).

Let’s take a quick look at my controls. In the coming days, you can expect in-depth posts about all of them. I am planning on going into the design issues as well and am going to point out areas that might change in the future. I’m even going to ‘hint’ at some of the features you just might want to vote on!

DomainUpDown

Live sample here.

This control won’t win the award for being the most creative control in the world, but looks can be deceiving. Think of it as a ComboBox without the popup, and you’ll understand what I mean. A more technical description would be: an UpDown control with an ItemTemplate.

DomainUpDown (DUD) allows you to show a collection of items and iterate over them. It allows you to set an itemtemplate to style them and it features an up and down arrow. Finally, it has an ‘edit’ mode, which will bring you back to a TextBox where you can type.

So, let’s say you have a domain of ‘Red’, ‘Green’ and ‘Blue’, you can visualize these any way you want (using the ItemTemplate) and the user could type ‘Green’ to jump to that item or use the ButtonSpinner to get there.

image

DUD is useful in numerous situations, like in mobile devices but also in visualizing data that you would like the user to ‘browse’ through, such as a slide show.

Accordion

Live sample here.

Accordion is a hugely requested control and I really hope many will find it useful in their day-to-day work. (Please send me samples of how you are using accordion in a creative way!)

An accordion is a control that presents multiple pieces of content and lets the user collapse/expands those. You use accordions everyday, for instance in Outlook.

Highlights of Accordion are:

  • allows multiselection (expanding multiple items)
  • has different selection modes (you can force that a minimum of one item is ‘open’)
  • open/close animation is a VSM state so very easily configured in blend
  • allows sequential animations (say the user selects an item, we will first close the old one and then open the selected item)
  • different expand directions (left/right/up/down)
  • can fill space or take the space it needs

The sample application has a little dashboard that lets you play with different settings.

image

There are a lot of exciting applications for a control like this and I will surely be blogging about Accordion with more details soon.

TimeUpDown

Live sample is here.

A TimeUpDown is TextBox with two spinners, allowing you to use the mouse (or keyboard) to easily set a time.

image

Without a doubt TimeUpDown and it’s friend TimePicker are the most complex controls of the bunch. There is a lot of functionality here that hopefully makes it really easy for end users to select a time.
The big feature is that the controls are completely globalizable, through many extension points. Arabic was easy to implement:
image

Noteworthy features are:

  • contextual spinning: the control is aware of the caret position and will spin accordingly. So you could spin in 10 minute intervals, or in 1 minute intervals, based on the location of the cursor
  • likewise, if you have a defined a minimum or maximum to the control, the possibilities of the spinner are determined by your caret position (you might not be allowed to increment by an hour, although if you move the cursor to the minutes, you can increment minutes still)
  • you can set a culture
  • you can set the TimeFormat used to format the string, as either Short, Long or custom. In the latter case you can define the format your self, the former cases will rely on the defined culture
  • there is a strategy class that handles all the globalization. Almost all methods are protected virtuals, just waiting for you to implement Klingon culture!
  • an extensible TimeParsing mechanism, allowing you to write small parsing classes that make sense in your business. Maybe you want your users to be able to write ‘now’ and have it parse to DateTime.Now?? Can do!
  • out of the box ‘catch-all’ parser, which tries to be as smart as possible when it comes to parsing your time. It allows you to write 1234 and parse it to 12:34. Or 9p and parse to 9:00 PM.
  • intellisense-like experience (balloon) that guides users into entering a correct time

Getting the experience of entering time just right is extremely important to us, so we are actively looking into making the experience even better. The balloon is one of those ideas that I absolutely love. See what I mean:

image

We will hopefully make incremental steps and are actively looking for your feedback.

TimePicker

Live sample is here.

TimePicker uses a TimeUpDown and combines it with the possibility of picking a time through a Popup. We ship with two popups:

The ever useful ListTimePickerPopup:
image

And the often overlooked RangeTimePickerPopup:
image

You can expect us to come up with a few different pickers in the coming releases, but yet again, I would love your feedback on what a good picker is.

The popups can be used as standalone controls as well.

TransitionContentControl

Live sample is here.

This control is in the experimental band. That translates into: don’t touch, just look. The API for this control is most likely going to change significantly. The only reason we publish it, is to get feedback. We want to know if you find these class of controls useful. It is not production quality.

I’ve blogged about the ideas behind this control extensively. A TransitionContentControl can be used as any other ContentControl: it just renders the content you give it to the screen. However, when you set different content to it (it inherits from ContentControl, so just set the Content property, like you are accustomed to), it will not immediately remove the content currently shown on the screen.
Instead, it will start an animation (VSM based) that will transition the ‘old’ content out and the ‘new’ content in. For instance, a fade in/out.

As such, it can be used anywhere where you would normally use a content control.

Most importantly, the API allows you to set the name of the transition you wish to play. So, you might have retemplated the control and added an ‘up’ and ‘down’ vsm state to the presentationgroup. By just setting the Transition property to those names (strings) you can control how the content is transitioned.
So, for instance, a DomainUpDown could use this control and define those states. Then, when the Up button is hit, the Up transition would be triggered. When the Down button is hit, the Down transition would be used.

My team mate Mehdi created some great visual transitions for content and used it in conjunction with the DomainUpDown.

The value of the control is squarely in making it easy for you to drop in a ContentControl that can do transitions, instead of having to write this code by your self again and again.

Warning: Since the control will keep the old element alive in the visual tree for some undetermined amount of time (the time it takes to transition), you should not use the control with UIElements.
That could easily give you a well-deserved exception.

 

More to follow soon!

Thursday, 19 March 2009 09:22:02 (Romance Standard Time, UTC+01:00)  #    Comments [32]  |  Trackback
 Tuesday, 09 December 2008

Only a few weeks after our initial release, my team has just released the December edition of the Silverlight Controls Toolkit. Go get it here!

It is a very exciting release because it underlines the teams dedication to releasing fast and often. As you know, the toolkit is licensed under the very permissive Microsoft Public License, which means you can do virtually anything you like to the code. I think with our release schedule, you guys can rest assured that we continue to deliver high quality and very useful controls. In this release we also fixed quite a few bugs, some of which were reported by our users!

One of the most visible controls in the toolkit (autocompletebox) has moved to the stable quality band, as did NumericUpDown. That means the api of those controls is less likely to change, and we are increasingly confident that the controls will address over 90% of customer scenarios.
Ofcourse, many new features were also introduced. Charting has continued to get (much) better, with changes to the way series get instantiated (making it easier on you) and support for multiple axis.
Please check out our improved sample application to see the controls in action.

There is now much better designer integration and 3 new themes were introduced. I love Whistler Blue:

Whistler Blue Thumbnail

I hope you enjoy this release. Please continue your feedback on our forum.

Tuesday, 09 December 2008 23:19:02 (Romance Standard Time, UTC+01:00)  #    Comments [12]  |  Trackback
 Wednesday, 26 November 2008

In my previous post, I introduced my thoughts on how animation between positions should be done. Today I will walk you through some of the technical benefits of that approach with a cool animated wrappanel sample. Yes, the silverlight control toolkit includes a wrappanel. Let’s animate it, the easy way!

Sample

I got some feedback that the samples that I inlined were not visible from rss feeds. So, visit this page to see the sample in action.

image

Please note the big black slider on the right.. Drag it to the left to do some layout transitions between items.
Also, to illustrate the flexibility of this approach, there is a textbox on the top that determines the startdelay of the items (Random between 0 and the number you give it). Set it to 700 milliseconds and then quickly drag the slider to the left. You will see some items moving immediately, others moving after some time. This gives it a less ‘plastic-fantastic’ feel.

The items are shown on the page using the xaml below. Notice how I use a regular wrappanel as ItemsPanelTemplate.

        <ItemsControl Grid.Row="1" x:Name="anim" HorizontalAlignment="Left">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <ItemMovementSpike:MoveableItem Margin="10">
                        <Border Background="Yellow" Width="80" Height="80" >
                            <ContentPresenter Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Border>
                    </ItemMovementSpike:MoveableItem>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Controls:WrapPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemsSource>
                <Controls:ObjectCollection>
                    <System:String>1</System:String>
                    <System:String>2</System:String>
                    <System:String>3</System:String>
                    <System:String>4</System:String>
                    <System:String>5</System:String>
                    <System:String>6</System:String>
                    <System:String>7</System:String>
                    <System:String>8</System:String>
                    <System:String>9</System:String>
                    <System:String>10</System:String>
                    <System:String>11</System:String>
                    <System:String>12</System:String>
                    <System:String>13</System:String>
                    <System:String>14</System:String>
                </Controls:ObjectCollection>
            </ItemsControl.ItemsSource>
</ItemsControl>

 

Again: who does the animation? Panel or the item

One of the first samples on layout transitions inside a panel are from Dave Relyea (DevDave) and can be seen here. The big difference between the approaches is that he uses a custom-made wrappanel, which does the layout transition.

As you can remember from my previous post, I believe that a panel should only calculate and determine the actual logical position of an item. The transition between positions is not the concern of the panel and should be handled by some other dedicated object. In this case, you can see that I’ve chosen to wrap my content inside of a ‘MoveableItem’ whose concern it is to transition between positions.

As discussed, it is a pity that we have to work so hard inside of MoveableItem, because panels do not notify their children about a position change. But it can be done fairly efficient.

C’mon, isn’t the panel responsible for layout.. So it is HIS concern!

No sir, I strongly disagree. The panel is responsible for the layout of an element on the screen, sure. But they exist in all kinds of forms (Grid, dockpanel, wrappanel) and the intent is to determine the end position as quickly as possible. I feel it is not okay to start mixing that concern with an animation framework.
As always, a class should have only 1 reason to change. A panel should only be changed to alter the layout logic, not because we decided to add features to our transitioning logic.

What are the benefits of separating these concerns

By letting a panel be a panel, and having another class handle the transition, you get a much cleaner model.
First and foremost, we now do not have to alter panels in order to use animation. Most of the solutions I have seen require you to change the baseclass of your panel. This means you can not easily leverage the panels that someone else created, like the toolkits WrapPanel or DockPanel or the build-in Grid and StackPanel! It would mean creating your own versions of these panels. If you’re on the Silverlight side, you may copy our source and change the baseclass, but that will be pretty hard to do for a Grid in any case.

Second, designtime experience. Although I setup my animation in code at this moment, MoveableItem could also just go to a different visual state: ‘Moved’ and allow you to use Blend to create a transition yourself! This would be very easy to do, because MoveableItem could just expose a double where 1 means: in endposition and 0 means: in beginposition. That double could be easily animated using keysplines to create cool effects. See my expander series for more on that approach. I might follow up with a complete sample.

Stretching it a bit, I feel these come into play as well:

Flexibility. I can think of quite a few interesting properties I might want to set on my ‘MoveableItem’, like Weight, Gravity and so on. We could plugin Penner-equations (like, easeIn, bounce etc.) at the item level in a very clean way (cleaner than attached properties).

Performance. The effects I am thinking of go beyond mere transitions. For instance, it might take one item some time to ‘settle-in’: slowly rocking for some time before it finally goes silent. Others might have settled earlier. A panel keeps performing arrange overrides to allow for this. This is overkill.

Conclusion

I find this an intriguing topic. I know people have been using transitioning panels for quite some time now and are very comfortable with it. I hope that this post does make you think though, even though you might completely disagree :)
Let me know either way!

Wednesday, 26 November 2008 21:08:01 (Romance Standard Time, UTC+01:00)  #    Comments [8]  |  Trackback
 Sunday, 23 November 2008

In my previous post, we talked about one approach to visualize the change of content. So, we had a control with the content ‘Ruurd’ and when we changed the content to ‘RJ’, we could very easily setup a fade out for ‘Ruurd’ and a fade in for ‘RJ’. Or get more fancy and add a small translate transform.
In this post I want to share some of my thinking on a related subject: how do you visualize the movement of an object? For example: we have a button in a grid on column 0 and we move it to column 1. That move is instant, but in many cases you would like an actual animation between those locations.

There is precedence here, namely in the many tweening libraries for Flash and nowadays for Silverlight. Also, Nikhil has written a lot about this subject with his great attached behavior series.

Sample

I like typing in that textbox a LOT. I’m sure you will too.  :)

Power to the item or does the panel still rule?

It is hard to determine when you should use a panel to do animation and when you should not. I live by this rule:

When the movement itself has meaning, use a panel. If it is an effect, let the item take care of it.

This means: if during the movement you should be able to stop the movement and use the items in that position, than the visual tree should not have any translate transforms going on. The layout system should be managing the location of these items.
If on the other hand the movement is just a transition, the panel should not need to be aware of this transition and just be used to determine the logical positions of an item. That is many times more efficient.

To use examples: a carousel is showing items and we let the user ‘nudge’ the items a few pixels –> the new positions are the real positions and thus a panel should be used.
Items in a dockpanel or a stackpanel that move around within that panel should be managing their transition themselves.

So, I believe effects (visual position) should be managed by the item, logical position should be determined by the panel.

This has the added technical advantage that we do not have to rewrite all panels. This is illustrated by the xaml for this sample:

    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ItemMovementSpike:MoveableItem HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
            <Button Click="Button_Click" Content="Move me" />
        </ItemMovementSpike:MoveableItem>
        <ItemMovementSpike:MoveableItem Grid.Row="1" HorizontalContentAlignment="Stretch" >
            <TextBox TextChanged="TextBox_TextChanged" Text="TypeMe" />
        </ItemMovementSpike:MoveableItem>
</Grid>
You can see I just use the regular Grid layout panel, nothing to see there!
Efficient location determination

Because we do not want to change our panels, we will have to determine our position on the screen ourselves. This is a somewhat inefficient process, so it is important to start thinking about where we want to take that hit.

The WPF/Silverlight layout system is very cautious about forcing a layout pass in a panel. It only does so when it is forced, so you can be certain that something has changed. The part of the layout pass that we are interested in is the Arrange part. During the arrange, the panel asks each child to arrange itself given a certain Size and then determines its position. So:

Panel.Arrange
  for each item: Item.Arrange
Panel –> set location and size for item (using a rectangle)

During this time, the most beloved of events is fired continuously on the item: CompositionTarget.Rendering.

The Rending event is called each time before the item is drawn on the screen and therefore comes with great power! If we were to do anything that is using cpu-cycles in this event, we will see some serious performance problems in our application. So, we need to be clever about it.

It is important to know that during the Item.Arrange, our item is still in its old location: The panel has not yet set its position, that will come a few Rendering events later. So, we can use the Item.Arrange to determine the current position with a TransformToVisual call. This is an expensive call, but since we only use it in the Arrange method (which is called only when necessary), it is cheap enough for our purposes.

   78 protected override Size ArrangeOverride(Size finalSize)

   79 {

   80     fromLocation = TransformToVisual(((UIElement) this.Parent)).Transform(new Point(0, 0));

   81     fromLocation.X += tt.X;

   82     moved = true;

   83     return base.ArrangeOverride(finalSize);

   84 }

I determine the current location on line 80 as it relates to its parent (the panel). You could do a quick visual tree walk to find our plugin root, which would enable some cool scenarios!!

Line 81 corrects for our current translatetransform (tt) and then we set a boolean ‘moved’ to true, indicating that something has happened.

The important stuff happens in our Rendering event:

   55 void CompositionTarget_Rendering(object sender, EventArgs e)

   56 {

   57     if(moved)

   58     {

   59         moved = false;

   60         toLocation = TransformToVisual(((UIElement) this.Parent)).Transform(new Point(0, 0));

   61         startmove = DateTime.Now;

   62         moving = true;

   63     }

   64 

   65     if(moving)

   66     {

   67         double fraction = DateTime.Now.Subtract(startmove).TotalMilliseconds/200d;

   68         tt.X = (fromLocation.X - toLocation.X)*(1 - fraction);

   69 

   70         if(fraction >= 1)

   71         {

   72             moving = false;

   73             tt.X = 0;

   74         }

   75     }

   76 }

It happens to be that in the first rendering event that is called, the move has occured. I’m not sure if that is a general rule, or something that just happens consistently on my machine. I haven’t looked into that yet.

We do one more inefficient TransformToVisual to find out where we currently are. Then we need the current time (startMove) and we indicate that we are moving. In the moving code we set the appropriate translateTransform (tt) and make sure that we stop moving when time is up.
If you’re into penner equations, you would plug them in after line 68.

No source for this one though, it’s just a spike. There are many things yet to be considered. But do let me know your thoughts.

Sunday, 23 November 2008 01:58:52 (Romance Standard Time, UTC+01:00)  #    Comments [15]  |  Trackback
 Thursday, 20 November 2008

This post will show you how to write a very simple contentControl, that allows you to transition your old content with your new content. It is a little prototype I put together after wanting to ‘spice up’ another control I was working on!

Show me!

What?

A contentControl is a control that only has one reason in life: to show your content. You will use it in places where you have an arbitrary object that you wish to display. For instance, a ‘Person’ business object, or a ‘string’. Its two most useful properties are ‘Content’ and ‘ContentTemplate’. The latter will let you define how to show your person.

By bundling two contentControls, we can move the new content to the upper contentControl and show the old content in the lower contentControl. Then we can use blend to think up some great transition between those visuals.

Why?

The current contentControl does not allow you to do a transition effect because it has no concept of ‘old content’. This means the change can not be visualized in a rich manner. The only thing you could do is transition in the new content, but the old content will be gone instantly.

Why not?

So, the Silverlight team could have just build such an approach into the framework, why didn’t they? Well, I have not checked officially, but can come up with a bunch of reasons. Most of all, this will keep a reference to old content (for the time the storyboard runs) that you might not expect. Not a big deal on a string, but possibly a very big deal on a large business object. Also, using this approach will result in a much larger object graph inside of the visual tree than just a contentControl: we’re adding a second contentControl, a layout panel, two visualstates and a few storyboards. Finally, creation of a template is costly and now happens twice when the content gets changed.
So, it is not a good idea to use this control all over the place. Please use sparingly, with caution and very lightweight templates.

How?

It’s a pretty straightforward control. (I’ll not do the pictures thing this time.. let’s see how copying regular code feels!)

    9     [TemplateVisualState(Name = "Transitioning", GroupName = "PresentationStates")]

   10     [TemplateVisualState(Name = "Normal", GroupName = "PresentationStates")]

   11     [TemplatePart(Name = "PreviousContentPresentationSite", Type = typeof(ContentControl))]

   12     public class TransitioningControl : ContentControl

   13     {

   14         private ContentControl PreviousPresentationSite { get; set; }

   15 

   16         public TransitioningControl()

   17         {

   18             DefaultStyleKey = typeof(TransitioningControl);

   19         }

Like I said, we’ll need a second contentControl to host our old content. This is represented by line 11.

Pretty interesting is the onApplyTemplate:

   21         public override void OnApplyTemplate()

   22         {

   23             base.OnApplyTemplate();

   24 

   25             PreviousPresentationSite = GetTemplateChild("PreviousContentPresentationSite") as ContentControl;

   26 

   27             Panel root = GetTemplateChild("LayoutRoot") as Panel;

   28 

   29             Storyboard sb= null;

   30 

   31             if (root != null)

   32             {

   33                 sb =

   34                     (from stategroup in (VisualStateManager.GetVisualStateGroups(root) as Collection<VisualStateGroup>)

   35                      where stategroup.Name == "PresentationStates"

   36                      from state in (stategroup.States as Collection<VisualState>)

   37                      where state.Name == "Transitioning"

   38                      select state.Storyboard).FirstOrDefault();

   39             }

   40 

   41             if (sb != null)

   42             {

   43                 sb.Completed += ((sender, e) =>

   44                                      {

   45                                          // go to normal state and release our hold on the old content.

   46                                          VisualStateManager.GoToState(this, "Normal", false);

   47                                          if (PreviousPresentationSite != null)

   48                                          {

   49                                              PreviousPresentationSite.Content = null;

   50                                          }

   51                                      });

   52             }

   53 

   54             VisualStateManager.GoToState(this, "Normal", false);

   55         }

I’m looking for the Transitioning State and take its StoryBoard. Now I can register some code to execute when the storyboard finishes. This represents our ‘cleanup’.
Obviously, this is prototyping code, so do not just copy this and use in a production system! At the very least you would need to take care of unhooking the event, etc. etc.

   57         protected override void OnContentChanged(object oldContent, object newContent)

   58         {

   59             if (PreviousPresentationSite != null)

   60             {

   61                 PreviousPresentationSite.Content = oldContent;

   62 

   63                 base.OnContentChanged(oldContent, newContent);

   64 

   65                 // if busy with transitioning, let's skip to normal

   66                 VisualStateManager.GoToState(this, "Normal", false);

   67                 // and start our state

   68                 VisualStateManager.GoToState(this, "Transitioning", true);

   69             }

   70         }

So, when our content changes, we get a very convenient ‘old content’ object.

I just quickly change to Normal state, which probably already happened (line 46), but if the content changes again before the storyboard finishes, it looks better to start afresh.

This is my attempt at a transition:

                        <vsm:VisualStateManager.VisualStateGroups>
                            <vsm:VisualStateGroup x:Name="PresentationStates">
                                <vsm:VisualState x:Name="Transitioning">
                                    <Storyboard>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CurrentContentPresentationSite" 
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="-40"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.800" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="CurrentContentPresentationSite" 
Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.800" Value="1"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PreviousContentPresentationSite" 
Storyboard.TargetProperty="(UIElement.Opacity)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.800" Value="0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="PreviousContentPresentationSite" 
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
                                            <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
                                            <SplineDoubleKeyFrame KeyTime="00:00:00.800" Value="40"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                                <vsm:VisualState x:Name="Normal">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" 
Storyboard.TargetName="PreviousContentPresentationSite" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </vsm:VisualState>
                            </vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>

 

This is how the sample is setup:

        <local:TransitioningControl x:Name="contentChanger" 
                                    HorizontalAlignment="Center" 
                                    VerticalAlignment="Center" 
                                    Content="{Binding Path=CurrentTime}">
            <local:TransitioningControl.ContentTemplate>
                <DataTemplate>
                    <Border BorderBrush="Blue" BorderThickness="2">
                        <ContentPresenter Content="{Binding}" />
                    </Border>
                </DataTemplate>
            </local:TransitioningControl.ContentTemplate>
</local:TransitioningControl>

CurrentTime is a string that changes each second. The datatemplate is not necessary at all, just showing that it works.

I’d appreciate some feedback on this. So let me know if you would find such a control useful.

Source

Thursday, 20 November 2008 01:40:00 (Romance Standard Time, UTC+01:00)  #    Comments [42]  |  Trackback
 Monday, 17 November 2008

In my previous post, I showed an approach to getting the expander to ‘reveal’ its content. Although it worked well, it did so by programmatically creating a storyboard and targeting the height/width. That did not sit well with me, and in this post I’ll show another approach.

Before I had settled on that approach, I had created a dependency property on Expander ‘ExpandAmount’ but quickly realized I could not target it from within the template. Since you also can not target custom attached properties in a storyboard, I was stuck. However, the solution is as clear as it is simple: Just create a custom ContentControl that has our beloved ‘ExpandAmount’ property. We will be able to target that property for sure!

The template becomes:

image

Note that I set the Height and Width to 0, because the expander always begins in ‘collapsed’ state. Visibility is not set to collapsed.
We can target it in our –now Blend friendly- VSM:

image

The relevant code on our ExpandableContentControl is shown here:

image

Since content can be either a UIElement or a regular object (like String), we can not use it to determine the size we would like to be. However, we can take a look at the contentpresenter that is presenting our content and ask it. By doing this in the OnApplyTemplate, we will not be able to respond to changes inside the contenttemplate. However, for our demonstration, that is not a big issue.

One issue I ran into was the fact that this control has no nice way to get to the expander and find out how it should do calculate its Size. Based on the ExpandDirection, we might want to change the width or the height. I chose to have the Expander tell its content when it starts to expand or collapse.

We end up with a fully customizable expander. Go have some fun in Blend. Below is a sample where I adjusted the speed to slow down at the end. Also included is the source.

 

Source

Monday, 17 November 2008 20:42:24 (Romance Standard Time, UTC+01:00)  #    Comments [23]  |  Trackback
 Friday, 14 November 2008

A few weeks ago, we released the Silverlight control toolkit. There are some nice controls in there, including the expander.
In this post I will take a look at the expander and alter is so it’s content transitions in/out. Yes, my peeps: we are extending the expander!

In a simple scenario, the expander is used as follows:

image

I have used a border to show off its content. When you run this program, and click on the expander, this is what you will see:

image

Now, when you click the expander button (helpfully pointed out by my big red arrow!), the expander will immediately remove our content. There is no nice effect or transition going on.
This is consistent with WPF behavior.

But it is not cool, is it?

So, let’s take a better look, and open up the expander.xaml inside of our toolkit source. The relevant part is here:

image

and in the visual states:

image

So, the ‘ExpandSite’ is an element that has a visibility of Collapsed. When the control is ‘expanded’ the expandsite will be set to visible. Effective, but no fun.

We will manually transition the height of the content. I will leave it up to you to create a switch on the expanddirection and animate the width instead.

In order to hook everything up, I will create a new class, called: SmoothExpander.

image

The code should point you to the next change: I have added a scrollviewer inside the new template. So, I copied the expander template and put it inside themes\generic.xaml. The relevant parts are:

image

and

image

First off, I removed the Storyboard in the VisualState, since I will be creating on in code. Also, I have put a scrollviewer around my contentcontrol. Let’s take a look at that in more detail:

  • The scrollviewer will host the contentcontrol
  • Its height is set to 0, so it will not show up
  • The visibility of the contentcontrol is set to Visible

This gives us the option of using the extentHeight property of the Scrollviewer to figure out how large our contentcontrol wants to be!

So, lets implement our behavior:

image

I have been wanting to show how to use a storyboard in code for some time.
In both cases a storyboard is created with a DoubleAnimation that animates from the current Height to either 0 or the intended height. We couple this animation to our scrollViewer and off we go.

Since we do not collapse or expand straight away, we should change our code to run those methods on the storyboard finish:

image

 

Running sample is below. The second expander is of type ‘SmoothExpander’.

Source for this sample

I kept the sample as straightforward as possible. Extra work that should be done though:

  1. Create a templatepart attribute so that Blend knows about this scrollviewer
  2. Do not rely on the existence of the scrollviewer.
  3. Use expanddirection to toggle behavior.
  4. Possibly allow for a maximum to be set on the scrollviewer.
Friday, 14 November 2008 02:15:12 (Romance Standard Time, UTC+01:00)  #    Comments [9]  |  Trackback
 Tuesday, 28 October 2008

[update: source included
I have included the source. The source code works with the asp.net developer webserver instead of IIS. That will make it easier for you to run the sample!]

The autocomplete box is a control that will popup a combobox based on what the user is typing. You see a lot of these on the web, and for good reason: they really help make your enduser more productive!
In this post I will walk you through building a (WCF) webservice and connecting it up to the box. As you will see, it's really easy.

If you are unable to visualize it, this picture from our codeplex pages demonstrates a simple example:

AutoCompleteBox example

Step 1: create the webservice

We start of by creating a new project and choosing to create a new asp.net project for us. It will hold our webservice. Let's add one by choosing the 'Silverlight enabled WCF Service' template that should be when you add a new item.

[sample uses asp.net web server]

Setting everything up correctly:
You will need to install IIS and most of the (non-default) settings. Visual studio will complain if you are not setup correctly and tell you which features it lacks.
What bites many people that install IIS after they install .Net is that WCF isn't registered correctly. You can do so by issuing the 'ServiceModelReg /r' command. Find it here: %SystemRoot%\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\

Do run visual studio as an administrator. You will get an error saying that the client can not be found, if you don't.

Lastly: you will not need any crossdomain xml definitions because you are running from the asp.net server. Do not forget to add one, if you are publishing your project though!!

So, add a service called 'NamesService.svc'.
After you have made a list of your friends, return them in a similar fashion as I do:

image

(Actually, after watching the PDC keynotes, don't you really want to use livemesh to do this kinds of stuff?? Or could we get this from Bluehoo? )

Step 2: connecting from silverlight

In your silverlight application, add a service reference. Because you are working from one big happy solution, you will be able to press the discover button and get your service. Add it and give it a nice name.

image

This will add a proxy to your Silverlight client.

Intermezzo:
People that have read this blog for some time, know I'm not very impressed with all the autogenerated proxy features. I do not feel generating your proxy gives you enough power over what you are sending over the wire. Also, I might want to do crazy stuff in my proxy. However, Silverlights data capabilities are pretty restricted and I will admit that the VS team has done some great integration here. All in all, this runs out of the box without any hickups.

Read my articles about EntityFramework Poco implementations which does a lot of WCF customization if you want to know more.

Step 2.1: test it out, man!

Let's just quickly see if we can get our service to work! Add these lines of code in your page.xaml.cs file and run.

image

This should print out the number 7 in your output.

We are first setting up a callback that will be called when the webservice returns with information. The args.Result property is a strongly typed property which holds your result.
We kick of the call to the webservice with our proxy.GetFriendsAsync() call.

Step 3: bring in the autocomplete textbox

It is time to add the autocomplete textbox in the mix.

Add a reference to the silverlight toolkit to your project, and (do) use the 'controls' prefix as the xmlns namespace. Then, just add it to the page xaml.

image

I have added the box and have added an eventhandler to the Populating event. This will allow me intercept and call our webservice. Dropping back to code, we'll hook everything up:

image

On line 33, I'm cancelling the population. Populating should really be done synchronously and fast. Since we can't do it fast, we will be doing the populating ourselves.
On line 50, we make the call to our webservice. When it is finished, it will execute the anonymous delegate on lines 39 to 46.
Our args.Result is a string[] with friends from the server. I will combine that with a list of friends we might keep in a different source.

The result:

image

 

Check out the LiveSearch scenario sample that comes with the toolkit. It connects to livesearch and fetches search suggestions on the fly.

AutoCompleteBoxToWebserviceProject.zip (159.35 KB)
Tuesday, 28 October 2008 23:33:41 (Romance Standard Time, UTC+01:00)  #    Comments [28]  |  Trackback

When you are browsing the charting samples here, you will notice a scenario that is currently called 'Zoom'.

unzoomedChart

I think it's pretty clear that you are supposed to drag the zoom slider :)

When you do, the chart is resized and to look like this:

ZoomedChart

Charts do not have this capability built-in at the moment, so this gives us the opportunity to dig into the charts a bit.

All charts are of the same type (Chart) and you can create a line series by adding an instance of LineSeries to the chart. This is quite flexible, because you can add as many as you want. It our case, two are added. Let's take a look at the different controls that make up this sample:

Zoomchart xaml

As you can see, two LineSeries have been added, their ItemsSource pointing to a static resource defined somewhere else.

The slider is hooked up to a method in my codebehind which executes this one line of code:

            ChartArea.Width = ScrollArea.ViewportWidth + (ScrollArea.ViewportWidth * zoom / 100.0);

I am not setting the width of the complete chart. If I were to do that, the legend and title would scroll away as well, which is not what I want. Besides, there is no ScrollViewer around to handle the scrolling. Lets take a look at the custom template that does have a ScrollViewer in it:

image

I have retemplated the chart to include a ScrollViewer.

You can clearly see that the legend is not in the ChartArea. So, setting the width of the ChartArea will give you the zoom you are aiming for. The axis get notified of the change and (if set to 'Auto'), will update to show more or less information.

Since Chart is a sealed class, I had to walk the visual tree to get to both the ChartArea component and the ScrollViewer.

Tuesday, 28 October 2008 20:48:51 (Romance Standard Time, UTC+01:00)  #    Comments [16]  |  Trackback

Today, Scott Guthrie announced our teams product: the Silverlight Toolkit. My boss, Shawn Burke just posted a blog that lists all the different controls that are included and gives out information about the quality levels of the controls. It's a must-read.

The components included in this release are:

Components in the Preview Quality Band

Components in the Stable Quality Band

I joined only a few weeks ago and have been looking at the controls. What is most impressive, is the incredible amount of unittests that are included with this release. On the team is Jeff Wilcox who heads the Silverlight Unit Test Framework.

I have been playing around with the controls and will probably be blogging some short blobs about my findings. I've spend quite some time with charting.

David Anson (Delay) heads up the charting control, which is the biggest feature that we are including. He has just announced it with a very detailed introduction post. He is also announcing the ChartBuilder application, that will make it easy for you to play around with the settings.

Go download and play, let us know what you think!

Tuesday, 28 October 2008 19:18:40 (Romance Standard Time, UTC+01:00)  #    Comments [7]  |  Trackback
 Sunday, 19 October 2008

I’m very excited that Silverlight 2 has been released. I’m too new to Microsoft to claim even the slightest involvement, but it’s wonderful to see the excitement both within Microsoft as outside.
It will be interesting to see what will happen with the (already great) uptake of Silverlight by the market.

As I am spending most of my days knee-deep in Xaml nowadays, I always try to find things that will help me be more productive. It happens quite often that I want to select a complete xaml tag. It’s way too much effort to use the mouse to select it, so I often use the control-m-m shortcut to collapse a tag and then select it. However, a few days ago I took 5 minutes to automate this.

image

When you put your mouse somewhere in the Grid tag, and use my macro, you end up with this:

image

You can even put your mouse in the endtag. I bound it to Control-Q and it has made my life that much better!

The macro is as simple as this:

    Sub SelectXMLTagContents()
        DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstColumn)
        DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
        DTE.ActiveDocument.Selection.StartOfLine(VsStartOfLineOptions.VsStartOfLineOptionsFirstColumn)
        DTE.ActiveDocument.Selection.EndOfLine(True)
        DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
End Sub

It collapses a tag, jumps to the first column, selects the line and then does an uncollapse.

For those using a tool like Karl’s ‘Xaml Power Toys’, it might also be a worthwile addition to their shortcuts.

Sunday, 19 October 2008 21:17:36 (Romance Standard Time, UTC+01:00)  #    Comments [26]  |  Trackback
 Friday, 03 October 2008

I’ll slowly try to start blogging again :)  It’s been extremely hectic, and I am slowly ramping up. Lately I’ve not been blogging or been just scribbling non-technical jibberish here. Let’s get back on the train!

Loading assemblies in Silverlight can be a bit hard because everything is packaged up in a Xap file. If you want to get to all the assemblies and start instantiating types, you’ll quickly run into a wall.

However, Windows is about living without walls, and thus there is a workaround! :)

  0    string assemblyname = "System.Windows.Controls";
  1
  2    AssemblyPart part = Deployment.Current.Parts.SingleOrDefault(asmpart => asmpart.GetValue(FrameworkElement.NameProperty).ToString() == assemblyname);
  3
  4    StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));
  5    Assembly asm = part.Load(streamInfo.Stream);
  6

It appears there is a very nice class available: Deployment. It is a list of the deployment section of your App.Manifest. The manifest describes all the ‘DeploymentParts’ in the Xap file. It thus also describes the filenames of the dll’s that are included.

If you know the dll name, that is great, and you can easily load it; skip to line 4.
In my case, I just had the assembly name (which does not mean I have the dll name) and on line 2 you can see me running through the parts that are available to me. The x:Name in the deployment file is the name of my assembly, so I get to it by using the DependencyProperty system: querying for the FrameworkElement.NameProperty.

Once I have an assembly part, I have the actual filename: part.Source. So, on line 4, I can now setup a stream to it, and on line 5 I can easily load my assembly.

From this point on, I can do everything you would expect.

Happy sailing!

Friday, 03 October 2008 07:55:38 (Romance Standard Time, UTC+01:00)  #    Comments [12]  |  Trackback
 Wednesday, 27 August 2008

[intended public is not the silverlight guru’s, but rather my friends and others that do not understand the coming of a new web :) ]

As I am preparing for our adventure in Canada, I’m meeting up with loads of old friends to have ‘one last beer’. That always seems to take place in my local Irish Pub, where I have spent way too many nights drinking their lovely Irish Red beer. I prefer it to Guinness, although it was quite nice to visit the Guinness factory in Dublin…

But I digress.

The discussion of the evening always, at some point in time, touches on Silverlight and how I think the web will change because of it. It’s quite a good feeling for me to see that I get all jazzed up talking about it :)
The current reigning web technology is obviously html. It has been king ever since the first webbrowser was introduced and for good reason. It is multi-platform, fast, easy to deploy and well-understood. Most importantly, it is stateless.

However, it can not compete against a desktop application when judged on UI richness and interactivity. Now, I am not talking about a simple mail-client or an rss-reader, but I’m talking about a big client application where quite a bit of information is processed. Is a stateless architecture appropriate for such an application?
No, it is not.

To be more exact: a large part of the application would be best to run on the client, where it can cache data and do processing. That application could (should) still talk to a back-end in a stateless manner.

The html-world has been working hard to ‘fake’ interactivity and has done so remarkably well. However, they will always be fighting against a technology which just wasn’t created to support the scenario’s they are trying to accomplish.

With the introduction of AIR, Flex and Silverlight 2, the kind of scenario’s I am envisioning are becoming a real possibility. It is now possible to create an application that is as rich as a desktop application, without all the hassle of deployment.
But, and this is what amazes me most, many of my friends don’t ‘get’ it. Ouch!! They fail to see how a RIA could do much better than a html based application. It is curious to me how we now all have a powerful desktop computer, and are still using it as a terminal. And even liking it!!

There are things html is perfect for: bringing text and even images in a nice layout. But that’s about it. Asp.Net, Ruby, Php and the lot, are all trying to add programmability to html. Since that is not what html is designed for, they have to process on the server. This model is slow and wasteful.

The only way it seems to really show people how a different web could look like, take a look at the work of thirteen23. Here they show a few different designs of how facebook could look like. It only shows off some nice visuals, so take a look at the incredible photosynth application.
My all time favorite in showing people what the world could look like is still the microsoft health patient journey demonstrator. If that doesn’t make it ‘click’ for you, check out another demo of woodgrove financial or a different way of browsing amazon.

The next few months or years, html will still be king. But it is inevitable that the web will transition towards the richness the new technologies are able to offer. I’m looking forward to seeing that happen and I hope that the current batch of html/ruby/asp.net/jscript/whatever developers are not missing out on the incredible opportunities it presents.

Wednesday, 27 August 2008 13:36:18 (Romance Standard Time, UTC+01:00)  #    Comments [8]  |  Trackback
 Thursday, 21 August 2008

I follow blogs about SL with quite some interest and found this site: http://riastats.com/ . It basically tracks browsers to see if Flash and Silverlight is installed. You can clearly see that SL has a long way to go, but I was still amazed to see that SL had almost 25% share already.

Looking at the line graph, you can see that in the last week, SL 2 market share has grown extremely rapidly. That makes this post by John Dowdell (from Adobe) outdated, since he was only seeing a 2% share at the moment of writing. The mere fact that he uses these numbers, lend some kind of credibility.

I’m very interested in seeing the statistics from some other sites. Keep in mind that riastats base these numbers on only 18,600 unique browsers and do not disclose which sites are used (for all we know, they are Silverlight blogs :) ).
When numbers seem too good to be true, they usually are.

In any case though, I will keep my eye on the statistics the next few weeks!

 

ps. if you know of other statistics sites that show SL reach, leave a comment.

Thursday, 21 August 2008 22:14:21 (Romance Standard Time, UTC+01:00)  #    Comments [21]  |  Trackback
 Friday, 25 July 2008

I started my career when I was still in university. I started a company named Sitechno that did webapplications and custom solutions. The last few years, I’ve been hired as a consultant on some big projects.
The role I was given on my last project, allowed me to do some crazy cool stuff, using nHibernate and winforms. I was able to push WCF, WF and WPF into a big client/server application, and had great success with it. We reformed a monolithic data-oriented application into a domain-oriented loosely coupled application.

It’s easy enough to find new projects, but the market for the cutting edge technologies is not very big ;-)

When I was asked to join Microsoft, my main attraction to the offer was the opportunity to work on cutting edge technology with a group of passionate people. When I talked Microsofties on the Redmond campus, it became clear that they could certainly offer just that.

I was torn between working on EF (which, I’ve been very involved with lately) and on Silverlight.
In the end, Silverlight won, because I strongly believe it to be the strongest contender in the client space, there is an interesting ‘war’ going on right now, and I have really enjoyed working with the WPF-framework in the past. I was torn by the decision because whatever choice I made, I knew that a great opportunity would be lost.

We will be heading to Vancouver, Canada in September and I will work for Microsoft Canada for a year. After that, I will be allowed to work in the U.S.A. and we will move to Redmond.

You can not believe how excited I am about this opportunity. I will be working for Shawn Burke on Silverlight Controls. I’m not sure yet who else is working on the team, but I believe/hope I’ll be working with David, Ted, Kirti and Jeff.

As for EFContrib: although I’m dedicated to it, I’m having a hard time finding the time to work on it. Also, since version 2.0 of EF will feature some nice Poco capabilities. So, I’m not sure if people are waiting for the solution. It would be nice to make it work with SL though! So, if I find the time, or get lots of mail of people wanting me to finish it properly, I’ll work on it some more.

Friday, 25 July 2008 09:46:13 (Romance Standard Time, UTC+01:00)  #    Comments [15]  |  Trackback
 Friday, 20 June 2008

Well, I wrote about one way to do deeplinking and knew that there had to be something out there that already did this.

Look no further. It’s here!

Friday, 20 June 2008 23:11:52 (Romance Standard Time, UTC+01:00)  #    Comments [8]  |  Trackback
 Wednesday, 18 June 2008

Ninject is a lightweight dependency injection framework and it has been getting quite a bit of attention lately.
What makes is superspecial, is that it runs under Silverlight 2.0 beta 2!

The Ninject website is here and Nate Kohari talks about this release here. From his post:

So, what are some of the features of Ninject 1.0?

  • Constructor, property, method, and field injection
  • Instantiation behaviors (singleton, one-per-thread, one-per-request)
  • Fluent interface for declaring type bindings
  • Contextual bindings, where the selection of which type to instantiate can be delayed until activation
  • Support for instance scope and deterministic disposal
  • Fully pluggable, modular design: each kernel component can be easily replaced to alter the framework’s behavior
  • Lightweight interceptor support (aspect-oriented programming)
  • Integrations with other popular frameworks

It’s great to see people working on MVC approaches and now even DI frameworks for SL. Keep it coming!

Wednesday, 18 June 2008 11:42:42 (Romance Standard Time, UTC+01:00)  #    Comments [6]  |  Trackback

Nihkil wrote sometime ago about ‘actions’, which was a concept he created that attach some behavior to elements. Think of the action of pressing the ‘enter’ key in a textbutton and executing some logic (sparing the user a click on the button next to the textbutton).

He has clearly been thinking more about his framework and now posts about the MVC concept of the ViewModel, extending it with his actions here. Good write-up. Go read it.

Basically, he explains what a ViewModel is and shows how to execute ‘command’-like behavior on them. As you might know, Silverlight does not support the concepts of commands, like WPF does. His syntax is like this:

 

  1 <vm:View xmlns="..." xmlns:x="..."
  2   xmlns:vm="clr-namespace:Silverlight.FX.ViewModel;assembly=Silverlight.FX"
  3   xmlns:app="clr-namespace:AmazonSearch.Views">
  4   <vm:View.Model>
  5     <app:SearchViewModel />
  6   </vm:View.Model>
  7 
  8   <Grid>
  9     <TextBox x:Name="searchTextBox" />
 10     <Button Content="Search" IsEnabled="{Binding CanSearch}">
 11       <vm:ButtonEvents.Click>
 12         <vm:InvokeMethod MethodName="Search">
 13           <vm:ElementParameter ElementName="searchTextBox" ElementProperty="Text" />
 14         </vm:InvokeMethod>
 15       </vm:ButtonEvents.Click>
 16     </Button>
 17 
 18     <ItemsControl ItemsSource="{Binding Products}">
 19       <ItemsControl.ItemTemplate> 
 20         <DataTemplate><app:ProductView /></DataTemplate>
 21       </ItemsControl.ItemTemplate>
 22     </ItemsControl>
 23   </Grid>
 24 </vm:View>
25

The thing to note is in lines 10 to 15. There he invokes a method straight on the ViewModel.

The concept is quite the same as the Caliburn project from Rob and the idea is very powerful!
It enables you to have no code-behind and go straight to the viewmodel. Very nice!

Thinking about this a bit though, does make me wonder if the added Xaml is worth the ability to go straight to the viewmodel. Wouldn't it be just as simple to define a Search method on the codebehind and invoke the correct method in the viewmodel?

My point being: is it worth to incorporate such a framework (albeit very small and succinct). What do you think?

Wednesday, 18 June 2008 10:04:08 (Romance Standard Time, UTC+01:00)  #    Comments [8]  |  Trackback
 Friday, 13 June 2008

I’ve been busy sharpening my Silverlight skills and created my very own button, complete with VSM.

The button (‘rjButton’) inherits from contentControl and in the project there is a generic.xaml which defines a default look for the button, including VSM definitions and state transitions.
When loading up my solution in Blend, I can happily get the rjButton in my page. It looks correct. However, when I ‘Edit a copy’ of the template, nothing shows up!! Funnily enough, when I use ‘Create empty’, the correct states do show up.

I was unable to google-fix the problem. I would guess that when you use ‘create empty’, Blend just looks at the attributes on your class and shows the correct states. When you ‘Edit a copy’, Blend looks for the template and can not find any. Somehow it is unaware of the link to the style in generic.xaml, and it creates an empty style.

I’ve double checked the behavior with the TabControl, which is also a control that is defined in an external control, and it works the same.
I'm not sure why controls included in the framework do work correctly. I've taken a look with reflector and I've seen nothing out of the ordinary.

You can work around this by naming the style and explicitly tell the control to use the style. But then again, it might be easier to just copy the style (use David's excellent style browser here).

If you know what is going on, leave a comment!

[update: jasonxz knows: I was informed, today, that Blend does not, yet, support the "edit a copy" functionality of a Template that is not defined in System.Windows.dll. Thnx! ]

Friday, 13 June 2008 17:45:21 (Romance Standard Time, UTC+01:00)  #    Comments [4]  |  Trackback

I just spent way too much time figuring out why my VisualStateManager suddenly started giving errors. Without ‘break on all exceptions’ I got a ‘Catastrophic error’. With breaking on all exceptions I could finally see that the layout system was trying to layout the visualstatemanager.

I basically had specified my vsm directly under the controltemplate, instead of defining it inside the grid. Whoops!

Friday, 13 June 2008 15:01:00 (Romance Standard Time, UTC+01:00)  #    Comments [5]  |  Trackback
 Wednesday, 11 June 2008

I was triggered to spend some time on thinking about deeplinking, when I was listening to a podcast with Neal Ford, here.
In it, he has quite a few arguments why something like Silverlight (and Flex for that matter) will never amount to anything. I disagree with almost all of his points, but one stuck with me more: the inability to do deeplinking.

Let me start off by saying that I think the ‘normal’ html world has reached maturity and should soon begin to disappear. As almost everything in the world, technology adoption follows a wave pattern. Html has had some great advantages which made it easy to adopt. However, as computing power increases and humans become more accustomed to great applications, html as a platform does not deliver anymore. It was rejuvenated by the Ajax revolution, but in the end, you are always fighting against the restrictions that it imposes.

Those restrictions are no longer necessary. Flash did not deliver as a RIA platform, but Flex seems very capable, as does Silverlight. It will take a few years, but the demand for rich clients will drive the adoption of said technologies.

Being able to do deeplinking, is an important part of the experience and usability. The more I thought about it, the more I agreed on this. Although I think that browsers (as we know them) will cease to exist pretty soon or more likely: change to meet new needs, for the next few years we will have to deal with the restrictions they impose.
So, I started to think about how we can solve the deeplinking issue for Silverlight.

I had a flash of brilliance when I thought of the achor (#) sign in url’s. They can be navigated to, but the page is not reloaded. Exactly what was needed for deeplinking.
After doing some fast research, I discovered half the world had already thought of this trick and it is heavily used by the Flex world.
That discovery ruined my chances to score some ‘smart'-points with my girlfriend, but it won't keep me from implementing my idea ;-)

The basic idea

The idea is to get to the url at the beginning of the application and treat everything after the # sign as 'state'. This state is used by the components in our application to initialize themselves to the correct state. For instance, show the help page, or the products page.
When a component is changed and it is important enough to reflect this change in the url, we use javascript to navigate to the new url. The browser will recognize that only the anchor has changed, and will not really browse there. However, the user can now confidently copy the url.

It's pretty easy.

Two approaches

Using a navigationcontroller.
Your application could instantiate one navigationcontroller. This controller would be used inside your application to show different usercontrols etcetera. I think such an approach is well suited for a MVC-style of application architecture.
Obviously, since all your navigation is taken care of by a central component, it would be very easy to take care of 'serializing' its state to the url.

Using per component approach.
If you're just not that kind of person, you could also just make the components you care about do the (de)serializing. So, possibly react to the Loaded event of a component and reading the url to see how to initialize it.
I've taken this a step further and created a class that serializes the vertical offset of a Scrollviewer. It can be used like so:

  1         <ScrollViewer x:Name="sv1" VerticalScrollBarVisibility="Auto" Margin="10">
  2             <nav:ScrollviewerOffsetStateAttacher.Register>
  3                 <nav:ScrollviewerOffsetState TemplateName="HelpTemplate" PartName="pos1" />
  4             </nav:ScrollviewerOffsetStateAttacher.Register>
  5         <TextBlock TextWrapping="Wrap">   
6 Lorem ipsum dolor sit amet, conse

(pretty radical, isn't it. Here I'm serializing the amount of scrolled pixels to the url. That's something else than just which page you are viewing!)

Some more code

First off, you need to think about how the url is serialized. Some kind of template needs to be known to the system. And, different templates should be available for different parts of your application. So, during initialization of my application I do this:

            NavigationState.RegisterTemplate("MainNavigationTemplate", "mainpage");
            NavigationState.RegisterTemplate("ProductTemplate", "mainpage/product/detailpos");
            NavigationState.RegisterTemplate("HelpTemplate", "mainpage/pos1/pos2");

A better approach might be to supply regex expressions.

As you have figured out by now, I'm using a static class 'NavigationState' to manage my url fiddling. It contains a SetNamedPartState and a GetNamedPartState method. So when I navigate to the Products page, I might do this:

appController.NavigateToPage(typeof(Products));
NavigationState.SetNamedPartState("mainpage", "ProductTemplate", "products");


I'm telling it to use the producttemplate and set the mainpage part of that template to the state 'products'.
During the load though, I do the opposite:

string state = NavigationState.GetNamedPartState("mainpage", "MainNavigationTemplate");
if (!String.IsNullOrEmpty(state))
{
    if (state == "products")
        ProductsSelected();
    else if (state == "help")
        HelpSelected();
}

Getting the url is easy:
string uri = HtmlPage.Document.DocumentUri.ToString();

Setting it is only slightly harder:
HtmlPage.Window.Eval(String.Format("window.navigate('{0}#{1}');",
    NavigationApplicationString, InternalStateString));

Conclusion, work ahead

It's pretty a pretty simple idea. I like the way I attached a 'state' object to a scrollviewer control to make a no-code, designer friendly experience possible.

If someone would get serious about this, I guess they would take a look at the asp.net mvc approach. They also hook the url's you are setting, to the back button(!) So, there should be good crossbrowser javascript available.

Also, a good and flexible url parsing engine is necessary.

Finally, when you take the approach of one controller doing all the hard work, nothing much has to be done. However, I can see the creation of all kinds of specific classes that can be attached to controls to make them 'url-persistable' (think of selecting the right listbox item).

Try it out

The homepage of our sample application:
http://www.sitechno.com/silverlight/deeplinking/deeplinkingtestpage.html

Two url's with state in them:
http://www.sitechno.com/silverlight/deeplinking/deeplinkingtestpage.html#products/SqlServer/760
http://www.sitechno.com/silverlight/deeplinking/deeplinkingtestpage.html#help/309/1166

I will not keep this application available forever and probably not update it to the release version of beta2.

 

Who is going to make themselves useful by creating a robust solution? Leave a comment of mail me if you want the source code!

Wednesday, 11 June 2008 20:48:01 (Romance Standard Time, UTC+01:00)  #    Comments [85]  |  Trackback
 Thursday, 05 June 2008

Okay, I've been busy with other stuff in my life, I'll blog about soon. Suffice it to say, I'm going to be able to focus my time more on Silverlight now ;-)

The Silverlight team is announcing beta 2 with this post. It's a pretty long list with changes, and it's looking very good! Here are a few of my favorites:

  • They included some controls into the runtime. So more goodies without added download size.
  • A visual tree helper. I'm looking for more information on this. As you know, SL does not have the same concept of a seperate visual tree and logical tree as WPF does. They instead opted to not differentiate. Does this helper mean that the two models come closer to each other?
  • Per binding level validation.
  • Binding to attached properties! Yay!
  • OnItemsChanged method
  • Fallback value during conversion
  • Duplex communication. Oh dear, they really hit the sweet spot here. This allows you to push data to the client during a call.

The big one for me is support for UIAutomation though. I'm not sure if it is supported fully, but this means you can test your controls with actual 'human'-like input. I think this is important when building an application with a late-bound system like Xaml-binding.

The whole blogosphere is raving about the new VisualStateManager. It is a highlevel api to work with your states. The blend team is able to give you a much improved templating story because of it. Read about it here or here.

Thursday, 05 June 2008 11:14:48 (Romance Standard Time, UTC+01:00)  #    Comments [2]  |  Trackback
 Wednesday, 05 March 2008

Finally, very exciting!! Scott released it at Mix 08.

Go get it here.

It seems they have released an Expression Blend version to match. Cool!
Also 2000 thousand unittests are released.

Wednesday, 05 March 2008 21:33:26 (Romance Standard Time, UTC+01:00)  #    Comments [0]  |  Trackback