Tag: Silverlight 3.0

  • Silverlight Tab Stops – IsTabStop

    Just found a tricky little quirk with TabStop that caused a fair bit of confusion. It turns out that the default implementation of ContentControl overrides Control, so implements the IsTabStop property, and defaults that value to true.

    ContentControl is implemented by the delightful BusyIndicator, so if you have used a BusyIndicator within the scope of a series of input controls you’ll see the Invisible Tab Stop behaviour.

    HINT: Using Silverlight Spy shows us where the offending property lies. You’ll need to re-template that to set the IsTabStop property to false.

  • Silverlight Commands – Data grid row selected

    following on from http://thoughtjelly.blogspot.com/2009/09/silverlight-prism-commands-textchanged.html and in response to John Papa’s PDC talk http://johnpapa.net/silverlight/mvvm-and-prism-demo-for-pdc09-silverlight-session. Another highly useful command behaviour is for DataGridRowSelected.
    This also gets over the issues described here and here. The code (as written by John Papa) is:

    public class DataGridRowSelectedCommandBehavior : CommandBehaviorBase
    {
    public DataGridRowSelectedCommandBehavior(DataGrid selectableObject)
    : base(selectableObject)
    {
    selectableObject.SelectionChanged += OnSelectionChanged;
    }

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    this.CommandParameter = this.TargetObject.SelectedItem;
    ExecuteCommand();
    }
    }

    And

    public static class DataGridRowSelected
    {
    private static readonly DependencyProperty DataGridRowSelectedCommandBehaviorProperty = DependencyProperty.RegisterAttached(
    "SelectedCommandBehavior",
    typeof(DataGridRowSelectedCommandBehavior),
    typeof(DataGridRowSelected),
    null);

    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
    "Command",
    typeof(ICommand),
    typeof(DataGridRowSelected),
    new PropertyMetadata(OnSetCommandCallback));

    public static void SetCommand(DataGrid dataGrid, ICommand command)
    {
    dataGrid.SetValue(CommandProperty, command);
    }

    public static ICommand GetCommand(DataGrid dataGrid)
    {
    return dataGrid.GetValue(CommandProperty) as ICommand;
    }

    private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
    var dataGrid = dependencyObject as DataGrid;
    if (dataGrid != null)
    GetOrCreateBehavior(dataGrid).Command = e.NewValue as ICommand;
    }

    private static DataGridRowSelectedCommandBehavior GetOrCreateBehavior(DataGrid dataGrid)
    {
    var behavior = dataGrid.GetValue(DataGridRowSelectedCommandBehaviorProperty) as DataGridRowSelectedCommandBehavior;
    if (behavior == null)
    {
    behavior = new DataGridRowSelectedCommandBehavior(dataGrid);
    dataGrid.SetValue(DataGridRowSelectedCommandBehaviorProperty, behavior);
    }

    return behavior;
    }
    }
  • Data-binding Radio Buttons in Silverlight to Enum

    In Silverlight there is no Enum.Getvalue() method, so there is not out-of-the box way of databinding a radio buttongroup to an enumerable value in a viewmodel.

    Step forward converters.

    using System;
    using System.Windows.Data;
    namespace YourNamespace
    { public class EnumBoolConverter : IValueConverter
    { #region Methods
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    { if (value == null || parameter == null)
    return value;
    return value.ToString() == parameter.ToString();
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
    if (value == null || parameter == null)
    return value;
    return Enum.Parse(targetType, (String)parameter, true);
    }
    #endregion Methods
    }
    }

    With the previous convertor it is then quite simple to bind the radio button list. Simply pass the Enum value (as a string) as the converter parameter, and you are away:

    <UserControl
    ...>

    <YourNamespace:EnumBoolConverter
    x:Key="ConvertEnum" />


    <RadioButton
    Content="Yes"
    GroupName="Group1"
    IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Yes}" />
    <RadioButton
    Content="No"
    GroupName="Group2"
    IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=No}" />
    <RadioButton
    Content="Both"
    GroupName="Group3"
    IsChecked="{Binding Path=UserOption, Mode=TwoWay, Converter={StaticResource ConvertEnum}, ConverterParameter=Both}" />



    Also make sure the GroupName on each radio button is different or the system gets monumentally confused and throws a StackOverflowException.

  • Date issues in Silverlight

    Seeing some strange date behaviours in SL UI components. My local culture is UK but we keep seeing US dates popping up “here and there”.

    Have tried setting the Language= “en-GB” in the upper most control, also setting the thread culture is not effecting across the app.

    Going to have to get help on this one…

  • Restyling Transitions in the Silverlight Toolkit TransitioningContentControl

    (This post extends my example for using cross project merged dictionaries in Silverlight)

    I’m a developer, not a designer and as such using Expression Blend doesn’t feel natural and makes me feel dirty… However I had to bite the bullet to ease the restyling of the transition animation in the Toolkit TransitioningContentControl.

    NOTE: At time of writing this control is in the Experimental Quality Band – the way I have built this might not work in the future!!

    To edit the transition in blend I added a TCC to my page, right clicked and chose “Edit Template Edit a Copy… Define in Application

    You can then start editing the VisualStates. There is tons of information on how to do this, so I won’t repeat that here, but here are the basics:

    Select States tab, Choose a transition to edit (or add a new one) expand the objects and make changes to the states along the time line.

    After making changes to your template open it and examine the changes. Most stuff is pretty self explanatory. Once this is done I can close blend and copy the XAML in to Visual Studio – now I feel safe and clean!!

    So, I wanted to add a flip transition, that makes the page look like it is doing a 360 flip around a vertical axis. First thing to do is edit the content template to include project planes, as these are hooked from my new animation (lines 17-19 and 37-39 below). You won’t need to do this is you are changing basic animation properties (offset, opacity, scale etc):


    <ContentPresenter x:Name="PreviousContentPresentationSite"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    Content="{x:Null}"
    ContentTemplate="{TemplateBinding ContentTemplate}">















    <ContentPresenter x:Name="CurrentContentPresentationSite"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    Content="{x:Null}"
    ContentTemplate="{TemplateBinding ContentTemplate}">














    Then add my flip animation (lines 53-74 below):

    <Style x:Key="TransitioningContentControlStyle1"
    TargetType="layoutToolkit:TransitioningContentControl">
    <Setter Property="IsTabStop"
    Value="True" />
    <Setter Property="HorizontalContentAlignment"
    Value="Left" />
    <Setter Property="VerticalContentAlignment"
    Value="Top" />
    <Setter Property="Transition"
    Value="DefaultTransition" />



    <Border Background="{TemplateBinding Background}"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}"
    CornerRadius="2">




    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="0" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="1" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="1" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="0" />





    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Visibility)">


    Collapsed







    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)">
    <EasingDoubleKeyFrame KeyTime="00:00:00"
    Value="0" />
    <EasingDoubleKeyFrame KeyTime="00:00:00.3000000"
    Value="90" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)">
    <EasingDoubleKeyFrame KeyTime="00:00:00"
    Value="-90" />
    <EasingDoubleKeyFrame KeyTime="00:00:00.3000000"
    Value="-90" />
    <EasingDoubleKeyFrame KeyTime="00:00:00.6000000"
    Value="0" />






    <ContentPresenter x:Name="PreviousContentPresentationSite"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    Content="{x:Null}"
    ContentTemplate="{TemplateBinding ContentTemplate}">















    <ContentPresenter x:Name="CurrentContentPresentationSite"
    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
    Content="{x:Null}"
    ContentTemplate="{TemplateBinding ContentTemplate}">



















    NOTE: I have removed the DefaultTransition, UpTransition and DownTransition VisualStates from this code as I am not using them. Normal MUST remain. If you want these transition states back they are copied below for your convenience.

    Finally I copied this style to a separate file so it can be referenced as a MergedDictionary across a multi project solution (as described here).

    When doing this, remember to Add a reference to System.Windows.Controls.Toolkit and System.Windows.Controls.Layout.Toolkit from the resources project, and in the resource xaml file:

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

    DefaultTransition, UpTransition and DownTransition Visual States



    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="0" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="1" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="1" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="0" />





    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="0" />
    <EasingDoubleKeyFrame KeyTime="00:00:00.7330000"
    Value="0" />
    <EasingDoubleKeyFrame KeyTime="00:00:01.1000000"
    Value="1" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="1" />
    <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
    Value="1" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.9000000"
    Value="0" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)">
    <EasingDoubleKeyFrame KeyTime="00:00:00"
    Value="0">




    <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
    Value="0">




    <EasingDoubleKeyFrame KeyTime="00:00:00.9000000"
    Value="0">





    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
    <EasingDoubleKeyFrame KeyTime="00:00:00.7330000"
    Value="0" />
    <EasingDoubleKeyFrame KeyTime="00:00:01.1000000"
    Value="1" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
    <EasingDoubleKeyFrame KeyTime="00:00:00.7330000"
    Value="0" />
    <EasingDoubleKeyFrame KeyTime="00:00:01.1000000"
    Value="1" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
    <EasingDoubleKeyFrame KeyTime="00:00:00"
    Value="1">




    <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
    Value="0.75">




    <EasingDoubleKeyFrame KeyTime="00:00:00.9000000"
    Value="1">





    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
    <EasingDoubleKeyFrame KeyTime="00:00:00"
    Value="1">




    <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
    Value="0.75">




    <EasingDoubleKeyFrame KeyTime="00:00:00.9000000"
    Value="1">





    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
    <EasingDoubleKeyFrame KeyTime="00:00:00.5000000"
    Value="0">




    <EasingDoubleKeyFrame KeyTime="00:00:00.9000000"
    Value="-500">





    <PointAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)">
    <EasingPointKeyFrame KeyTime="00:00:00"
    Value="0.5,0.5" />
    <EasingPointKeyFrame KeyTime="00:00:00.5000000"
    Value="0.5,0.5" />

    <PointAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)">
    <EasingPointKeyFrame KeyTime="00:00:00"
    Value="0.5,0.5" />
    <EasingPointKeyFrame KeyTime="00:00:00.7330000"
    Value="0.5,0.5" />
    <EasingPointKeyFrame KeyTime="00:00:01.1000000"
    Value="0.5,0.5" />





    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="CurrentContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="0" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="1" />

    <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.300"
    Value="0" />

    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
    Storyboard.TargetName="PreviousContentPresentationSite"
    Storyboard.TargetProperty="(UIElement.Opacity)">
    <SplineDoubleKeyFrame KeyTime="00:00:00"
    Value="1" />
    <SplineDoubleKeyFrame KeyTime="00:00:00.300"
    Value="0" />

    <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.300"
    Value="40" />


    Note: UpTransition has been altered to do a funky “drop-back-swing-left-and-fade-in” animation

    Example

    The obligatory example is here…

  • Cross Project MergedDictionaries in Silverlight

    Todd Miranda has posted a video on the use of merged dictionaries on the Learning portal of Silverlight.net (find it here). This video explains the concepts of MergedDictionaries in a very long winded way. IMHO people investigating MergedDictionaries will already know how to layout a grid, and play some simple controls, so the first 8m of the tutorial is pretty boring.

    The description of the use of MergedDictionaries is pretty good, along with a couple of common pitfalls, but this does not explain one core feature well enough: Cross Project

    Anyone writing a Silverlight LOB application is almost certainly going to have multiple projects, and the use of MergedDictionaries corss project is going to be a huge advantage.

    Luckily this isn’t much more difficult than Todd has shown. Imagine the following scenario; we have a bunch of silverlight pages that need to share styles, but are spread across a few different projects. We want to keep the styles (as resources) in one location and reference these.

    So simply create a Resources project, and add your resources files as described by Todd (Add | New Item… | XML file etc) (you can also add these to any sensible location in your existing solution – say a UserControls project)

    Then when referencing the MergedDictionary from the UserControl where the style will be applied, use the Assembly name of the resources location, rather than the assembly name of the UserControl location.

    So the Source value will be formatted as Source=”/[AssemblyName];component/[Path and filename]

    Don’t forget to add a reference to the Resources project from the Target Project.

    Further details and and example of this in action with a re-styled TransitioningContentControl can be found here: http://thoughtjelly.blogspot.com/2009/10/restyling-transitions-in-silverlight.html.