WPF: Entity Framework MVVM Walk Through 2 Andy ONeills example

scott thomson 66 Reputation points
2020-10-11T01:36:01.86+00:00

I have learnt a lot from Andy O'Neill's WPF: Entity Framework MVVM Walk Through 2 example as I learn WPF and MVVM etc. Generally though I always seem to struggle on comboboxes and getting the ItemsSource, SelectedValue and SelectedValuePath set up correctly to successfully show data in the combobox. The Binding is really tricky in combination with the particular comboxbox control design. In Andys example I was trying to put a combobox inside an ItemsControl thats inside a ContentControl (Popup) thats inside a grid on a UserControl. I cannot get the data from MyObservableCollection to show in the ItemsSource. I have tried all manner of versions of comboc control construction and Binding including {Binding DataContext.MyObservableCollection, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl} , RelativeSource={RelativeSource AncestorType={x:Type local:UserControl}} and replacing UserControl with the ViewModels , and using FindAncestor etc. The DataContext is set to the ViewModel where the observable collection is and its got data in it, but seems cant get binding to work.

Any tips to look at for this type of situation would be greatly appreciated or even an answer from the great man himself Mr Andy O'Neill (anonymous userONeill) . wow imagine that

thanks
Scott

Developer technologies Windows Presentation Foundation
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2020-10-11T06:52:39.713+00:00

    Hi Scott,
    in following demo you can see how to bind ComboBoxColumn in DataGrid in UserControl.

    XAML MainWindow:

    <Window x:Class="Window064"  
            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"  
            xmlns:local="clr-namespace:WpfApp1.WpfApp064"  
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"  
            mc:Ignorable="d"  
            Title="Demo ComboBox in UserControl" Height="450" Width="800">  
      <Window.DataContext>  
        <local:ViewModel/>  
      </Window.DataContext>  
      <Grid>  
        <uc:Window064UC1/>  
      </Grid>  
    </Window>  
    

    ViewModel as DataContext in MainWindow (and UserControl), View property for rows in DataGrid, MasterList as Lookup values for ItemsSource in DataGridComboBoxColumn, LoadDemoData for loading data (e.g. EF).

    Imports System.Collections.ObjectModel  
    Imports System.ComponentModel  
      
    Namespace WpfApp064  
      
      Public Class ViewModel  
      
        Public Sub New()  
          LoadDemoData  
        End Sub  
      
        Private cvsChild As New CollectionViewSource  
        Private colChild As New ObservableCollection(Of Data)  
        Public ReadOnly Property View As ICollectionView  
          Get  
            Return cvsChild.View  
          End Get  
        End Property  
      
        Private colMaster As New ObservableCollection(Of Master)  
        Public ReadOnly Property MasterList As ObservableCollection(Of Master)  
          Get  
            Return colMaster  
          End Get  
        End Property  
      
        Private Sub LoadDemoData()  
          Dim rnd As New Random  
          For i = 1 To 10  
            colMaster.Add(New Master With {.ID = i, .MasterInfo = $"Master {i}"})  
          Next  
          For i = 1 To 100  
            colChild.Add(New Data With {.ID = i, .Info = $"Row {i}", .FKMaster = rnd.Next(1, 11)})  
          Next  
          cvsChild.Source = colChild  
        End Sub  
      End Class  
      
      Public Class Data  
        Public Property ID As Integer  
        Public Property FKMaster As Integer  
        Public Property Info As String  
      End Class  
      
      Public Class Master  
        Public Property ID As Integer  
        Public Property MasterInfo As String  
      End Class  
      
    End Namespace  
    

    XAML UserControl (no CodeBehind)

    <UserControl x:Class="Window064UC1"  
                 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:WpfControlLibrary1"  
                 mc:Ignorable="d"   
                 d:DesignHeight="450" d:DesignWidth="800">  
      <Grid>  
        <Grid.Resources>  
          <CollectionViewSource x:Key="ItemsCVS" Source="{Binding MasterList}" />  
        </Grid.Resources>  
        <DataGrid ItemsSource="{Binding View}" AutoGenerateColumns="False">  
          <DataGrid.Columns>  
            <DataGridTextColumn Header="ID" Binding="{Binding ID}"/>  
            <DataGridTextColumn Header="Info" Binding="{Binding Info}"/>  
            <DataGridComboBoxColumn Header="Lookup"  
                                    ItemsSource="{Binding Source={StaticResource ItemsCVS}}"  
                                    DisplayMemberPath="MasterInfo"  
                                    SelectedValuePath="ID"  
                                    SelectedValueBinding="{Binding FKMaster}"/>  
          </DataGrid.Columns>  
        </DataGrid>  
      </Grid>  
    </UserControl>  
    

    Result:

    31461-x.gif

    1 person found this answer helpful.

5 additional answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2020-10-12T07:53:37.727+00:00

    Hi Scott,
    I cannot reproduce your code because I don't know yours additional classes.

    I reduce your code and this code works fine:

    XAML MainWindow:

    <Window x:Class="WpfApp1.Window90"  
            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"  
            xmlns:local="clr-namespace:WpfApp90"  
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"  
            mc:Ignorable="d"  
            Title="Window90" Height="450" Width="800">  
      <Window.DataContext>  
        <local:TypeListViewModel/>  
      </Window.DataContext>  
      <Grid>  
        <uc:Window90UC1/>  
      </Grid>  
    </Window>  
    

    XAML UserControl:

    <UserControl x:Class="WpfControlLibrary1.Window90UC1"  
                 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:WpfControlLibrary1"  
                 mc:Ignorable="d"   
                 d:DesignHeight="180" d:DesignWidth="660">  
      <Grid>  
        <Grid.RowDefinitions>  
          <RowDefinition Height="auto"/>  
          <RowDefinition/>  
        </Grid.RowDefinitions>  
        <DataGrid Grid.Row="1"  
                  AutoGenerateColumns="False"  
                  ItemsSource="{Binding Types}"  
                  HeadersVisibility="Column"  
                  SelectedItem="{Binding SelectedType, Mode=TwoWay}"  
                  SelectionMode="Single"  
                  CanUserAddRows="False"  
                  CanUserDeleteRows="False"  
                  Background="Transparent">  
          <DataGrid.Columns>  
            <DataGridTemplateColumn Header="Contract Type Description">  
              <DataGridTemplateColumn.CellTemplate >  
                <DataTemplate >  
                  <TextBox x:Name="FocusTextBox"   
                           Text="{Binding TheEntity.Type1, Mode=TwoWay}"   
                           Width="460"   
                           IsReadOnly="True"   
                           BorderBrush="Transparent"/>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellTemplate>  
            </DataGridTemplateColumn>  
            <DataGridTemplateColumn Header="Retention Type">  
              <DataGridTemplateColumn.CellTemplate >  
                <DataTemplate >  
                  <TextBox Text="{Binding TheEntity.IDRetention, Mode=TwoWay}"   
                           Width="60"   
                           IsReadOnly="True"   
                           BorderBrush="Transparent"/>  
                </DataTemplate>  
              </DataGridTemplateColumn.CellTemplate>  
            </DataGridTemplateColumn>  
          </DataGrid.Columns>  
        </DataGrid>  
        <ContentControl Margin="0,0,0,0" >  
          <ScrollViewer VerticalScrollBarVisibility="Auto">  
            <ItemsControl Width="300" >  
              <ComboBox ItemsSource="{Binding Retentions}"/>  
            </ItemsControl>  
          </ScrollViewer>  
        </ContentControl>  
        <TextBlock Text="{Binding ErrorMessage}"    
                   HorizontalAlignment="Right"   
                   VerticalAlignment="Center"/>  
      </Grid>  
    </UserControl>  
    

    And classes:

    using System.Collections.Generic;  
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Runtime.CompilerServices;  
    using System.Windows;  
      
    namespace WpfApp90  
    {  
      public class TypeListViewModel : CrudVMBase  
      {  
        public TypeListViewModel()  
        : base()  
        {  
          Types = new ObservableCollection<TypeVM>();  
          for (int i = 1; i < 50; i++)  
            Types.Add(new TypeVM() { TheEntity = new Ent() { Type1 = $"Type {i}", IDRetention = $"IDRet {i}" } });  
          Retentions = new List<string>();  
          for (int i = 1; i < 10; i++) Retentions.Add($"Ret {i}");  
        }  
      
        public ObservableCollection<TypeVM> Types { get; set; }  
      
        public object SelectedType { get; set; }  
      
        public string ErrorMessage { get; set; } = "no error";  
      
        public List<string> Retentions { get; set; }  
      }  
      
      public class CrudVMBase : INotifyPropertyChanged  
      {  
        public event PropertyChangedEventHandler PropertyChanged;  
        internal void OnPropertyChanged([CallerMemberName] string propName = "") =>  
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
      }  
      
      public class TypeVM  
      {  
        public Ent TheEntity { get; set; }  
      }  
      
      public class Ent  
      {  
        public string Type1 { get; set; }  
        public string IDRetention { get; set; }  
      }  
    }  
    

    Result:

    31595-x.gif

    1 person found this answer helpful.
    0 comments No comments

  2. scott thomson 66 Reputation points
    2020-10-12T06:28:03.987+00:00
    0 comments No comments

  3. scott thomson 66 Reputation points
    2020-10-12T06:43:56.077+00:00
    0 comments No comments

  4. scott thomson 66 Reputation points
    2020-10-12T06:45:16.287+00:00
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.