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:
1 comment:
Great post, been spending hours looking for the right syntax for this having jsut moved from the solution posted at DAn Wahlin's blog form a while back.
Post a Comment