0
votes

I have a WPF custom UserControl defined like that:

<UserControl x:Name="parent" [..]>
    <UserControl.Resources>
        <ResourceDictionary>
            <ObjectDataProvider x:Key="StatusValues" MethodName="GetValues" ObjectType="{x:Type core:Enum}">
                <ObjectDataProvider.MethodParameters>
                    <x:Type TypeName="s:Status" />
                </ObjectDataProvider.MethodParameters>
            </ObjectDataProvider>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid>
        <DataGrid ItemsSource="{Binding Data}">
            [..]
            <DataGridTextColumn Binding="{Binding Status}">
                <DataGridTextColumn.HeaderTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
                            <ComboBox SelectedItem="{Binding ElementName=parent, Path=DataContext.StatusFilter}">
                                <ComboBox.ItemsSource>
                                    <CompositeCollection>
                                        <ComboBoxItem Foreground="Black">All</ComboBoxItem>
                                        <CollectionContainer Collection="{Binding Source={StaticResource StatusValues}}" />
                                    </CompositeCollection>
                                </ComboBox.ItemsSource>
                            </ComboBox>
                        </StackPanel>
                    </DataTemplate>
                </DataGridTextColumn.HeaderTemplate>
            </DataGridTextColumn>
        </DataGrid>
    </Grid>
</UserControl>

I removed a lot of stuff for readability but I let what I think is essential for describing the problem. Do not hesitate to ask me for more code if needed.

The goal of this user control is to add filtering capabilities to the DataGrid. Among others columns, there is a Status column. When the user picks up a status in the header's combo-box, only the elements with that status are displayed in the grid.

Everything actually works well. The selected status is well bound to my view model. The rows are well filtered, etc. My only concern is about the following errors I am getting:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment; DataItem=null; target element is 'ComboBoxItem' (Name=''); target property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.ItemsControl', AncestorLevel='1''. BindingExpression:Path=VerticalContentAlignment; DataItem=null; target element is 'ComboBoxItem' (Name=''); target property is 'VerticalContentAlignment' (type 'VerticalAlignment')

I do not even use RelativeSource and all this stuff myself. My guess is that it is used under the hood by the framework or something.

But how can I avoid these errors? The application is running on .NET 4.5.2.

I have read solutions about setting the style of the ComboBoxItem, but it does not solve my problem. I have read threads explaining this happens when the collection source is filtered, but that's not the case of my ComboBox collection view.

1
Filtering is best done via a ICollectionView, such as CollectionViewSourceMikeT
That's what I am doing in the ViewModel. But the issue is not the filtering. It is the binding error I am getting on a binding I am not setting myself.fharreau
The erroneous binding is set by ComboBoxItem default style, because ComboBoxItem is designed to be used specifically in ComboBox.Items collection. Consider replacing it with some other type (preferably the type of StatusFilter property on your view-model).Grx70
@MikeT No, the error is thrown by the combobox mapping (when I comment out the CombBox, there no more errors, and look at the end of the error: target element is 'ComboBoxItem'). The line you are talking about is used to retrieve the DataGridColumn Header value.fharreau
@Grx70 as I said, I tried to set the style of the ComboBoxItem but it does not solve it. How would you do that? I may did somethong wrong!fharreau

1 Answers

1
votes

The problem is that you are Hosting a ComboboxItem inside of a ComboboxItem, a ComboboxItem is designed to be hosted in a Combobox and binds itself to the ComboBox's VerticalContentAlignment & HorizontalContentAlignment

this is because when you set the item source the binding creates a ComboboxItem with a ContentTemplate set to the Combobox's ItemTemplate and a DataContent set to the item in the item source

so you need to remove your ComboBoxItem from the composite collection and style the item container

eg

<ComboBox SelectedItem="{Binding Filter}">
    <ComboBox.ItemContainerStyle>
        <Style TargetType="ComboBoxItem">
            <Setter Property="Foreground" Value="Black"/> <!--Default Value-->
            <Style.Triggers>
                <DataTrigger Binding="{Binding}" Value="All"> <!--Using the default binding ie DataContext and if it has a Value of All do the following-->
                    <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ComboBox.ItemContainerStyle>

    <ComboBox.ItemsSource>
        <CompositeCollection>
            <clr:String xmlns:clr="clr-namespace:System;assembly=mscorlib">All</clr:String> 
            <CollectionContainer Collection="{Binding Mode=OneWay, Source={StaticResource Statuses}}" />
        </CompositeCollection>
    </ComboBox.ItemsSource>
</ComboBox>