Monday, August 8, 2011

Implicit DataTempates in Silverlight 5 Beta

In this post we are going to discuss a new feature expected to be released in Silverlight 5. This feature is implicit DataTemplates. Those who come from WPF background to Silverlight often find it difficult to design without implicit DataTemplates. Well, this limitation has been worked in Silverlight 5 and it is available in the beta release. Silverlight 5 beta tools can be downloaded from here:

http://www.microsoft.com/download/en/details.aspx?id=23887

Let's create a sample Silverlight application and name it AppSilverlightImplicitDataTemplates.


Make sure that you have Silverlight 5 option selected in the following screen displayed right after the previous one.


You would need a reference of System.Windows.Controls.Data.Input assembly for Labels. If you directly drag and drop the Label Control from Toolbox on the designer surface, the assembly is automatically referenced.


Let's suppose we have the following view model. It has a collection of students. We want the user to select a particular student and allow that to be edited.
namespace AppSilverlightImplicitDataTemplates
{
    using System.Collections.ObjectModel;
    using System.Collections.Generic;

    public class MainViewModel : ViewModelBase
    {
        ObservableCollection<StudentViewModel> _students;
        public  ObservableCollection<StudentViewModel> Students
        {
            get
            {
                if (_students == null)
                {
                    _students = new ObservableCollection<StudentViewModel>();
                    _students.Add(new StudentViewModel() { StudentFirstName = "Muhammad", StudentLastName = "Siddiqi" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Jayson", StudentLastName = "Abante" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Meena", StudentLastName = "Gulla" });                                        
                }
                return _students;
            }
        }
    }
}
The view model is inheriting from ViewModelBase class. It is basically for providing general functionality to all view models specially implementation of INotifyPropertyChanged interface. As you might have guessed, like this view model, all others view models in the application would also be inheriting from this.
namespace AppSilverlightImplicitDataTemplates
{
    using System.ComponentModel;

    public class ViewModelBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Implementation

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

    }
}
In MainViewModel we have used StudentViewModel collection. It basically has two properties StudentFirstName and StudentLastName. Its definition can be as follows:
namespace AppSilverlightImplicitDataTemplates
{
    using System.ComponentModel;

    public class StudentViewModel : ViewModelBase
    {
        #region Notifiable Properties

        string _studentLastName;
        public string StudentLastName
        {
            get { return _studentLastName; }
            set
            {
                _studentLastName = value;
                OnPropertyChanged("StudentLastName");
            }
        }

        string _studentFirstName;
        public string StudentFirstName
        {
            get { return _studentFirstName; }
            set
            {
                _studentFirstName = value;
                OnPropertyChanged("StudentFirstName");
            }
        }

        #endregion

    }
}
As you can see above, it also inherits from ViewModelBase. The two properties support change notification by calling OnPropertyChanged method. In turn this would raise PropertyChanged event. As we have discussed already that we would be needing to use this view model in two different types of views. One is just for display for selection based control used to display the collection of students. We also need a different view to allow a student to be edited.

For displaying in collection based control:
<UserControl x:Class="AppSilverlightImplicitDataTemplates.StudentDisplayView"
        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" >    
    <TextBlock>
        <Run Text="{Binding StudentLastName}" FontWeight="Bold" />
        <Run Text="," />
        <Run Text="{Binding StudentFirstName}" />
    </TextBlock>
</UserControl>

For editing StudentViewModel:
<UserControl x:Class="AppSilverlightImplicitDataTemplates.StudentEditView"
        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:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" >
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="93" />
            <ColumnDefinition Width="307" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>           
            <RowDefinition Height="35" />
            <RowDefinition Height="*" />            
        </Grid.RowDefinitions>     
        <sdk:Label Height="23" HorizontalAlignment="Left"
                   Margin="12,12,0,0"  VerticalAlignment="Top"                    
                   Content="First Name" Width="83" Grid.ColumnSpan="2" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="8,8,0,0" 
                 VerticalAlignment="Top" Width="287" Text="{Binding StudentFirstName}" Grid.Column="1" />
        <sdk:Label Content="Last Name" Height="23" HorizontalAlignment="Left" 
                   Margin="12,7,0,0" VerticalAlignment="Top" Width="83" Grid.Row="1" Grid.ColumnSpan="2" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="8,3,0,0" 
                 VerticalAlignment="Top" Width="287" Grid.Row="1" 
                 Text="{Binding StudentLastName}" Grid.Column="1" />
    </Grid>
</UserControl>

Using UserControls in DataTemplate
By default we need the StudentViewModel type of objects to be using the display one. So we can define it as application resource. Just updated App.xaml as follows:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             x:Class="AppSilverlightImplicitDataTemplates.App"
             xmlns:local="clr-namespace:AppSilverlightImplicitDataTemplates"
             >
    <Application.Resources>
        <DataTemplate DataType="local:StudentViewModel">
            <local:StudentDisplayView />
        </DataTemplate>
    </Application.Resources>
</Application>
Now we define the Main application view. As we have discussed, we need to provide a collection of Students and allow the user to select any one of them and edit that.
<UserControl x:Class="AppSilverlightImplicitDataTemplates.MainPage"
        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"
        xmlns:local="clr-namespace:AppSilverlightImplicitDataTemplates"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400"
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <UserControl.DataContext>
        <local:MainViewModel />
    </UserControl.DataContext>    
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="97*" />
            <ColumnDefinition Width="303*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="43*" />
            <RowDefinition Height="257*" />
        </Grid.RowDefinitions>
        <sdk:Label Height="22" HorizontalAlignment="Left" Margin="11,18,0,0"
                   Name="label1" VerticalAlignment="Top" Width="86"
                   Content="Select Student" DataContext="{Binding Path=Students}" />
        <ComboBox Height="24" HorizontalAlignment="Left" Margin="6,14,0,0"
                  Name="cmbStudents" VerticalAlignment="Top" Width="285"
                  ItemsSource="{Binding Students}" Grid.Column="1" />
        <ContentControl 
            DataContext="{Binding ElementName=cmbStudents, Path=SelectedItem}" 
            Content="{Binding}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" 
            Margin="11,18,0,0"/>
    </Grid>
</UserControl>
Now lets run the application. It appears as follows:


Now select a student from the combo box. The same student is loaded in the ContentControl as follows:


Now we need the ContentControl to be using the editing type of UserControl for StudentViewModel so that it could be edited. Let's override it as follows in the main view:
<UserControl x:Class="AppSilverlightImplicitDataTemplates.MainPage"
        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"
        xmlns:local="clr-namespace:AppSilverlightImplicitDataTemplates"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400"
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <UserControl.DataContext>
        <local:MainViewModel />
    </UserControl.DataContext>    
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="97*" />
            <ColumnDefinition Width="303*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="43*" />
            <RowDefinition Height="257*" />
        </Grid.RowDefinitions>
        <sdk:Label Height="22" HorizontalAlignment="Left" Margin="11,18,0,0"
                   Name="label1" VerticalAlignment="Top" Width="86"
                   Content="Select Student" DataContext="{Binding Path=Students}" />
        <ComboBox Height="24" HorizontalAlignment="Left" Margin="6,14,0,0"
                  Name="cmbStudents" VerticalAlignment="Top" Width="285"
                  ItemsSource="{Binding Students}" Grid.Column="1" />
        <StackPanel Margin="11,18,0,0" Grid.Row="1" 
                    Grid.Column="0" Grid.ColumnSpan="2" >
            <StackPanel.Resources>
                <DataTemplate DataType="local:StudentViewModel" >
                    <local:StudentEditView />
                </DataTemplate>
            </StackPanel.Resources>
            <ContentControl 
            DataContext="{Binding ElementName=cmbStudents, Path=SelectedItem}" 
            Content="{Binding}" 
            />
        </StackPanel>
    </Grid>
</UserControl>
Here we have thrown the ContentControl inside a StackPanel. In the Resources section of the StackPanel, we have overridden the DataTemplate for StudentViewModel. Since local resource have preference over application level resources so this DataTemplate is applied for the StudentViewModel in ContentControl. When we run the application and select a student, the instance is loaded in the ContentControl with correct DataTemplate applied.


Download:

No comments: