Michael
Michael

Reputation: 1879

How do I bind a DataGridComboBoxColumn to EntityFramework using MVVM?

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?

Upvotes: 0

Views: 2905

Answers (4)

Michael
Michael

Reputation: 1879

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

Upvotes: 0

Jason Ridge
Jason Ridge

Reputation: 1868

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)

Upvotes: 0

Phil
Phil

Reputation: 43011

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; }
}

Upvotes: 1

HB MAAM
HB MAAM

Reputation: 4012

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>

Upvotes: 0

Related Questions