Reputation: 51
I'm trying to create a UI with WPF using MVVM, but I'm having a bit of trouble. I want to have a DataGrid with two columns. The first column will be a label, and the second column will have a ComboBox in it. The label column always shows up, but nothing ever shows up in the ComboBox. I'm don't think I'm binding it properly, but I'm not sure how to fix it.
I created a class with the properties that I want to appear in the DataGrid. The label will be the Parameter property and I have a list property called Revisions for the ComboBox.
class RevisionMapModel
{
private string _parameter;
public string Parameter
{
get { return _parameter; }
}
private List<string> _revisions;
public List<string> Revisions
{
get { return _revisions; }
}
private string _selection;
public string Selection
{
get { return _selection; }
set { _selection = value; }
}
public RevisionMapModel(string parameter, List<string> revisions)
{
// set the parameter name and the list of revisions
_parameter = parameter;
_revisions = revisions;
_selection = "";
// add a default revision
_revisions.Add("None");
// attempt to find which revision matches with this parameter
FullRevisionMatch(_parameter, _revisions);
// if a full match isn't found, then try again
if (_selection == "None")
{
PartialRevisionMatch(_parameter, _revisions);
}
}
}
Here is the ViewModel class that serves as the DataContext. The DataGrid is bound to the RevisionMapModels property, which is a list of the previous class.
class RevisionMapViewModel
{
private string _label;
public string Label
{
get { return _label; }
}
private List<RevisionMapModel> _revisionMapModels;
public List<RevisionMapModel> RevisionMapModels
{
get { return _revisionMapModels; }
}
public RevisionMapViewModel(List<Definition> parameters, List<Revision> revisions, string label)
{
// instantiate the list
_revisionMapModels = new List<RevisionMapModel>();
_label = label;
// convert the parameters and revisions to strings
List<string> revisionDescriptions = revisions.Select(r => r.Description).ToList();
List<string> parameterNames = parameters.Select(p => p.Name).ToList();
// create classes for each parameter
List<string> reservedRevisions = new List<string>();
for (int i=0; i< parameterNames.Count; i++)
{
RevisionMapModel model = new RevisionMapModel(parameterNames[i], revisionDescriptions);
// check to ensure this parameter is not mapped to a revision that is already selected
if (reservedRevisions.Contains(model.Selection))
{
// change the selection to none if it's already used
model.Selection = "None";
}
else
{
reservedRevisions.Add(model.Selection);
}
// add it to the list
_revisionMapModels.Add(model);
}
}
}
This is my XAML below. I'm guessing it's having trouble binding to the Revisions property because it's a list. I've tried a number of things, but nothing seems to get this to show up.
<Window x:Class="SheetRevisions.RevisionMapView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SheetRevisions"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" MinWidth="500" MinHeight="500" Width="500">
<Grid>
<DataGrid Margin="20,40,20,45" MinWidth="50" MinHeight="47" ItemsSource="{Binding RevisionMapModels}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Parameter Name" Binding="{Binding Parameter}" Width="Auto"/>
<DataGridComboBoxColumn Header="Revision Description" ItemsSource="{Binding Revisions}" Width="*" />
</DataGrid.Columns>
</DataGrid>
<Label Content="{Binding Label}" HorizontalAlignment="Left" Height="25" Margin="20,10,0,0" VerticalAlignment="Top" Width="412"/>
<Button Content="Cancel" HorizontalAlignment="Right" Margin="0,0,20,10" VerticalAlignment="Bottom" Width="75" IsCancel="True"/>
<Button Content="OK" HorizontalAlignment="Right" Margin="0,0,110,10" VerticalAlignment="Bottom" Width="75" IsDefault="True"/>
</Grid>
</Window>
I'm really new to WPF so any help is appreciated.
Upvotes: 1
Views: 68
Reputation: 51
In addition to the comments above about implimenting INotifyPropertyChanged I also needed to change my XAML to use a DataGridTemplateColumn rather than a DataGridComboBoxColumn. It has something to do with a list not being a valid source as I found out from this post:
Working XAML:
<Window x:Class="SheetRevisions.RevisionMapView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SheetRevisions"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" MinWidth="500" MinHeight="500" Width="500">
<Grid>
<DataGrid Margin="20,40,20,45" MinWidth="50" MinHeight="47" ItemsSource="{Binding RevisionMapModels}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Parameter Name" Binding="{Binding Parameter}" Width="*" IsReadOnly="True"/>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Revisions}" SelectedItem="{Binding Selection}" Width="Auto"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Label Content="{Binding Label}" HorizontalAlignment="Left" Height="25" Margin="20,10,0,0" VerticalAlignment="Top" Width="412"/>
<Button Content="Cancel" HorizontalAlignment="Right" Margin="0,0,20,10" VerticalAlignment="Bottom" Width="75" IsCancel="True"/>
<Button Content="OK" HorizontalAlignment="Right" Margin="0,0,110,10" VerticalAlignment="Bottom" Width="75" IsDefault="True"/>
</Grid>
</Window>
Revised model:
public class RevisionMapModel : INotifyPropertyChanged
{
#region fields
private Logger _logger = LogUtils.LogFactory.GetCurrentClassLogger();
private string _parameter;
public string Parameter
{
get { return _parameter; }
set { }
}
private List<string> _revisions;
public List<string> Revisions
{
get { return _revisions; }
}
private string _selection;
public string Selection
{
get { return _selection; }
set
{
_selection = value;
OnPropertyRaised("Selection");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public RevisionMapModel(string parameter, List<string> revisions)
{
// set the parameter name and the list of revisions
_parameter = parameter;
_revisions = revisions;
_selection = "";
// add a default revision
_revisions.Add("None");
// attempt to find which revision matches with this parameter
FullRevisionMatch(_parameter, _revisions);
// if a full match isn't found, then try again
if (_selection == "None")
{
PartialRevisionMatch(_parameter, _revisions);
}
}
protected void OnPropertyRaised(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 0
Reputation: 443
As already mentioned here, you need to implement INotifyPropertyChanged in your ViewModel. Something like
RevisionMapViewModel : INotifyPropertyChanged
//...your code here
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String info) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Then, RevisionMapModel should be a public class, as I think. Also, in case you work in Visual Studio, some tips about your incorrect bindings can be visible in Output window.
Upvotes: 1