Search code examples
c#wpfdata-binding

DependencyProperty.unsetValue in a multibinding


I have a listView of items which are 'Book' instances, and when I click on a book the combobox should display its keywords; in fact it's a little trickier : the combobox contains the list of all the keywords of all books (duplicates removed)(the comboboxItems are checkboxes), and those of the selected book are checked. here is the multibinding:

<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,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">

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

When I run my program, it seems ok When I click on a book, but I get an exception when I click on the combobox : impossible cast from 'MS.Internal.NamedObject' to 'System.String' type. I saw that value[0] is UnsetValue.

At debugging, when I use spies to track the value of WpfApp1.App.Books[0].KeywordsForTextbox, it gives me the good value (a string which is the list of the keywords of Book[0]. maybe the problem comes from listboxBooks.SelectedItem.KeywordsForTextBox? I can't spy in VS the value of 'listboxBooks'.

some related content... the beginning of the constructor of MainWindow:

public MainWindow()
{
    InitializeComponent();
    listBoxBooks.ItemsSource = App.Books;

the convert method of the converter:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    var check = false;
    if ((values != null && values.Length == 2))
    {
        string listString = (string)values[0];
        string wordToFind = (string) values[1];
        if ((listString != null))
        {
            List<string> keywordsList = listString.Split(',').ToList();
            if (keywordsList.Contains(wordToFind)) check = true;
        }       
    }

    return check;
}

the KeywordsForTextbox method:

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

and finally the implementation of AllBooks:(as a window resource)

<ObjectDataProvider
    x:Key="AllBooks"
    MethodName="listOfAllKeywords"
    ObjectType="{x:Type mangmt:BookManagement}" />

thank you.


Solution

  • The first Binding of the Multi should be to be to the SelectedItem in the ListBox of Books. I have added in the <CheckBox.IsChecked> where appropriate, and Content="{Binding}" to the CheckBox:

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

    You may also wish to add some validation to the IMultiValueConverter to make sure the passed values are not unset, to avoid an exception: If Not values(0) Is DependencyProperty.UnsetValue And Not values(1) Is DependencyProperty.UnsetValue Then in VB.

    Regarding the behaviour on checking the checkbox, I am guessing this is because of the ConvertBack Method of the IMultiValueConverter. You can remove the 'Throw Exception' code, and write a method to add/remove the text of the checked/unchecked box to your keyword list.