My Scenario
I have two objects named Racers and Divisions. One racer can be in zero or more divisions and one division can contain zero or more racers. The data is edited on a Racer maintenance form that looks like this:The corresponding database tables look like this:

These are my model classes. Note that there are just two classes- Racer and Division. The navigation properties (Racer.Divisions and Divisions.Racers) are the link between them and are equivalent to the RacerDivisions link table in the database.
public class Racer : ModelBase, IModificationHistory
{
public Racer()
{
Divisions = new List<Division>();
}
private int racerId;
private int carNumber;
private string carName;
private string ownerLastName;
private string ownerFirstName;
private List<Division> divisions;
private DateTime dateModified;
private DateTime dateCreated;
public int RacerId
{
get { return racerId; }
set
{
if (value == racerId) return;
racerId = value;
RaisePropertyChanged("RacerId");
}
}
// The other getters/setters here
}
public class Division : ModelBase, IModificationHistory
{
public Division()
{
Racers = new List();
}
private int divisionId;
private int sequence;
private string name;
private string logoPath;
private bool includeInChampionship;
private bool isChampionship;
private List racers;
private DateTime dateModified;
private DateTime dateCreated;
public int DivisionId
{
get { return divisionId; }
set
{
divisionId = value;
RaisePropertyChanged("DivisionId");
}
}
// The other getters/setters here
}
Showing the Data
public class RacerDetailViewModel : ViewModelBase, IDataErrorInfo
{
private List<Division> _divisions;
private Racer _selectedRacer { get; set; }
// Various properties exposing Racer data to the view
// The list of all Divisions
public List<Division> Divisions
{
get { return _divisions; }
}
// The Racer Instance's Divisions
public ObservableCollection<Division> RacerDivisions
{
get { return _selectedRacer.Divisions.ToObservableCollection(); }
set
{
_selectedRacer.Divisions = value.ToList();
SetRacerDirty();
RaisePropertyChanged("Divisions");
}
}
// Other unrelated parts of the viewmodel (constructor, command handlers, etc.)
}
The checkboxes need a boolean value to bind to. I have a converter for this that takes two parameters: a Division object and a list of Racer.Divisions. If Racer.Divisions contains the Division, it returns True, and if not- False.
class DivisionToBooleanConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (!(values[0] is Division))
throw new ArgumentException("DivisionToBooleanConverter- First arg must be Division");
if (!(values[1] is ObservableCollection<Division>))
throw new ArgumentException("DivisionToBooleanConverter- Second arg must be RacerDivisions.");
var division = (Division)values[0];
var racerDivisions = (ObservableCollection<Division>)values[1];
return racerDivisions.Contains(division);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And finally, the WPF DataGrid that shows the data. It is bound to the list of all Divisions. I put the checkboxes in a template column because in a regular checkbox column the checkbox has to be clicked twice to change its value. The current division and list of Racer's divisions are passed to the converter to populate the row's checkbox.
<DataGrid Grid.Row="4" Grid.Column="1" Margin="5" ItemsSource="{Binding Divisions}"
AutoGenerateColumns="False" CanUserAddRows="False" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}"></DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource localDivisionToBooleanConverter}"
Mode="OneWay">
<Binding Path="."></Binding>
<Binding Path="DataContext.RacerDivisions"
RelativeSource="{RelativeSource AncestorType=Window}"></Binding>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And there you have it! Next I'll explain how the data is edited and saved. That will be in a new post because this one is long enough.

No comments:
Post a Comment