smally
smally

Reputation: 463

XAML Binding Failures on ComboBox

I have created a ComboBox that adds an item inside XAML, this is so I can have an uneditable ComboBox with an option that will set the binded item null.

ComboBox Clear Screenshot

The following code works, however Visual Studio is reporting a few XAML binding failures that I don't know how to fix.

<ComboBox x:Name="NewNewComboBox"
          SelectedValuePath="CompCategoryId"
          DisplayMemberPath="CategoryName">
    <ComboBox.Resources>
        <CollectionViewSource x:Key="MyCollection" Source="{Binding Categories}"/>
    </ComboBox.Resources>
    <ComboBox.ItemsSource>
        <CompositeCollection>
            <ComboBoxItem FontStyle="Italic" FontSize="8px" Content="(clear)" />
            <CollectionContainer Collection="{Binding Source={StaticResource MyCollection}}"  />
        </CompositeCollection>
    </ComboBox.ItemsSource>
    <ComboBox.SelectedValue>
        <Binding Path="CompCategoryId"/>
    </ComboBox.SelectedValue>
</ComboBox>

XAML Binding Failures Screenshot

Note: A normal ComboBox (without the (clear) item) does not show any failures.

Edit: I would like the solution to avoid code specific to the class the ComboBox uses because I want to make this ComboBox as a UserControl so it can be reused on different class (which will all have an int Id and string Name, already defined by the SelectedValuePath and DisplayMemberPath)

I have amended the ComboBoxItem and no longer receive XAML Binding Failures. But I cannot edit the FontStyle of it (and I have no clue how to implement this as a re-usable UserControl)

<ComboBox x:Name="NewNewComboBox"
          SelectedValuePath="CompCategoryId"
          DisplayMemberPath="CategoryName"
          SelectionChanged="NewNewComboBox_SelectionChanged">
    <ComboBox.Resources>
        <CollectionViewSource x:Key="MyCollection" Source="{Binding Categories}"/>
    </ComboBox.Resources>
    <ComboBox.ItemsSource>
        <CompositeCollection>
            <m:CompCategory CategoryName="(clear)" CompCategoryId="0"/>
            <CollectionContainer Collection="{Binding Source={StaticResource MyCollection}}"  />
        </CompositeCollection>
    </ComboBox.ItemsSource>
    <ComboBox.SelectedValue>
        <Binding Path="CompCategoryId"/>
    </ComboBox.SelectedValue>
</ComboBox>

private void NewNewComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (NewNewComboBox.SelectedIndex == 0) NewNewComboBox.SelectedValue = null;
}

Upvotes: 0

Views: 316

Answers (2)

EldHasp
EldHasp

Reputation: 7978

You specify bindings to the properties of an element (type Category?) of the Categories collection.
But your CombobBox is not bound to the Categories collection, but to the CompositeCollection.
And the first item in this collection is of type ComboBoxItem.
And this type has no properties to which you create bindings.
Notice the "DataContext" column in the binding errors window.
There is a ComboBoxItem type, not a Category type.

To solve this, you can add a new Category element with the necessary values ​​to the CompositeCollection instead of the ComboBoxItem.
But it will be more convenient if you create a static empty instance.
Then, if necessary, it will be possible to compare with it to determine that although it is not null, it is still an empty instance.

    public class Category
    {
       // Some code

       public static Category Empty {get;} = new Category() {.....};
    }
<ComboBox x:Name="NewNewComboBox"
          SelectedValuePath="CompCategoryId"
          SelectedValue="{Binding CompCategoryId}"
          DisplayMemberPath="CategoryName"
          ItemsSource="{DynamicResource MyCollection}}">
    <ComboBox.Resources>
        <CompositeCollection x:Key="MyCollection">
            <x:Static Member="local:Category.Empty"/>
            <CollectionContainer Collection="{Binding Categories}"/>
        </CompositeCollection>
    </ComboBox.Resources>
</ComboBox>

P.S. Additional recommendation. Although using SelectedValue and SelectedValuePath is possible, in MVVM concept when binding to ViewModel properties, in my opinion it is rather pointless.
Better to use the SelectedItem binding to a ViewModel property of type Category.
According to my personal experience, SelectedValue and SelectedValuePath are mainly used when implementing the solution "inside" the View, without using the ViewModel and MVVM.

But I cannot edit the FontStyle of it...

To style the elements, you must compare their data context with a null instance.
To do this, you need to create it somewhere, and only then add it to the collection.
If you do this only in XAML, then you need to create a null instance in resources.

<ComboBox x:Name="NewNewComboBox"
          SelectedValuePath="CompCategoryId"
          SelectedValue="{Binding CompCategoryId}"
          DisplayMemberPath="CategoryName"
          ItemsSource="{DynamicResource MyCollection}}"
          ItemContainerStyle="{DynamicResource itemStyle}">
    <ComboBox.Resources>
        <m:CompCategory x:Key="nullInstance"
                        CategoryName="(clear)"
                        CompCategoryId="0"/>
        <CompositeCollection x:Key="MyCollection">
            <StaticResource ResourceKey="nullInstance"/>
            <CollectionContainer Collection="{Binding Categories}"/>
        </CompositeCollection>
        <Style x:Key="itemStyle" TargetType="ComboBoxItem">
            <Style.Triggers>
                <DataTrigger Binding="{Binding}"
                             Value="{StaticResource nullInstance}">
                    <Setter Property="FontSize" Value="15"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                    <Setter Property="FontFamily" Value="arial"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ComboBox.Resources>
</ComboBox>

I would like the solution to avoid code specific to the class the ComboBox uses because I want to make this ComboBox as a UserControl so it can be reused on different class...

I don't think this is a good idea. The main purpose of a UserControl is to represent the data coming in its DataContext property. What do you expect the DataContext to receive? If you want some kind of versatility, then all types entering the DataContext must implement some kind of common interface. And it is by the implementation of this interface that UserControl will "understand" how to work with this data. Perhaps Custom Control would be a more better choice for your task.

But for the right advice, you need more details of what you want to implement.

Upvotes: 1

Anu Viswan
Anu Viswan

Reputation: 18163

The errors are due to absence of the CompCategoryId and CategoryName in the "Clear" item you had added. Or rather in other words, the newly added item is not of the type which matches the rest of collection and do not have properties which match the DisplayMemberPath and SelectedValuePath.

You could create an object that signifies a Null Category. For example,

public class NullCategory:Category
{
  public NullCategory()
  {
    CompCategoryId = -1;
    CategoryName = "Clear";
  }
}

Now you can add your Null Item as the following

 <CompositeCollection>
     <ViewModel:NullCategory />
     <CollectionContainer Collection="{Binding Source={StaticResource MyCollection}}"  />
</CompositeCollection>

Upvotes: 0

Related Questions