Search code examples
c#wpfdatagridcatel

Raise PropertyChanged in a nested ObservableCollection (a matrix of values)


Hi i have a matrix of objects which is binded to a DataGrid:

In the ViewMOdel the matrix itself is packed into a ObservableCollection:

public ObservableCollection<ObservableCollection<object>> MyMatrix {get; set;}

My Problem is when i change the values in the Datagrid, those values are not updated in the binded collection right away.

How can i make the datagrid update its datasource right away after typing in one cell?

Here are my codes:

XAML:

<Window x:Class="TestMatrix.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:Behaviours="clr-namespace:TestMatrix.ViewModels.Helpers.Behaviours"
        xmlns:ViewModels="clr-namespace:TestMatrix.ViewModels">

    <Window.DataContext>
        <ViewModels:MainWindowViewModel/>
    </Window.DataContext>

    <StackPanel Orientation="Vertical">
        <ToolBar VerticalAlignment="Top"            Height="Auto">
            <Button Grid.Row="1" Height="50" Width="200"  Content="Show" Command="{Binding ShowCmd}"/>
        </ToolBar>
        <ToolBar VerticalAlignment="Top"            Height="Auto">
            <TextBox Width="200" Text="{Binding Data.AString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        </ToolBar>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>

            <DataGrid AutoGenerateColumns="False"                               
                                        Width="Auto"
                                        Height="Auto"
                                        Margin="3"
                                        FontSize="15"
                                        FontWeight="Bold"
                                        CanUserAddRows="False" 
                                        CanUserDeleteRows="False"
                                        CanUserReorderColumns="False" 
                                        CanUserSortColumns="False"
                                        ItemsSource="{Binding Data.MarketerMixMatrix, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                        Behaviours:DataGridColumnsBehavior.BindableColumns="{Binding OnlineMarketerMixColumns, Mode=TwoWay}">

            </DataGrid>
        </Grid>
    </StackPanel>         </Window>

ViewModel:

using Catel.MVVM;
using System;
using Catel.Data;
using TestMatrix.Models;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Data;
using System.Collections.Generic;


namespace TestMatrix.ViewModels
{
    /// <summary>
    /// name view model.
    /// </summary>
    public class MainWindowViewModel : ViewModelBase
    {
        #region Properties
        /// <summary>
        /// Gets or sets the property value.
        /// </summary>
        public ObservableCollection<DataGridColumn> OnlineMarketerMixColumns
        {
            get { return GetValue<ObservableCollection<DataGridColumn>>(OnlineMarketerMixColumnsProperty); }
            set { SetValue(OnlineMarketerMixColumnsProperty, value); }
        }

        /// <summary>
        /// Register the OnlineMarketerMixColumns property so it is known in the class.
        /// </summary>
        public static readonly PropertyData OnlineMarketerMixColumnsProperty = RegisterProperty("OnlineMarketerMixColumns", typeof(ObservableCollection<DataGridColumn>), null);

        /// <summary>
        /// Gets or sets the property value.
        /// </summary>
        public ExtractedOnlineData Data
        {
            get { return GetValue<ExtractedOnlineData>(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        /// <summary>
        /// Register the Data property so it is known in the class.
        /// </summary>
        public static readonly PropertyData DataProperty = RegisterProperty("Data", typeof(ExtractedOnlineData), null);

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
        /// </summary>
        public MainWindowViewModel()
        {

            Data = new ExtractedOnlineData();

            ObservableCollection<ObservableCollection<object>> aMatrix = new ObservableCollection<ObservableCollection<object>>();
            ObservableCollection<object> innerMatrix = new ObservableCollection<object>() { 1, 2, 3, 4 };
            aMatrix.Add(innerMatrix);
            Data.MarketerMixMatrix = aMatrix;

            OnlineMarketerMixColumns = new ObservableCollection<DataGridColumn>();

            for (int i = 0; i < 4; i++)
            {
                var column = new DataGridTextColumn();

                OnlineMarketerMixColumns.Add(
                    new DataGridTextColumn
                    {
                        Header = string.Format("ColumnNumber"+i),
                        Binding = new Binding(string.Format("[{0}]", i))
                    });
            }
        }}

MOdel:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace TestMatrix.Models
{
    public class ExtractedOnlineData : INotifyPropertyChanged
    {
        public ExtractedOnlineData()
        {
            this.ColumnNames = new List<string>();
            this.MarketerMixMatrix = new ObservableCollection<ObservableCollection<object>>();

        }

        private ObservableCollection<ObservableCollection<object>> marketerMixMatrix;

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Spaltenname der eingelesene Datei.
        /// </summary>
        public List<string> ColumnNames { get; set; }


        /// <summary>
        /// Vermarkter-Matrix.
        /// </summary>
        public ObservableCollection<ObservableCollection<object>> MarketerMixMatrix
        {
            get { return marketerMixMatrix; }
            set { 
                marketerMixMatrix = value;
                RaisePropertyChanged("MarketerMixMatrix");
                }
        }

        /// <summary>
        /// 
        /// </summary>
        public string AString { get; set; }
    }
}

Solution

  • I see you attempted to solve your issue by using UpdateSourceTrigger=PropertyChanged on the ItemsSource of your DataGrid. What you want is the cell binding source to update on cell changes, not the DataGrid items.

    With that reasoning, it becomes clear that you need to adjust the binding when instantiating the DataGridTextColumn objects of your OnlineMarketerMixColumns property:

    for (int i = 0; i < 4; i++)
    {
        OnlineMarketerMixColumns.Add(
            new DataGridTextColumn
            {
                Header = string.Format("ColumnNumber"+i),
                Binding = new Binding(string.Format("[{0}]", i))
                {
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                },
            });
    }