MVVM datagrid comboboxes are not displaying the initial values and the moderator dropdown list needs to be unique

Yvon 50 Reputation points
2024-04-06T18:45:14.3233333+00:00

Based on my last request from this community and I got the POC working.

[https://learn.microsoft.com/en-us/answers/questions/1571452/how-to-save-all-the-updates-in-a-datagrid-(set-of?page=4&orderby=helpful#answers](https://learn.microsoft.com/en-us/answers/questions/1571452/how-to-save-all-the-updates-in-a-datagrid-(set-of?page=4&orderby=helpful#answers)

I start associate with the real work.

I continue use student dataset as guideline.

My schema: 2 tables (Moderators and ModeratorType)

Moderators is table containing data to be manipulated. While ModeratorType is overloading table serves as lookup.

The question: (Moderator datagrid) The drop down comboboxes are not showing the initial values? The Student datagrid is working fine.

Here is my codes and schema

insert Moderators values ('1234','Plastic','<30')

insert Moderators values ('1234','Water','Hot')

insert Moderators values ('1234','Water','Code')

insert Moderators values ('4567','Metal','Silver')

ModeratorType is just a lookup table

insert ModeratorType values ('Plastic','p','<=50');

insert ModeratorType values ('Plastic','p','>50');

insert ModeratorType values ('Plastic','p','=100');

insert ModeratorType values ('Water','w','Hot');

insert ModeratorType values ('Water','w','Cold');

insert ModeratorType values ('Metal','m','Silver');

insert ModeratorType values ('Metal','m','Gold');

I create a new UserControl2.xaml and similar to UserControl1.xaml Except the schema different.

But somehow the datagrid combobox didn't display the initial values

Tried so many SelectedValue/SelectedValuePath/SelectedItem, and still not displaying initial values

Plus I want the drop down combobox (material column) returns unique instead repeating (eg: Plastic)

User's image

User's image

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,387 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,674 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,266 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
765 questions
{count} votes

3 answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,231 Reputation points
    2024-04-10T13:08:51.2033333+00:00

    Try following demo:

    SQL:

    USE [CollegeDB]
    GO
    /****** Object:  Table [dbo].[Moderators] ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[Moderators](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[PartNumber] nvarchar(20) null,
    	[Material] nvarchar(20) null,
    	[Mass] nvarchar(10) null
     CONSTRAINT [PK_Moderators] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    /****** Object:  Table [dbo].[ModeratorType] *****/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TABLE [dbo].[ModeratorType](
    	[ID] [int] IDENTITY(1,1) NOT NULL,
    	[Moderator] nvarchar(20) not null,
    	[Type] varchar(1) not null,
    	[Mass] nvarchar(10) not null
     CONSTRAINT [PK_ModeratorType] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    INSERT [dbo].[Moderators] ([PartNumber], [Material], [Mass]) VALUES ('1234','Plastic','<30')
    GO
    INSERT [dbo].[Moderators] ([PartNumber], [Material], [Mass]) VALUES ('1234','Water','Hot')
    GO
    INSERT [dbo].[Moderators] ([PartNumber], [Material], [Mass]) VALUES ('1234','Water','Cold')
    GO
    INSERT [dbo].[Moderators] ([PartNumber], [Material], [Mass]) VALUES ('4567','Metal','Silver')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Plastic','p','<30')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Plastic','p','>50')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Plastic','p','=100')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Water','w','Hot')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Water','w','Cold')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Metal','m','Silver')
    GO
    INSERT [dbo].[ModeratorType] ([Moderator], [Type], [Mass]) VALUES ('Metal','m','Gold')
    GO
    

    MainWindow:

    <Window x:Class="WpfApp1.Window004"
            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:WpfApp004"
            xmlns:uc="clr-namespace:WpfApp004"
            mc:Ignorable="d"
            Title="Yvon_240406" Height="500" Width="800">
      <Window.Resources>
        <local:ViewModelMain x:Key="vm"/>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}" Margin="10">
        <Grid.Resources>
          <Style TargetType="Button">
            <Setter Property="Margin" Value="10"/>
            <Setter Property="Width" Value="150"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
          </Style>
          <Style TargetType="Label">
            <Setter Property="Margin" Value="5"/>
          </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="100"/>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="250"/>
          <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition Width="220"/>
        </Grid.ColumnDefinitions>
        <uc:UserControl2 Grid.Row="3" Grid.Column="0" Margin="5"
                         DataGridItemsSource="{Binding AllModerators}"/>
        <Button Grid.Row="3" Grid.Column="1"  Margin="5" Width="150" HorizontalAlignment="Left"
                Content="AddModerator"
                Command="{Binding Cmd}" CommandParameter="AddModerator"/>
        <StackPanel Grid.Row="4" Grid.ColumnSpan="2" HorizontalAlignment="Right" Orientation="Horizontal" >
          <Button Content="CancelAll" Command="{Binding Cmd}" CommandParameter="CancelAll"/>
          <Button Content="SaveAll" Command="{Binding Cmd}" CommandParameter="SaveAll"/>
        </StackPanel>
      </Grid>
    </Window>
    

    XAML UserControl:

    <UserControl x:Name="userControl"
                 x:Class="WpfApp004.UserControl2"
                 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:WpfApp004"
                 xmlns:vm="clr-namespace:WpfApp004"
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
      <UserControl.Resources>
        <vm:ModeratorsDataGridViewModel x:Key="viewModel"/>
        <Color x:Key="GlyphColor">#FF444444</Color>
      </UserControl.Resources>
      <Grid x:Name="grid" DataContext="{StaticResource viewModel}">
        <DataGrid x:Name="dg"
                  AutoGenerateColumns="False"
                  CanUserAddRows="False"
                  ItemsSource="{Binding DataGridItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
                  SelectedItem="{Binding SelectedItem}">
          <!--local:ModeratorsDataGridViewModel.AttProp="True">-->
          <DataGrid.Columns>
            <DataGridTextColumn Header="Place" Width="120" Binding="{Binding PartNumber}" />
            <DataGridComboBoxColumn Header="Material" Width="130" 
                                    SelectedItemBinding="{Binding Material}"
                                    ItemsSource="{Binding AllMaterials, Source={StaticResource viewModel}}"/>
            <DataGridTemplateColumn Header="Mass" Width="140">
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <Grid>
                    <Grid.ColumnDefinitions>
                      <ColumnDefinition />
                      <ColumnDefinition Width="20" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Mass}"/>
                    <Grid Grid.Column="1" MouseDown="Path_MouseDown">
                      <Path Grid.Column="1"
                          HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          Data="M 0 0 L 4 4 L 8 0 Z" >
                        <Path.Fill>
                          <SolidColorBrush Color="{DynamicResource GlyphColor}"/>
                        </Path.Fill>
                      </Path>
                    </Grid>
                  </Grid>
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
              <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                  <ComboBox
                    ItemsSource="{Binding CurrentMaterial, Source={StaticResource viewModel}}"
                    SelectedValue="{Binding Mass, UpdateSourceTrigger=PropertyChanged}"/>
                </DataTemplate>
              </DataGridTemplateColumn.CellEditingTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Operation">
              <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                  <Button Content="Delete Record" Margin="2"
                          Command="{Binding CmdDelete, Source={StaticResource viewModel}}" 
                          CommandParameter="{Binding}" />
                </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
          </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </UserControl>
    

    CodeBehind UserControl:

    using System;
    using System.Collections;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;
    namespace WpfApp004
    {
    	/// <summary>
    	/// Interaction logic for UserControl2.xaml
    	/// </summary>
    	public partial class UserControl2 : UserControl
    	{
    		public UserControl2()
    		{
    			InitializeComponent();
    		}
    		public static readonly DependencyProperty DataGridItemsSourceProperty =
    				DependencyProperty.Register("DataGridItemsSource", typeof(IEnumerable), typeof(UserControl2), new PropertyMetadata(null));
    		public IEnumerable DataGridItemsSource
    		{
    			get { return (IEnumerable)GetValue(DataGridItemsSourceProperty); }
    			set { SetValue(DataGridItemsSourceProperty, value); }
    		}
    		private void Path_MouseDown(object sender, MouseButtonEventArgs e)
    		{
    			HitTestResult hitTestResult = VisualTreeHelper.HitTest(dg, e.GetPosition(dg));
    			DataGridRow dataGridRow = hitTestResult.VisualHit.GetParentOfType<DataGridRow>();
    			if (dataGridRow == null) return;
    			int index = dataGridRow.GetIndex();
    			var item = dg.Items[index];
    			if (dg.SelectedItem != item || dg.SelectedItem == null) dg.SelectedItem = item;
    			DataGridCell dgc = hitTestResult.VisualHit.GetParentOfType<DataGridCell>();
    			if (dgc == null) return;
    			dgc.Focus();
    			dg.BeginEdit();
    		}
    		public T GetParentOfType<T>(DependencyObject element) where T : DependencyObject
    		{
    			Type type = typeof(T);
    			if (element == null) return null;
    			DependencyObject parent = VisualTreeHelper.GetParent(element);
    			if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
    			if (parent == null) return null;
    			else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
    			return GetParentOfType<T>(parent);
    		}
    	}
    }
    

    Other Code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    using WpfApp1;
    namespace WpfApp004
    {
    	public class ViewModelMain : INotifyPropertyChanged
    	{
    		public ViewModelMain()
    		{
    			DAL da = new DAL();
    			da.LoadData();
    			cvsModerators.Source = da.GetModerators();
    		}
    		private CollectionViewSource cvsModerators = new CollectionViewSource();
    		public ICollectionView AllModerators { get => cvsModerators.View; }
    		public ICommand Cmd { get => new RelayCommand(CmdExec); }
    		private void CmdExec(object obj)
    		{
    			switch (obj.ToString())
    			{
    				case "AddModerator":
    					(new DAL()).AddModerator();
    					break;
    				case "SaveAll":
    					(new DAL()).SaveAll();
    					break;
    				case "CancelAll":
    					(new DAL()).CancelAll();
    					OnPropertyChanged(nameof(AllModerators));
    					break;
    				default:
    					break;
    			}
    		}
    		public event PropertyChangedEventHandler PropertyChanged;
    		private void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    	}
    	public partial class ModeratorsDataGridViewModel
    	{
    		public List<String> AllMaterials { get => new DAL().GetMaterials(); }
    		public DataRowView SelectedItem { get; set; }
    		public List<String> CurrentMaterial { get => new DAL().GetMassForMaterial(SelectedItem); }
    		public ICommand CmdDelete { get => new RelayCommand(CmdDeleteExec); }
    		private void CmdDeleteExec(object obj) => (obj as DataRowView).Delete();
    		public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
    		{
    			if (depObj == null) return null;
    			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    			{
    				var child = VisualTreeHelper.GetChild(depObj, i);
    				var result = (child as T) ?? GetChildOfType<T>(child);
    				if (result != null) return result;
    			}
    			return null;
    		}
    	}
    	public class DAL
    	{
    		private string ConnString = WpfApp1.Properties.Settings.Default.cnSQL_CollegDB;
    		private static Window004DS ds;
    		public void LoadData()
    		{
    			ds = new Window004DS();
    			using (SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Moderators", ConnString)) da.Fill(ds.Moderators);
    			using (SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM ModeratorType", ConnString)) da.Fill(ds.ModeratorType);
    		}
    		internal IEnumerable<DataRow> GetModerators() => ds.Moderators;
    		internal List<string> GetMaterials() => new List<string>(ds.ModeratorType.AsEnumerable().Select(row => row.Moderator).Distinct());
    		internal List<string> GetMassForMaterial(DataRowView item)
    		{
    			if (item == null) return new List<string>();
    			string mat = ((Window004DS.ModeratorsRow)item.Row).Material;
    			return new List<string>(ds.ModeratorType.AsEnumerable().Where(row => row.Moderator == mat).Select(row => row.Mass).Distinct());
    		}
    		internal void AddModerator() => ds.Moderators.Rows.Add(ds.Moderators.NewRow());
    		internal void SaveAll()
    		{
    			using (SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Moderators", ConnString))
    			{
    				SqlCommandBuilder cb = new SqlCommandBuilder(da);
    				da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
    				da.RowUpdated += OnRowUpdated;
    				da.Update(ds.Moderators);
    			}
    		}
    		private void OnRowUpdated(object sender, SqlRowUpdatedEventArgs e)
    		{
    			// Include a variable and a command to retrieve the identity value from the Access database.
    			SqlCommand idCMD = new SqlCommand("SELECT @@IDENTITY", e.Command.Connection);
    			if (e.StatementType != StatementType.Insert) return;
    			// Retrieve the identity value and store it in the ID column.
    			object newID = idCMD.ExecuteScalar();
    			// ID column
    			DataColumn primCol = e.Row.Table.PrimaryKey[0];
    			e.Row[primCol] = Convert.ChangeType(newID, primCol.DataType);
    		}
    		internal void CancelAll() => ds.RejectChanges();
    	}
    	public class RelayCommand : ICommand
    	{
    		private readonly Predicate<object> _canExecute;
    		private readonly Action<object> _action;
    		public RelayCommand(Action<object> action) { _action = action; _canExecute = null; }
    		public RelayCommand(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
    		public void Execute(object o) => _action(o);
    		public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
    		public event EventHandler CanExecuteChanged
    		{
    			add { CommandManager.RequerySuggested += value; }
    			remove { CommandManager.RequerySuggested -= value; }
    		}
    	}
    }
    namespace WpfApp004
    {
    	public static class MyExtensions
    	{
    		public static T GetParentOfType<T>(this DependencyObject element) where T : DependencyObject
    		{
    			Type type = typeof(T);
    			if (element == null) return null;
    			DependencyObject parent = VisualTreeHelper.GetParent(element);
    			if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
    			if (parent == null) return null;
    			else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
    			return GetParentOfType<T>(parent);
    		}
    	}
    }
    

    Result:

    x

    0 comments No comments

  2. Yvon 50 Reputation points
    2024-04-12T17:52:57.69+00:00

    @Peter Fleischer (former MVP)

    I can't change the schema on the "Moderators and ModeratorTYpe". They are very old schema. I can't use ID as primary key.

    They are overloading tables. Therefore, the code you have I still can't display the initial value on the material columns.

    User's image


  3. Yvon 50 Reputation points
    2024-04-12T23:25:28.7266667+00:00

    Need to turn on this property. The initial values show<!--local:ModeratorsDataGridViewModel.AttProp="True">-->

    The Add/Delete and SaveAll are not working.

    The Add with the drop down on mass return empty. Delete has removed a record, but when 'SaveAll', nothing happened