Thursday, May 3, 2012

Customize DomainUpDown control in Silverlight

Introduction

DomainUpDown control is silverlight toolkit control, this control allows user to spin(up/down) through collection of objects.

To work with domainupdown control, first you have to download silverlight latest toolkit from Silverlight Toolkit  and install.
In this post we walk-through how to change appearance of  DomainUpDown control.

Walk-through step by step

you can find DoaminUpDown control from System.Windows.Controls.Input.Toolkit. So first add reference of System.Windows.Controls and System.Windows.Controls.Input.Toolkit.


I have customize DomainUpDown control as shown in above snap as i am binding ImageList with name as ItemsSource to the control.

Step 1: First Start with Data,Create XML file that contains data collections

<?xml version="1.0" encoding="utf-8" ?>
<root>
  <natures>
    <natureview id = "1" title = "Mountain" 
      imagesource="/DomainUpDownCustomization;component/Images/Mountain.jpg" />
    <natureview id = "2" title = "Sunset"  
      imagesource="/DomainUpDownCustomization;component/Images/Sunset.jpg" />
    <natureview id = "3" title = "Tulips-photo" 
      imagesource="/DomainUpDownCustomization;component/Images/tulips-photo.jpg"/>
    <natureview id = "4" title = "Waterfalls"  
      imagesource="/DomainUpDownCustomization;component/Images/Waterfalls_64.jpg"/>
  </natures>
  
</root>
 
I have created Service Oriented Architecture in this application, like creating Request/Response class.

Step 2 : Create Entity that holds data collection
 

public class Nature
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string ImageSource { get; set; }
    } 
 
storing image collection from XML files to Nature Collection.

Step 3: Create Response class that return Nature collections
 
   public class GetNatureResponse : Response
    {
        public IEnumerable<Nature> NatureCollection { get; set; }

        public GetNatureResponse(IEnumerable<Nature> natureCollection)
        {
            NatureCollection = natureCollection;
        }

    }
    public class Response
    {
        public bool IsSuccess { get; set; }
    } 
 
Above class holds collections of Natures.

Step 4: Create ServiceHelper class to get data from XML and bind into Entity

   public class NatureServiceHandler
    {
        public static void GetNaturePictures(GetRequest request, 
                         Action<GetNatureResponse> callback)
        {
            StreamResourceInfo resources = 
                Application.GetResourceStream(new Uri("Data/Nature.xml", UriKind.Relative));
            XDocument document = XDocument.Load(resources.Stream);

            IEnumerable<Nature> result = from doc in document.Descendants("natureview")
                  select new Nature
                  {
                     Id = Convert.ToInt32(doc.Attribute("id").Value),
                     Title = doc.Attribute("title").Value,
                     ImageSource = doc.Attribute("imagesource").Value
                  };

            callback(new GetNatureResponse(result));
        }
    }

This Service Helper class returns data collection retive from XML files. Result will return in callback.
now, create wrapper class for Nature class (i.e. NatureViewModel) which contains properties of Nature class.

Step 5 :Create ViewModel for UI

public class NatureCollectionViewModel : ViewModelBase
    {      

        private ObservableCollection<NatureViewModel> _natures;
        public ObservableCollection<NatureViewModel> Natures
        {
            get { return _natures; }
            set
            {
                if (_natures != value)
                {
                    _natures = value;
                    RaisePropertyChanged(() => Natures);
                }
            }
        }
        private NatureViewModel _selectedNature;
        public NatureViewModel SelectedNature
        {
            get { return _selectedNature; }
            set
            {
                if (_selectedNature != value)
                {
                    _selectedNature = value;
                    RaisePropertyChanged(() => SelectedNature);
                }
            }
        }
        private NatureTypes _natureType;
        public NatureTypes NatureType
        {
            get { return _natureType; }
            set
            {
                if (_natureType != value)
                {
                    _natureType = value;
                    RaisePropertyChanged(() => NatureType);
                    SelectedNature = Natures[Convert.ToInt16(_natureType)];
                }
            }

        }

        public NatureCollectionViewModel()
        {
            Natures = new ObservableCollection<NatureViewModel>();
             NatureServiceHandler.GetNaturePictures(
                new GetRequest(),
                (response) =>
                {
                    foreach (var item in response.NatureCollection)
                    {
                        Natures.Add(new NatureViewModel(item));
                    }

                    if (Natures.Count > 0)
                    {
                        SelectedNature = Natures[Convert.ToInt32(NatureType)];
                    }
                });
        }

    } 
 
In Default Constructor i am calling ServiceHanlder clas by passing request and bind Nature collection property with response result.

Step 6: Switch to UI, add InputToolkit and LayoutToolkit namespance.
 
<UserControl x:Class="DomainUpDownCustomization.View.NatureCollectionView"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d"           
   xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"
   xmlns:inputToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"> 

InputToolkit contain DomainUpDownControl and LayoutToolkit has TransitioningContentControl .
To format layout for both control i have added reference of this toolkit dlls.

Step 7 : Craeate DomainUpDown control in your Page

 <inputToolkit:DomainUpDown Grid.Row="1"                                  
              ItemsSource="{Binding Natures}"
              Value="{Binding SelectedNature, Mode=TwoWay}"
              IsCyclic="True"
              x:Name="NatureDomainControl"
              Height="360"
              Style="{StaticResource DomainUpDownStyle}"
              HorizontalAlignment="Center"
              Width="645"
              Margin="0,0,0,0"
              IsEditable="False"
              BorderBrush="Black">
          
            <inputToolkit:DomainUpDown.ItemTemplate>
                <DataTemplate>
                   .......
                   ....... 
                </DataTemplate>
            </inputToolkit:DomainUpDown.ItemTemplate>
        </inputToolkit:DomainUpDown> 

In above snippet, i am binding Nature Collection as a ItemsSource to DomainUpDown and binding properties like images in child control.

Step 8:Now move to styles, creating TransitioningContentControl style

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

 
To understand animation first you have to know what is VisualStateManager.
it Manages states and the logic for transitioning between states for controls. like (Normal,MouseOver,Focus) these are VisualStates.

StoryBoard is used for animation with a timeline,
if you want animate control on MouseOver of control than you can place storyborad within MouseOver VisualState.

Created VisualState for each ContentControl,VisualState is used to Transition content on Up/Down state.

Step 9:Creating Button Spinner Style 

<Style x:Key="ButtonSpinnerStyle"
               TargetType="inputToolkit:ButtonSpinner">
               .......
               .......
                Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="inputToolkit:ButtonSpinner">
                        <Grid>
                            <Grid.Resources>
                                <ControlTemplate x:Key="IncreaseButtonTemplate"
                                                 TargetType="RepeatButton">
 
                          .......
                          .......
 
button spinner style is used to change spin button image or path. i have created two templates one for Increase button and another for Descrease button.

you can create you own path or image for the buttons, currently i have created path  ( > ) for increase button and  (< ) for decrease button.

I have set Double Animation for each button.

Step 10: Create DomainUpDown style

<Style x:Key="DomainUpDownStyle"
               TargetType="inputToolkit:DomainUpDown">
           .......
           .......
   <Setter Property="Template">
       <Setter.Value>
          <ControlTemplate TargetType="inputToolkit:DomainUpDown">
             <Grid x:Name="RootElement"
                              Width="670">                            
                <Border x:Name="Border"
                    Opacity="1"
                    Background="{TemplateBinding Background}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}"
                    Padding="{TemplateBinding Padding}"
                    CornerRadius="1"
                    IsHitTestVisible="True"
                    <Border x:Name="MouseOverBorder"
                          BorderBrush="Transparent"
                          BorderThickness="1"
                          IsHitTestVisible="True">
                          <inputToolkit:ButtonSpinner x:Name="Spinner"
                            HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            HorizontalContentAlignment="Stretch"
                            VerticalContentAlignment="Stretch"
                            Style="{StaticResource ButtonSpinnerStyle}"
                            IsTabStop="False"
                            TabIndex="3">
                            <Grid>
                               <layoutToolkit:TransitioningContentControl x:Name="Visualization"
                                  Style="{StaticResource TransitioningContentTranslation}"
                                  IsTabStop="False"
                                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                  Content="{TemplateBinding Value}"
                                  ContentTemplate="{TemplateBinding ItemTemplate}"
                                  FontFamily="{TemplateBinding FontFamily}"
                                  FontSize="{TemplateBinding FontSize}"
                                  FontStretch="{TemplateBinding FontStretch}"
                                  Foreground="{TemplateBinding Foreground}" />
                            </Grid>
                          </inputToolkit:ButtonSpinner>
                       </Border>
                </Border>
                 ......
                 ......
             </Grid>
          </ControlTemplate>
       </Setter.Value>
   </Setter>
</Style>
 

I am setting styles of Button spinner and TransitioningContentControl that was previously created in this article.
In this way you can change style of any control using modifying ControlTemplate.

You can download source from below link.

Click DomainUpDownControlCustomization To Donwload Source.

  

No comments:

Post a Comment