A recent problem I had was to bind the SelectedItem from a master datagrid, to the ItemsSource of a child datagrid. My work around’s using mouse clicks had too many limitations when used with MVVM – delete the selected item from the parent collection, the child collection did not update. Whilst watching John Papas PDC video for building large scale Silverlight application I spotted his sweet DataGridRowSelected command behaviours (documented http://thoughtjelly.blogspot.com/2009/12/silverlight-commands-data-grid-row.html)
To implement this you can do the following, XAML:
<UserControl x:Class="NestedDataGrid.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"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.GridView"
xmlns:this="clr-namespace:NestedDataGrid"
xmlns:commands="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
>
<data:DataGrid x:Name="dgTeams"
SelectedItem="{Binding SelectedTeam}"
ItemsSource="{Binding Teams}"
this:DataGridRowSelected.Command="{Binding GridViewRowSelectedCommand}"
/>
<Button Content="Delete Current"
commands:Click.Command="{Binding DeleteCurrentTeamCommand}"
commands:Click.CommandParameter="{Binding SelectedTeam}"
/>
Code behind:
public partial class MainPage : UserControl
{
ViewModel viewModel = new ViewModel();
public MainPage()
{
InitializeComponent();
this.LayoutRoot.DataContext = viewModel;
}
}
and ViewModel:
public class ViewModel: ObjectBase
{
ObservableCollection _rainbowPeeps;
ObservableCollection _theSimpsons;
ObservableCollection _familyGuyKids;
public ViewModel()
{
/// setting up some dummy data
_rainbowPeeps = new ObservableCollection()
{
new Person(){ PersonId=1, Name="George"},
new Person(){ PersonId=2, Name="Zippy"},
new Person(){ PersonId=3, Name="Bungle"},
};
_theSimpsons = new ObservableCollection()
{
new Person(){ PersonId=4, Name="Moe"},
new Person(){ PersonId=5, Name="Barney"},
new Person(){ PersonId=6, Name="Selma"},
};
_familyGuyKids = new ObservableCollection()
{
new Person(){ PersonId=7, Name="Stewie"},
new Person(){ PersonId=8, Name="Meg"},
new Person(){ PersonId=9, Name="Chris"},
};
Teams = new ObservableCollection()
{
new Team(){ TeamId=1, TeamDesc="Rainbow", People=_rainbowPeeps},
new Team(){ TeamId=2, TeamDesc="Simpsons", People=_theSimpsons},
new Team(){ TeamId=3, TeamDesc="Family Guys", People=_familyGuyKids },
};
/// COMMAND IMPLEMENTATIONS. The DataGridRowSelected parameter
/// is the datacontext of the selected row.
GridViewRowSelectedCommand = new DelegateCommand(
delegate(Team team) { SelectedTeam = team; }
);
DeleteCurrentTeamCommand = new DelegateCommand(
delegate(Team team)
{
Teams.Remove(team);
if (Teams.Count > 0)
SelectedTeam = Teams[0];
else
SelectedTeam = null;
}
);
}
private ObservableCollection _teams;
public ObservableCollection Teams
{
get { return _teams; }
set
{
SetValue(ref _teams, value, "Teams");
}
}
private Team _selectedTeam;
public Team SelectedTeam
{
get { return _selectedTeam; }
set
{
SetValue(ref _selectedTeam, value, "SelectedTeam");
}
}
///
/// Command to handle the selection of a row.
///
private DelegateCommand _gridViewRowSelectedCommand;
public DelegateCommand GridViewRowSelectedCommand
{
get { return _gridViewRowSelectedCommand; }
set
{
SetValue(ref _gridViewRowSelectedCommand, value, "GridViewRowSelectedCommand");
}
}
///
/// Used to simulate the deletion of a parent team
///
private DelegateCommand _deleteCurrentTeamCommand;
public DelegateCommand DeleteCurrentTeamCommand
{
get { return _deleteCurrentTeamCommand; }
set
{
SetValue(ref _deleteCurrentTeamCommand, value, "DeleteCurrentTeamCommand");
}
}
}
Finally, some misc data classes and the ObjectBase super class for INotifyPropertyChanged:
public abstract class ObjectBase : Object, INotifyPropertyChanged
{
public ObjectBase()
{ }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void _OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler pceh = PropertyChanged;
if (pceh != null)
{
pceh(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual bool SetValue(ref T target, T value, string propertyName)
{
if (Object.Equals(target, value))
{
return false;
}
target = value;
_OnPropertyChanged(propertyName);
return true;
}
}
public class Person: ObjectBase
{
private int _personId;
public int PersonId
{
get { return _personId; }
set
{
SetValue(ref _personId, value, "PersonId");
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
SetValue(ref _name, value, "Name");
}
}
}
public class Team : ObjectBase
{
private int _teamId;
public int TeamId
{
get { return _teamId; }
set
{
SetValue(ref _teamId, value, "TeamId");
}
}
private string _teamDesc;
public string TeamDesc
{
get { return _teamDesc; }
set
{
SetValue(ref _teamDesc, value, "TeamDesc");
}
}
private ObservableCollection _people;
public ObservableCollection People
{
get { return _people; }
set
{
SetValue(ref _people, value, "People");
}
}
public override string ToString()
{
return _teamId.ToString() + " " + _teamDesc;
}
}
Brilliant 🙂
NOTE: Code for DataGridRowSelected and DataGridRowSelectedCommandBehaviour is here
Thank you so much. This was exactly the problem I was having. Found your post via StackOverflow.