'WPF two-way binding does not work for checkboxes inside a combobox

I've tried lots of solutions online but I still get this issue. I have a combobox whose item source is a list of customized class. Each element in the source is displayed as a checkbox in the combobox. Now I have a button whose "Click" function is to uncheck all the checkboxes.

The customized class:

    public class LinkObject: INotifyPropertyChanged
    {
        public int index { set; get; }
        public string LO_Name { set; get; }
        private bool _checkStatus { set; get; }

        public event PropertyChangedEventHandler PropertyChanged;

        public void Notify(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public bool checkStatus
        {
            get { return _checkStatus; }
            set
            {
                _checkStatus = value;
                Notify("IsChecked");
            }
        }
    }

The XAML:

<ComboBox Name="cbx1" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="126,82,0,0" VerticalAlignment="Top" Width="50" Height="20" IsEditable="True" IsTextSearchEnabled="True" StaysOpenOnEdit="True" TextBoxBase.TextChanged="cbx1_TextChanged">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Name="cbk1" IsChecked="{Binding checkStatus, Mode=TwoWay}" CommandParameter="{Binding index}" Checked="chk_Checked"  Unchecked="chk_Unchecked">
                <CheckBox.Content>
                    <TextBlock Text="{Binding LO_Name}"/>
                </CheckBox.Content>
            </CheckBox>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

Main function by initialization:

cbx1.ItemsSource = LinkObjectsList_cbx1;

LinkObjectsList_cbx1 is a List<LinkObject>.

The button has a name "clearAllTopView", the click function is:

private void clearAllTopViewBtn_Click(object sender, RoutedEventArgs e)
{
    LinkObjectsList_cbx1.Where(l => l.checkStatus == true).ToList().ForEach(lo => lo.checkStatus = false);
}

But when I click the button, nothing happens. Could someone give me a hint or advice? Thank you.



Solution 1:[1]

You have an accidental error due to name confusion. In order not to make such simple but subtle mistakes, I advise you to use a simple base class, but with a better implementation of INPC.

Here is my version of this class. Copy it and include it in your Solution.

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace Simplified
{
    /// <summary>Base class with implementation of the <see cref="INotifyPropertyChanged"/> interface.</summary>
    public abstract class BaseInpc : INotifyPropertyChanged
    {
        /// <inheritdoc cref="INotifyPropertyChanged"/>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>The protected method for raising the event <see cref = "PropertyChanged"/>.</summary>
        /// <param name="propertyName">The name of the changed property.
        /// If the value is not specified, the name of the method in which the call was made is used.</param>
        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary> Protected method for assigning a value to a field and raising 
        /// an event <see cref = "PropertyChanged" />. </summary>
        /// <typeparam name = "T"> The type of the field and assigned value. </typeparam>
        /// <param name = "propertyField"> Field reference. </param>
        /// <param name = "newValue"> The value to assign. </param>
        /// <param name = "propertyName"> The name of the changed property.
        /// If no value is specified, then the name of the method 
        /// in which the call was made is used. </param>
        /// <remarks> The method is intended for use in the property setter. <br/>
        /// To check for changes,
        /// used the <see cref = "object.Equals (object, object)" /> method.
        /// If the assigned value is not equivalent to the field value,
        /// then it is assigned to the field. <br/>
        /// After the assignment, an event is created <see cref = "PropertyChanged" />
        /// by calling the method <see cref = "RaisePropertyChanged (string)" />
        /// passing the parameter <paramref name = "propertyName" />. <br/>
        /// After the event is created,
        /// the <see cref = "OnPropertyChanged (string, object, object)" />
        /// method is called. </remarks>
        protected void Set<T>(ref T propertyField, T newValue, [CallerMemberName] string propertyName = null)
        {
            if (!object.Equals(propertyField, newValue))
            {
                T oldValue = propertyField;
                propertyField = newValue;
                RaisePropertyChanged(propertyName);

                OnPropertyChanged(propertyName, oldValue, newValue);
            }
        }

        /// <summary> The protected virtual method is called after the property has been assigned a value and after the event is raised <see cref = "PropertyChanged" />. </summary>
        /// <param name = "propertyName"> The name of the changed property. </param>
        /// <param name = "oldValue"> The old value of the property. </param>
        /// <param name = "newValue"> The new value of the property. </param>
        /// <remarks> Can be overridden in derived classes to respond to property value changes. <br/>
        /// It is recommended to call the base method as the first operator in the overridden method. <br/>
        /// If the overridden method does not call the base class, then an unwanted change in the base class logic is possible. </remarks>
        protected virtual void OnPropertyChanged(string propertyName, object oldValue, object newValue) { }
    }
}

With this basic implementation, your entity class will have code like this:

using Simplified;

namespace Febr20y
{
    public class LinkObject : BaseInpc
    {
        public int Index { set; get; }
        public string LO_Name { set; get; }
        private bool _checkStatus;

       public bool CheckStatus { get => _checkStatus; set => Set(ref _checkStatus, value); }
    }
}

This is not relevant to your question, but I advise you to follow the Naming Guidelines. Namely, property names must begin with the Upper letter.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 GregC