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
 Saturday, 28 March 2009

Completely unrelated to anything on this site, I wanted to talk about IE8 for a minute. It looks like a great browser, but on my home machine, just spinning up a tab took about 3 seconds. When I noticed that other people did not have this problem, I started to investigate. These are the two tips that I’ve found:

The first is by Ed Bot and basically tells you to execute ‘regsvr32 actxprxy.dll’ from an elevated command prompt. That registers a dll that in certain configurations is not registered. It can make a huge difference.

The second on is the the one that saved me. I don’t know the source, but there are several blogs telling you to look at your restricted sites (Tools/InternetOptions/Security/Restricted Sites, press the button labeled ‘sites’ ).
I had used spybot long ago, which had added a huge list of sites there. Apparently this can really slow down outlook and ie!
Remove them by resetting your settings to default (Advanced tab, button labeled ‘reset’).

Now I have a blindingly fast internet explorer, and I must admit, I do love this browser now. Switching back!

Saturday, 28 March 2009 23:08:28 (Romance Standard Time, UTC+01:00)  #    Comments [29]  |  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