1
votes

I have a model class, Book, which contains a Keywords property:

public class Book : INotifyPropertyChanged
{
    private ObservableCollection<string> _keywords;
    ...
    public ObservableCollection<string> Keywords
    {
        get => _keywords;
        set
        {
            _keywords = value;
            OnPropertyChanged("Keywords");
        }
    }
}

and in my MainPage I have 2 components : a list View and a combobox whose each entry is a checkBox:

<ComboBox
    x:Name="cbb_Keywords"
    Grid.Column="2"
    Width="300"
    Margin="5,0,0,0"
    HorizontalAlignment="Left"
    ItemsSource="{Binding Source={StaticResource AllBooks}}"
    DataContext="{Binding ElementName=listBoxBooks,Path=SelectedItem,UpdateSourceTrigger=PropertyChanged}">


    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox Width="200" Content="{Binding}" Click="ButtonBase_OnClick">
                    <CheckBox.IsChecked>
                        <MultiBinding Converter="{StaticResource TextInListTrueFalseConverter}" Mode="OneWay">
                            <Binding ElementName="listBoxBooks" Path="SelectedItem.KeywordsForTextbox" Mode="OneWay"></Binding>
                            <Binding RelativeSource="{RelativeSource Self}" Path="Content"></Binding>
                        </MultiBinding>
                    </CheckBox.IsChecked>
                </CheckBox>
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

the checkBox.IsChecked multibinding is oneway, and when I click on a checkbox, it calls this method:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    CheckBox cb = (CheckBox)sender;
    var content = (string)cb.Content;
    var keywords = ((Book)listBoxBooks.SelectedItem).Keywords;
    bool clicked = cb.IsChecked.Value;
    if (clicked)
        keywords.Add(content);
    else
        keywords.Remove(content);  
}

it works more or less but there are 2 caveats:

  • sometimes the checkbox on which I just clicked is displayed in the combobox's checkbox, which is not expected and is annoying

  • I have, in addition of the combobox, an other component, a textbox, which contains the list of the keywords for the listview's selectedItem:

here

but when I click on a checkbox to toogle this, the listbox containing the list is not refreshed...

so I chenged a little my Keywords property, in Book:

public ObservableCollection<string> Keywords
{
    get => _keywords;
    set
    {
        _keywords = value;
        OnPropertyChanged("Keywords");
        OnPropertyChanged("KeywordsForTextbox");
    }
}

and the KeywordsForTextbox property is like this:

public string KeywordsForTextbox
{
    get { return string.Join(",", _keywords); }    
}

finally, to be complete, here is the textBox component in my MainWindow:

<TextBox x:Name="txb_Keywords"
         Grid.Column="1"
         Width="500"
         Text="{Binding ElementName=listBoxBooks,Path=SelectedItem.KeywordsForTextbox,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" />

why does the checkbox appears in the combobox's textbox? why isn't refreshed the other textbox?

thank you.

1

1 Answers

1
votes

The problem is that when modifying the Keywords collection the actual Keywords property doesn't change. It's still the same collection object. Only the object's properties (Items) change.

In your Book class you could use methods to do the adding, and removing, then notify property changed from there.

public void AddKeyword(string name)
{
    Keywords.Add(name);
    OnPropertyChanged("Keywords");
}

public void RemoveKeyword(string name)
{
    Keywords.Remove(name);
    OnPropertyChanged("Keywords");
}

Then change your event like this.

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    CheckBox cb = (CheckBox)sender;
    var content = (string)cb.Content;
    var book = ((Book)listBoxBooks.SelectedItem);
    bool clicked = cb.IsChecked.Value;
    if (clicked)
        book.AddKeyword(content);
    else
        book.RemoveKeyword(content);
}