Search code examples
c#wpfdatagriddatatemplatetogglebutton

WPF MVVM: Datagrid Template ToggleButton not communicating with Model when clicked


This is my first question on here so bear with me.

I am stumped on why this ToggleButton column doesnt communicate changes to the Match model Reviewed property when toggled:

<ToggleButton    
    Height="auto" Foreground="White" Margin="0, 0, 10, 0"
    IsChecked="{Binding Reviewed, Mode=TwoWay}">      
</ToggleButton>

However, this column works fine to communicate with the Match model Reviewed property

<DataGridCheckBoxColumn Header="Test" Width="auto" Binding="{Binding Reviewed, Mode=TwoWay}"/>

I'll post some basic code to help understand my problem.

XAML Datagrid Example

  • ToggleButton does not communicate changes
  • DataGridCheckBoxColumn works great
<DataGrid ItemsSource="{Binding Path=ListOfBestMatches}" x:Name="DataGrid_Matches" Height="auto" Width="Auto" Margin="0,0,-4,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" AutoGenerateColumns="False">
                    <!-- Column Headers -->
                    <DataGrid.Columns>                   
                        <DataGridTemplateColumn Header="Reviewed">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <ToggleButton                                         
                                        Height="auto" Foreground="White" Margin="0, 0, 10, 0"
                                        IsChecked="{Binding Reviewed, Mode=TwoWay}">     
                                    </ToggleButton>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>

                        <DataGridCheckBoxColumn Header="Test" Width="auto" Binding="{Binding Reviewed, Mode=TwoWay}"/>

                    </DataGrid.Columns>
</DataGrid>

ViewModel

public class MainWindowViewModel : BaseViewModel 
{
        /// <summary>
        /// Property/field which holds the official list of best matches and bound to a datagrid
        /// </summary>
        private ObservableCollection<Match> _listOfBestMatches;
        public ObservableCollection<Match> ListOfBestMatches
        {
            get => _listOfBestMatches;
            set
            {
                if (_listOfBestMatches != value)
                {
                    _listOfBestMatches = value;
                    this.OnPropertyChanged("ListOfBestMatches");
                }
            }
        }
}

Model

public class Match : BaseViewModel
{
        /// <summary>
        /// Property which represents the state of the Check Toggle Box in the Best Matches Data grid Reviewed column
        /// </summary>
        private bool reviewed;
        public bool Reviewed
        {
            get { return reviewed; }
            set 
            {                
                    reviewed = value;
                    OnPropertyChanged("Reviewed");

                    Debug.WriteLine("=== SET REVIEWED ===");

            }
        
        }
}

Base View Model

namespace UserFolders.ViewModels
{

    /// <summary>
    /// This is the base view model which all other view models will extend from.
    /// This model is necesarry for notifying the UI when data has changed so it 
    /// can display appropriately on its own
    /// </summary>
    public class BaseViewModel : INotifyPropertyChanged
    {
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}


Solution

  • Finally found the solution to your problem. I guess, the problem was how data grid cells are updated. I have for example added some text columns and wondered, that they are only updated as soon as the datagrid cell lost focus. UpdateSourceTrigger=PropertyChanged (Which I had used before, but these things are easily forgotten^^) did the trick.

    Here is the changed code along with the text columns for testing:

       <DataGrid ItemsSource="{Binding Path=ListOfBestMatches}" x:Name="DataGrid_Matches" Height="auto" Width="Auto" Margin="0,0,-4,0" VerticalAlignment="Top" HorizontalAlignment="Stretch" AutoGenerateColumns="False"
                   >
            <!-- Column Headers -->
                      
            <DataGrid.Columns>
                <DataGridTextColumn Header="name"  Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTextColumn Header="name"  Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
                <DataGridTemplateColumn Header="Reviewed">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ToggleButton                                         
                                        Height="auto" Foreground="White" Margin="0, 0, 10, 0"
                                        IsChecked="{Binding Reviewed, UpdateSourceTrigger=PropertyChanged}">
                            </ToggleButton>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
    
                <DataGridCheckBoxColumn Header="Test" Binding="{Binding Reviewed, UpdateSourceTrigger=PropertyChanged}"/>
    
            </DataGrid.Columns>
        </DataGrid>
    

    You only need to add the corresponding property to your VM (or delete it):

    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }
    

    BTW: In case you are interested, I made a MVVM Pattern Example project for a course some time ago. It shows a bit of an updated example of the ViewModelBase class for example as well as a few other things. You can find it here: https://github.com/TheRealRolandDeschain/DesignPatternsMVVMExample