I'm trying to get a DataGridComboBoxColumn working with my ViewModel. Everything appears to work correctly but when I change the value of the combo box, the entity isn't changed.
The datacontext of the window has these properties:
ItemsSource
Public Property AllEnergySources() As ObservableCollection(Of EnergySourceViewModel)
SelectedItemBinding
Private _CurrentEnergySource As EnergySourceViewModel
Public Property CurrentEnergySource() As EnergySourceViewModel
Get
Return _CurrentEnergySource
End Get
Set(ByVal value As EnergySourceViewModel)
_CurrentEnergySource = value
OnPropertyChanged("CurrentEnergySource")
End Set
End Property
I feel the problem lies with how I populate CurrentEnergySource in the ViewModel that is the DataContext:
Sub New(SelectedEntity as EquipmentEnergySource)
AllEnergySources = New ObservableCollection(Of EnergySourceViewModel)
//Select all EnergySources from the EntityFramework
Dim EnergyEntities = From esr in db.EnergySources Select esr
//Loop through to convert Entity POCO to Collection of ViewModels
For Each es In EnergyEntities
_AllEnergySources.Add(New EnergySourceViewModel(es))
//Optionally Set the newly created ViewModel to SelectedItemBinding object
If es.EnergySourceID = SelectedEntity.EnergySourceID Then
_CurrentEnergySource = _AllEnergySources.Last
End If
Next
End Sub
When I create the backing collection for the combobox, if the model is the selected one, I set that viewmodel to be the CurrentEnergySource but after that point it is disconnected(and that's the problem)
What should I reference in CurrentEnergySource so that it updates the model when the combo box changes?
One thing that seems to be wrong is you should probably be using SelectedValueBinding not SelectedItemBinding.
Here's a sample that works ok for me:
<Page.Resources>
<ViewModel:DataGridComboBoxViewModel x:Key="model"/>
<Style x:Key="ElementStyle" TargetType="ComboBox">
<Setter
Property="ItemsControl.ItemsSource"
Value="{Binding Path=DataContext.DetailItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
/>
</Style>
</Page.Resources>
<Grid DataContext="{StaticResource model}">
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridComboBoxColumn Header="Combo"
DisplayMemberPath="Name"
SelectedValueBinding="{Binding DetailItem}"
ElementStyle="{StaticResource ElementStyle}"
EditingElementStyle="{StaticResource ElementStyle}"
>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
public class DataItem : ViewModelBase
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
private DetailItem _detailItem;
public DetailItem DetailItem
{
get { return _detailItem; }
set
{
Debug.WriteLine(value != null
? string.Format("Setting detail item to: {0}", value.Name)
: "Setting detail item to null.");
Set(() => DetailItem, ref _detailItem, value);
}
}
}
public class DetailItem : ViewModelBase
{
public int Id { get; set; }
public string Name { get; set; }
}
public class DataGridComboBoxViewModel : ViewModelBase
{
public DataGridComboBoxViewModel()
{
DetailItems = new List<DetailItem>
{
new DetailItem {Id = 0, Name = "Zero"},
new DetailItem {Id = 1, Name = "One"},
new DetailItem {Id = 2, Name = "Two"},
new DetailItem {Id = 3, Name = "Three"},
};
Items = new List<DataItem>
{
new DataItem {Id = 0, Name = "Item 1", Description = "This is item 1"},
new DataItem {Id = 1, Name = "Item 2", Description = "This is item 2"},
new DataItem {Id = 2, Name = "Item 3", Description = "This is item 3"},
new DataItem {Id = 3, Name = "Item 4", Description = "This is item 4"},
};
}
public List<DataItem> Items { get; set; }
public List<DetailItem> DetailItems { get; private set; }
}
of course, the problem is in your binding, the DataGridComboBoxColumn automatically take one item from CurrentEnergySource, and the CurrentEnergySource don't have AllEnergySources on it, Whey you don't semply Use this,
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SelectedItemFromItemsSource}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ItemsSource}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Have you tried a RelativeSource on the binding? usually I find when I'm binding to a column in a template etc the binding looks inside the binding of the control, not that of the datacontext of the view.
Try either:
"{Binding Path=DataContext.CurrentEnergySource,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},
Mode=TwoWay}"
or
"{Binding ElementName=NameOfTheView,
Path=DataContext.CurrentEnergySource,
Mode=TwoWay}"
and then adding x:Name="NameOfTheView"
to the View's attributes (below the xmlns place inside the > bracket)
My answer is you need to change the foreign key manually (I now change it in the setter of CurrentEnergySource, which is the SelectedItemBinding bound property)
Private _CurrentEnergySource As EnergySourceViewModel
Public Property CurrentEnergySource() As EnergySourceViewModel
Get
Return _CurrentEnergySource
End Get
Set(ByVal value As EnergySourceViewModel)
_CurrentEnergySource = value
Me.Model.EnergySourceID = value.Model.EnergySourceID
OnPropertyChanged("CurrentEnergySource")
End Set
End Property
On load, populate the private backing store _CurrentEnergySource
instead of the property to avoid all objects starting out with modified state