Sunday, August 14, 2011

Silverlight 5 Beta - Relative Source Binding

In this post we will be discussing another new feature of Silverlight 5 Beta. It is Relative Source binding. RelativeSource has long been missing from Silverlight. It has always been there in WPF. Silverlight 5 is currently available in Beta and can be downloaded from here:

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

Let's create a MVVM Light Silverlight application AppSilverlightRelativeSourceBinding. It would have a list of students on display. The user should be allowed to remove any student on the list. In order to see the application of Relative Source binding, we would see how we can bind the Command on the child element to a parent element. Actually, the Remove button on child view should be bound to a Command in the DataContext of a parent element in the hierarchy [UserControl].


Just make sure that you have Silverlight 5 version selected in the project's properties.


This view model is the Main view model of the application. It has a collection of students (StudentViewModel). Since the collection is an ObservableCollection, all the addition / removal of elements in the collection would directly reflected in the view. It also has a Command which removes the student from the collection. This is the Command which would be bound by child elements.
namespace AppSilverlightRelativeSourceBinding.ViewModel
{
    using GalaSoft.MvvmLight;
    using System.Collections.ObjectModel;
    using GalaSoft.MvvmLight.Command;

    public class MainViewModel : ViewModelBase
    {
        #region Properties

        public string Welcome
        {
            get
            {
                return "Silverlight 5 Beta - Relative Source Binding";
            }
        }

        ObservableCollection<StudentViewModel> _students;
        public ObservableCollection<StudentViewModel> Students
        {
            get
            {
                if (_students == null)
                {
                    _students = new ObservableCollection<StudentViewModel>();

                    _students.Add(new StudentViewModel() { StudentFirstName = "Kamran", StudentLastName = "Khan" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Asad", StudentLastName = "Hussain" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Faisal", StudentLastName = "Lashari" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Hyder", StudentLastName = "Baloch" });
                    _students.Add(new StudentViewModel() { StudentFirstName = "Baber", StudentLastName = "Chaudhari" });
                }
                return _students;
            }
        }

        #endregion

        #region Commands

        RelayCommand<StudentViewModel> _removeStudentCommand;
        public RelayCommand<StudentViewModel> RemoveStudentCommand
        {
            get
            {
                return _removeStudentCommand ??
                        new RelayCommand<StudentViewModel>((student) =>
                        {
                            if (this.Students.Contains(student))
                            {
                                this.Students.Remove(student);
                            }
                        });
            }
        }

        #endregion        
    }
}

The definition of StudentViewModel used above is as follows:
namespace AppSilverlightRelativeSourceBinding.ViewModel
{   
    using GalaSoft.MvvmLight;

    public class StudentViewModel : ViewModelBase
    {
        #region Notifiable Properties

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

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

        #endregion

    }
}
It simply has two properties for first and last names of a student. The view model inherits from ViewModelBase which is provided by MVVM Light toolkit. Now let's see how we can display this view model. It is an implicit DataTemplate to show the student. The first and last names are displayed in a TextBlock. It also has a Remove button. Clicking the button would delete the student from the collection. Since StudentViewModel has no idea about which collection it has been part of and it has to be handled by something which holds this collection. So the button's Command needs to be bound to MainViewModel. This is why Relative Source binding makes most sense to be used here. Let's define this DataTemplate in MainSkin.xaml resource dictionary provided by MVVM Light.
<DataTemplate DataType="vm:StudentViewModel" >
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="30" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Button Content="Remove" Grid.Column="0"                       
                CommandParameter="{Binding}" >                
            <Button.Command>
                <Binding RelativeSource="{RelativeSource AncestorType=UserControl}" 
                         Path="DataContext.RemoveStudentCommand" />
            </Button.Command>
        </Button>
        <Border Grid.Column="1" />
        <Grid Grid.Column="2" >
            <TextBlock>  
                <Run Text="{Binding StudentLastName}" FontWeight="Bold" />
                <Run>
                    <Run.Text>
                        <Binding Path="StudentFirstName" StringFormat=",{0}" />
                    </Run.Text>
                    </Run>
            </TextBlock>
        </Grid>
    </Grid>
</DataTemplate>
We need to include this namespace in the above resource dictionary.
xmlns:vm="clr-namespace:AppSilverlightRelativeSourceBinding.ViewModel"
Now the design of MainPage is simple enough. Mainly it has a ListBox to display the collection of StudentViewModel. Since MainSkin.xaml resource dictionary is merged here, the implicit DataTemplate is automatically applied.
<UserControl x:Class="AppSilverlightRelativeSourceBinding.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"
             mc:Ignorable="d"            
             DataContext="{Binding Main, Source={StaticResource Locator}}">
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock FontSize="36" Grid.Row="0"
                   FontWeight="Bold"
                   Foreground="Purple"
                   Text="{Binding Welcome}"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   TextWrapping="Wrap" />
        <ListBox ItemsSource="{Binding Students}"
                 Grid.Row="1" />
    </Grid>
</UserControl>
When we run the application, the application is loaded as follows:


Download Code:

Friday, August 12, 2011

Silverlight 5 Beta - Binding Style's Setter.Value

In this post we will be discussing a binding enhancements in Silverlight 5. Now Style's Setters also support binding for their Value property. Silverlight 5 is currently available in Beta and can be downloaded from here:

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

Let's consider a simple example in which we need to define an application banner in a TextBlock. The application banner will provided by the view model so it needs to be bound to the TextBlock's Text property using Binding extension.


Let's consider a simple example in which we need to define an application banner in a TextBlock. The application banner will provided by the view model so it needs to be bound to the TextBlock's Text property using Binding extension. Let's update the MainPage as follows:
<UserControl x:Class="AppSilverlightBindingInStyle.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:AppSilverlightBindingInStyle"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.DataContext>
        <local:MainViewModel BannerText ="Application Banner" />
    </UserControl.DataContext>
    <UserControl.Resources>
        <Style TargetType="TextBlock" >            
            <Setter Property="Text" Value="{Binding BannerText}" />
            <Setter Property="FontSize" Value="30" />           
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Name="bannerBox" TextAlignment="Center" />
    </Grid>
</UserControl>
It has a simple TextBlock in the view. The DataContext is specified as MainViewModel. It has an inline assignment to its BannerText property. In the Resources we have a Style defined for the TextBlock. This would serve as the default Style of any TextBlock in the page unless overriden. Look at how we are able to bind the Value property of Style's Setter.

The view model just needs to have a property BannerText. The definition of MainViewModel used as DataContext in the above view is as follows:
namespace AppSilverlightBindingInStyle
{
    using System.ComponentModel;

    public class MainViewModel : INotifyPropertyChanged
    {
        #region Properties

        string _bannerText;
        public string BannerText
        {
            get { return _bannerText; }
            set
            {
                _bannerText = value;
                OnPropertyChanged("BannerText");
            }
        }

        #endregion
        
        #region INotifyPropertyChanged implementation

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

        #endregion        
    }
}
When we run the application, it is shown as follows:


Download Code:


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:

Sunday, August 7, 2011

Binding Debugging in Silverlight 5

In this post we will be discussing a long desired feature in XAML based applications. This feature is Binding Debugging specified in XAML. This feature is introduced in Silverlight 5.

Let's create a sample Silverlight application AppSilverlightDebuggingDemo.


Definitely an installation of Silverlight 5 is required for this example. Silverlight 5 beta tools can be downloaded from here:

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

If you have it installed already, the second step would allow you to select Silverlight 5 as the Silverlight version.


Here in order to test our example we are selecting the option to create a new website to host the silvelight application specified in previous step. When you hit OK the two projects should be available in Solution Explorer as follows:


Now we add a view model to the Silverlight project MainPageViewModel. It is a simple view model implementing INotifyPropertyChanged interface for supporting change notification for its properties. The view model has two properties StudentId and StudentName, setting both would cause PropertyChanged event to be triggered.
namespace AppSilverlightDebuggingDemo
{
    using System.ComponentModel;

    public class MainPageViewModel : INotifyPropertyChanged
    {
        #region Notifiable Properties

        string _studentId;
        public string StudentId
        {
            get { return _studentId; }
            set
            {
                _studentId = value;
                OnPropertyChanged("StudentId");
            }
        }

        string _studentName;
        public string StudentName
        {
            get { return _studentName; }
            set
            {
                _studentName = value;
                OnPropertyChanged("StudentName");
            }
        }

        #endregion

        #region INotifyPropertyChanged Implementation

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

        #endregion        
    }
}

Let us update the definition of MainPage.xaml as follows:

<UserControl x:Class="AppSilverlightDebuggingDemo.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:AppSilverlightDebuggingDemo"             
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" 
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <UserControl.DataContext>
        <local:MainPageViewModel StudentId="1" StudentName="Muhammad" />
    </UserControl.DataContext>
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="48*" />
            <RowDefinition Height="35*" />
            <RowDefinition Height="36*" />
            <RowDefinition Height="181*" />
        </Grid.RowDefinitions>
        <sdk:Label Height="24" HorizontalAlignment="Center" 
                   VerticalAlignment="Center" Width="189" FontWeight="Bold" FontSize="16"
                   Content="Student Information" Margin="106,24,106,0" />
        <sdk:Label Height="23" HorizontalAlignment="Left"
                   Margin="12,12,0,0" Name="lblStudentId" VerticalAlignment="Top"                    
                   Content="Student Id" Width="83" Grid.Row="1" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="101,8,0,0" 
                 VerticalAlignment="Top" Width="287" Grid.Row="1"
                 Text="{Binding StudentId}" />
        <sdk:Label Content="Student Name" Height="23" HorizontalAlignment="Left" 
                   Margin="12,8,0,0" VerticalAlignment="Top" Width="83" Grid.Row="2" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="101,4,0,0" 
                 VerticalAlignment="Top" Width="287" Grid.Row="2" 
                 Text="{Binding StudentName}"/>        
    </Grid>
</UserControl>
In the above view we are using the view model defined previously as DataContext. Here we have assigned some default values the two properties so that the view is loaded with some already existing information. We are binding the properties StudentId and StudentName to the two TextBox(es).Now when we run the application, it should display as follows:


Now open the view's XAML and try hitting F9 while putting the cursor on each of the bindings. This would insert a break point in the Binding expression. The execution would break at these bindings like a regular break point but the difference is that now this break point has been inserted in XAML.


Now let us run the application again. The execution would step at the break point as follows:


It not only steps at the breakpoint, it also lets us to use regular debugging tools provided in Visual Studio. You can see that we have the Locals window opened and it is showing the values of the properties of the DataContext. It also lets us find the issues with binding. Let's update the StudentName binding as follows:


How easy do you think your life would be. Easy debugging...no late sittings at work...more time with family :)

Download Code:

Friday, August 5, 2011

Binding ObservableCollection to Text Properties

In this post we will be discussing the issue when we bind a collection based (ObservableCollection) property to some scalar DependencyProperty e.g. Text property of a TextBlock.

Let's create a sample MVVM Light based WPF application.


Note:
Yes, you would need an installation of MVVM Light in order to follow this example.

Let's update MainWindow's definition as follows:
<Window x:Class="AppBindingScalarPropertiesCollections.MainWindow"
        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:converters="clr-namespace:AppBindingScalarPropertiesCollections.Converters"
        mc:Ignorable="d"
        Height="386"
        Width="514"
        Title="MVVM Light Application"
        DataContext="{Binding Main, Source={StaticResource Locator}}">
    
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <converters:StudentsListToStringConverter x:Key="studentConverter" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="42*" />
            <RowDefinition Height="37*" />
            <RowDefinition Height="189*" />
            <RowDefinition Height="79*" />
        </Grid.RowDefinitions>
        <TextBlock FontSize="36"
                   FontWeight="Bold"
                   Foreground="Purple"
                   Text="{Binding Welcome}"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   TextWrapping="Wrap" Margin="246,104,246,71" Grid.Row="2" />
        <Label Content="New Student" Height="23" HorizontalAlignment="Left"
               Margin="6,15,0,0" Name="label1" VerticalAlignment="Top" Width="97" />
        <TextBox Height="25" HorizontalAlignment="Right" Margin="0,13,12,0"  
                 VerticalAlignment="Top" Width="370"
                 Text="{Binding NewStudentName, UpdateSourceTrigger=PropertyChanged}" />
        <GroupBox Header="Students List" Height="179" HorizontalAlignment="Left" Margin="6,2,0,0" 
                  Name="groupBox1" VerticalAlignment="Top" Width="476" Grid.Row="2">
            <Grid>
                <ListBox ItemsSource="{Binding Students}"  />
            </Grid>
        </GroupBox>
        <GroupBox Header="Comma Separated Students List" Height="57" HorizontalAlignment="Left"
                   VerticalAlignment="Top" Width="476" Grid.Row="3" Margin="0,8,0,0">
            <TextBlock 
                Text="{Binding Students, Converter={StaticResource studentConverter}}" 
                Height="21" />
        </GroupBox>
        <Button Content="Add" Grid.Row="1" Height="26" HorizontalAlignment="Left" 
                Margin="382,7,0,0" VerticalAlignment="Top" Width="99"
                Command="{Binding AddNewStudentCommand}" />
    </Grid>
</Window>
The above view has some expectations from view model. Let's update the view model provided by MVVM Light's view model locator as follows:
namespace AppBindingScalarPropertiesCollections.ViewModel
{
    using GalaSoft.MvvmLight;
    using System.Collections.ObjectModel;
    using System.Windows.Input;
    using GalaSoft.MvvmLight.Command;
  
    public class MainViewModel : ViewModelBase
    {
        public string Welcome
        {
            get
            {
                return "Welcome to MVVM Light";
            }
        }

        string _newStudentName;
        public string NewStudentName
        {
            get { return _newStudentName; }
            set 
            {
                _newStudentName = value;
                RaisePropertyChanged("NewStudentName");
            }
        }

        ObservableCollection<string> _students;
        public ObservableCollection<string> Students
        {
            get
            {
                if (_students == null)
                {
                    _students = new ObservableCollection<string>();
                    _students.Add("Muhammad");
                    _students.Add("Ryan");
                    _students.Add("Jim");
                    _students.Add("Brian");
                    _students.Add("Josh");                   
                    _students.Add("Jeremy");
                }
                return _students;
            }
        }

        ICommand _addNewStudentCommand;
        public ICommand AddNewStudentCommand
        {
            get 
            {
                if (_addNewStudentCommand == null)
                {
                    _addNewStudentCommand = new RelayCommand(
                        () =>
                        {
                            if (!Students.Contains(NewStudentName))
                            {
                                Students.Add(NewStudentName);
                            }
                        });
                }
                
                return _addNewStudentCommand;
            }
        }
    }
}
Mainly it has three things. NewStudentName property to be bound to the TextBox to enter the name of a new student. AddNewStudentCommand
for the button's Command property. Clicking this button should add the student's name as entered in the TextBox to Student's collection if it already doesn't exist. The ListBox and TextBlock showing this are automatically expected be updated as a new student is added to the collection. Students collection to be bound to ListBox's ItemSource and TextBlock's Text properties.

We need the Student's list in the TextBlock to be comma separated names of all students. The view is using a converter for this purpose. It is a simple IValueConverter. Let's see the definition of this converter.
namespace AppBindingScalarPropertiesCollections.Converters
{
    using System.Windows.Data;
    using System.Collections.ObjectModel;
    using System.Linq;

    class StudentsListToStringConverter : IValueConverter
    {
        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string studentCommaSeparatedList = string.Empty;
            ObservableCollection<string> studentList = value as ObservableCollection<string>;

            if (studentList != null)
            {
                studentCommaSeparatedList = string.Join(", ",
                                            (from string studentName
                                                 in studentList
                                             select studentName).ToArray<string>());
            }           

            return studentCommaSeparatedList;
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new System.NotImplementedException();
        }
    }
}
In the Convert method of this IValueConverter, we are simple joining the elements of the collection with comma separation and returning it.

Let's run the application now. It appears as follows...


As we enter new student's name and hit Add button, the new student is added to Students collection. Since this collection is bound to the ItemsSource of the ListBox, it appears in the list box. But the same does not appear in the TextBlock's comma separated list. This is weird!!!

Basically, the reason is very simple. When we bind to the Text property of TextBlock, it is just interested in the PropertyChanged events. It handles this event to update its contents. But it seems that it doesn't handle CollectionChanged event of ObservableCollection. That is why it is not able to update itself. Now since we know the problem how we can resolve this. Basically there might be two different solutions to this problem.

Solution # 1: [Raise PropertyChanged Event when collection is updated for items]

This is very simple. Since we are just adding items in the Execute method of the ICommand bound to the Add button, we can simply do it there. Let's update the ICommand definition in the view model as follows:
public ICommand AddNewStudentCommand
{
    get 
    {
        if (_addNewStudentCommand == null)
        {
            _addNewStudentCommand = new RelayCommand(
                () =>
                {
                    if (!Students.Contains(NewStudentName))
                    {
                        Students.Add(NewStudentName);
                        RaisePropertyChanged("Students");
                    }
                });
        }
        
        return _addNewStudentCommand;
    }
}
Here RaisePropertyChanged method raises PropertyChanged event for the name of property provided as argument. This is available due to inheritence of this view model by ViewModelBase from MVVM Light. Let's run the application again.


As we entered Sekhar and hit the button, it appears both in the ListBox and TextBlock. This is exactly what we desired. Zindabad!!!

2nd Solution: [Use MultiBinding, adding binding for ObservableCollection.Count]
In this solution, we can simply add binding for Count property from the same collection. We can change the binding of TextBlock as follows:
<Window x:Class="AppBindingScalarPropertiesCollections.MainWindow"
        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:converters="clr-namespace:AppBindingScalarPropertiesCollections.Converters"
        mc:Ignorable="d"
        Height="386"
        Width="514"
        Title="MVVM Light Application"
        DataContext="{Binding Main, Source={StaticResource Locator}}">
    
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <converters:StudentListToStringMultiConverter x:Key="studentMultiConverter" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="42*" />
            <RowDefinition Height="37*" />
            <RowDefinition Height="189*" />
            <RowDefinition Height="79*" />
        </Grid.RowDefinitions>
        <TextBlock FontSize="36"
                   FontWeight="Bold"
                   Foreground="Purple"
                   Text="{Binding Welcome}"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   TextWrapping="Wrap" Margin="246,104,246,71" Grid.Row="2" />
        <Label Content="New Student" Height="23" HorizontalAlignment="Left"
               Margin="6,15,0,0" Name="label1" VerticalAlignment="Top" Width="97" />
        <TextBox Height="25" HorizontalAlignment="Right" Margin="0,13,12,0"  
                 VerticalAlignment="Top" Width="370"
                 Text="{Binding NewStudentName, UpdateSourceTrigger=PropertyChanged}" />
        <GroupBox Header="Students List" Height="179" HorizontalAlignment="Left" Margin="6,2,0,0" 
                  Name="groupBox1" VerticalAlignment="Top" Width="476" Grid.Row="2">
            <Grid>
                <ListBox ItemsSource="{Binding Students}"  />
            </Grid>
        </GroupBox>
        <GroupBox Header="Comma Separated Students List" Height="57" HorizontalAlignment="Left"
                   VerticalAlignment="Top" Width="476" Grid.Row="3" Margin="0,8,0,0">

            <TextBlock                 
                Height="21" >
                <TextBlock.Text>
                    <MultiBinding  Converter="{StaticResource studentMultiConverter}" >
                        <Binding Path ="Students"/>
                        <Binding Path ="Students.Count" />
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </GroupBox>
        <Button Content="Add" Grid.Row="1" Height="26" HorizontalAlignment="Left" 
                Margin="382,7,0,0" VerticalAlignment="Top" Width="99"
                Command="{Binding AddNewStudentCommand}" />
    </Grid>
</Window>
In addition of Binding update, we also need to update the converter to IMultiValueConverter. Let's see the definition of StudentListToStringMultiConverter used above.
class StudentListToStringMultiConverter : IMultiValueConverter
{

    public object Convert(object[] values, System.Type targetType, object parameter, 
        System.Globalization.CultureInfo culture)
    {
        string studentCommaSeparatedList = string.Empty;
        ObservableCollection<string> studentList = values[0] as ObservableCollection<string>;

        if (studentList != null)
        {
            studentCommaSeparatedList = string.Join(", ",
                                        (from string studentName
                                             in studentList
                                         select studentName).ToArray<string>());
        }

        return studentCommaSeparatedList;
    }

    public object[] ConvertBack(object value, System.Type[] targetTypes, object parameter,
        System.Globalization.CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}
Running the application should have the same result as the first option.

Any Questions??? Comments...

Download:
The sameple project can be downloaded from SkyDrive here: