Search code examples
c#wpfxamlcombobox

WPF ComboBox with CheckBoxes - Wrong header text binded


I'm trying to display a custom text on the header of a combobox, following the solution given here:

wpf-combobox-with-checkboxes-display-info-about-checked-items

I achieved to get a similar result as below:

enter image description here

The header text is managed in a method from the VM, concatenating all texts (GroupName) or, if all are selected as here, display "All groups".

The code looks like the following (after deleting some other useless code for the example):

        <ComboBox Text="{Binding SelectedGroupNames, FallbackValue='[List of groups]'}"
                  IsEditable="True"
                  IsReadOnly="True"
                  ItemsSource="{Binding TestGroups}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" >
                        <CheckBox IsChecked="{Binding GroupEnabled}"
                                  Content="{Binding GroupName}"/>
                    </StackPanel>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>

And in the VM, the binded property is the following:

        private string selectedGroupNames = "All groups";
        public string SelectedGroupNames
        {
            get { return selectedGroupNames; }
            set { selectedGroupNames = value; OnPropertyChanged(nameof(SelectedGroupNames)); }
        }

For information, the code updating the SelectedGroupNames is the following (reduced for the example):

        private void Group_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if(TestGroups.Count() == TestGroups.GetAllEnabled().Count()) // Everything is selected
            {
                SelectedGroupNames = "All groups";
            }
            else if(TestGroups.GetAllEnabled().Count() == 0) // No test group is selected
            {
                SelectedGroupNames = "No group selected!";
            }
            else
            {
                SelectedGroupNames = string.Join(", ", TestGroups.GetAllEnabled().Select(x => x.GroupName)); // Some groups are selected
            }
        }

My issue happens when we select/click outside of a checkbox or a text: e.g. on the 2 mm padding left to the checkbox, or just on the right of the text. The Text displayed is not anymore a concatenation of the GroupName (property of my object) but the object itself: "Namespace.class.object".

The same as if I did "myObject.ToString()".

How can I avoid this situation?

  • Block the user to click somewhere else?

  • make a check on the string property "SelectedGroupNames"?

I already tried to make a check in the VM to see if the value is a string, but it's not working. It's always a string because of ToString() I suppose.

I tried the solution from Sinatr without success:

  • Added in the view: SelectedItem="{Binding SelectedTestGroup}"
  • and in the VM:
        public ITestGroup SelectedTestGroup
        {
            get { return selectedTestGroup; }
            set { selectedTestGroup = value; OnPropertyChanged(nameof(SelectedTestGroup)); OnPropertyChanged(nameof(SelectedGroupNames)); }
        }

Edit: following solution from Sinatr, I changed the binding mode to OneWay (previously empty) and used CurrentDispatcher to raise property changed:

        public ITestGroup SelectedTestGroup
        {
            get { return selectedTestGroup; }
            set { selectedTestGroup = value; Dispatcher.CurrentDispatcher.InvokeAsync(() => OnPropertyChanged(nameof(SelectedGroupNames)));  }
        }

And it works! Great


Solution

  • As suggested by Sinatr, one workaround was to use a fake property to update the text from the ComboBox, with the Dispatcher to produce a small delay:

        public ITestGroup SelectedTestGroup
        {
            get { return selectedTestGroup; }
            set { selectedTestGroup = value; Dispatcher.CurrentDispatcher.InvokeAsync(() => OnPropertyChanged(nameof(SelectedGroupNames)));  }
        }
    

    And secondly change the binding mode to OneWay:

    Text="{Binding SelectedGroupNames, FallbackValue='[List of groups]', Mode=OneWay}"