BoucherS: Please see my ".Xaml" and ".Xaml.Cs" code samples below and the 3 occurrences of the Comment that says: "//!!!WARNING!!!: The following Statement does NOT result in "OnUserGroupTypeToggled" being called!?!".
<Window x:Class="TestXceedCheckedComboBoxWpfApp.MainWindow"
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:TestXceedCheckedComboBoxWpfApp"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<StackPanel>
<xctk:CheckComboBox x:Name="UserGroupsTypesCheckedComboBox"
ItemsSource="{Binding UserGroupTypes}"
SelectedItemsOverride="{Binding SelectedUserGroupTypesOverride}"
DisplayMemberPath="Name"
Command="{Binding OnUserGroupTypeToggledCommand}"
ValueMemberPath="ID"
/>
<StackPanel Margin="0,200,0,0"
Orientation="Horizontal">
<TextBlock Text="SelectedItems: " />
<TextBlock Text="{Binding SelectedItemsOverride.Count, ElementName=UserGroupsCheckedComboBox}" />
</StackPanel>
<CheckBox Name="UserGroupTypesAllCheckBox"
IsChecked="{Binding UserGroupTypesAllChecked}"
VerticalAlignment="Center"
>
All
</CheckBox>
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace TestXceedCheckedComboBoxWpfApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private List<UserGroupType> userGroupTypes;
private ObservableCollection<UserGroupType> selectedUserGroupTypesOverride = new ObservableCollection<UserGroupType>();
private bool userGroupTypesAllChecked;
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
this.UserGroupTypes = new List<UserGroupType>()
{
new UserGroupType() { ID = 1, Name = "First" },
new UserGroupType() { ID = 2, Name = "Second" },
new UserGroupType() { ID = 3, Name = "Third" },
new UserGroupType() { ID = 4, Name = "Fourth" },
new UserGroupType() { ID = 5, Name = "Fifth" },
};
this.DataContext = this;
//!!!WARNING!!!: The following Statement does NOT result in "OnUserGroupTypeToggled" being called!?!
this.SelectedUserGroupTypesOverride = new ObservableCollection<UserGroupType>() { this.UserGroupTypes[0] };
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//!!!WARNING!!!: The following Statement does NOT result in "OnUserGroupTypeToggled" being called!?!
this.SelectedUserGroupTypesOverride = new ObservableCollection<UserGroupType>() { this.UserGroupTypes[1] };
//!!!WARNING!!!: The following Statement does NOT result in "OnUserGroupTypeToggled" being called -
// UNTIL an item is toggled on/off via the U.I.!?!
this.UserGroupTypesAllCheckBox.IsChecked = true;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand _OnUserGroupTypeToggled { get; private set; }
public ICommand OnUserGroupTypeToggledCommand
{
get
{
return _OnUserGroupTypeToggled
??
(
_OnUserGroupTypeToggled =
(
new RelayCommand(execute: OnUserGroupTypeToggled)
)
);
}
}
public List<UserGroupType> UserGroupTypes
{
get
{
return userGroupTypes;
}
set
{
if (userGroupTypes != value)
{
userGroupTypes = value;
OnPropertyChanged();
}
}
}
public bool UserGroupTypesAllChecked
{
get
{
return userGroupTypesAllChecked;
}
set
{
if (userGroupTypesAllChecked != value)
{
userGroupTypesAllChecked = value;
// BEGIN Force select / clear all UserGroupTypes depending on whether userGroupTypesAllChecked is true.
// Build new empty (AllChecked = false) / full (AllChecked = true) selected items list in a temporary Collection
// to avoid the normal ObservableCollection notifying the View until all changes are made.
var tempSelectedUserGroupTypes = new ObservableCollection<UserGroupType>();
if (userGroupTypesAllChecked == true)
{
UserGroupTypes.ForEach(ug => tempSelectedUserGroupTypes.Add(ug));
}
SelectedUserGroupTypesOverride = tempSelectedUserGroupTypes;
// END Force select / clear all UserGroupTypes depending on whether userGroupTypesAllChecked is true.
//OnPropertyChanged();
} // if (userGroupTypesAllChecked != value)
} // set
}
public ObservableCollection<UserGroupType> SelectedUserGroupTypesOverride
{
get
{
return selectedUserGroupTypesOverride;
}
set
{
if (selectedUserGroupTypesOverride != value)
{
selectedUserGroupTypesOverride = value;
OnPropertyChanged();
}
}
}
public void OnUserGroupTypeToggled(object parameter)
{
System.Diagnostics.Debug.WriteLine("OnUserGroupTypeToggled.");
var selectedUserGroupType = (UserGroupType)parameter;
if (selectedUserGroupTypesOverride.Count < userGroupTypes.Count)
{
// Change the Backing Variable vs. the Bound Property, so its Setter won't clear all selections.
userGroupTypesAllChecked = false;
// Notify View so it'll clear the checkbox.
OnPropertyChanged(propertyName: nameof(UserGroupTypesAllChecked));
}
else if (selectedUserGroupTypesOverride.Count == userGroupTypes.Count)
{
// Change the Backing Variable vs. the Bound Property, so its Setter won't clear all selections.
userGroupTypesAllChecked = true;
// Notify View so it'll clear the checkbox.
OnPropertyChanged(propertyName: nameof(UserGroupTypesAllChecked));
}
}
}
public class UserGroupType
{
public string Name
{
get;
set;
}
public int ID
{
get;
set;
}
}
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException(nameof(execute));
if (canExecute == null)
throw new ArgumentNullException(nameof(canExecute));
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
}