1
votes

I am using a compositecollection:

  • Comboboxitem with content "Select a vendor"
  • Collectioncontainer bound to a Observablecollection of Vendor objects

The desired functionality: the user has to select a vendor from the combobox. Selecting "Select a vendor" sets the Vendor property in the viewmodel to null.

I am getting an binding error. Any ideas how to fix this?

Error when debugging

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')

XAML

<ComboBox Name='cmbVendor'
            SelectedItem='{Binding Vendor, Converter={StaticResource ComboboxConverter}, Mode=TwoWay}'
            IsSynchronizedWithCurrentItem='True'>
    <ComboBox.Resources>
      <CollectionViewSource x:Key='VendorsCollection'
                            Source='{Binding Vendors}' />
      <DataTemplate DataType='{x:Type ComboBoxItem}'>
        <TextBlock Text='{Binding Content}' />
      </DataTemplate>
      <DataTemplate DataType='{x:Type objects:Vendor}'>
        <StackPanel>
          <TextBlock Text='{Binding Name}' />
        </StackPanel>
      </DataTemplate>
    </ComboBox.Resources>
    <ComboBox.ItemsSource>
      <CompositeCollection>
        <ComboBoxItem Content='Select a vendor' />
        <CollectionContainer Collection='{Binding Source={StaticResource VendorsCollection}}' />
      </CompositeCollection>
    </ComboBox.ItemsSource>
  </ComboBox>

ComboboxConverter

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {

      var vendor = value as Vendor;
        if (vendor != null)
        {
            return vendor;
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var vendor = value as Vendor;
        if (vendor != null)
        {
            return vendor;
        }

        var comboboxItem = value as ComboBoxItem;
        if (comboboxItem != null)
        {
            return null;
        }
        return null;
    }
4

4 Answers

2
votes

Add to your App.xaml

<Style TargetType="{x:Type ComboBoxItem}">
    <Setter Property="HorizontalContentAlignment" Value="Left" />
    <Setter Property="VerticalContentAlignment" Value="Top" />
</Style>
0
votes

These binding errors are harmless and handled internally so you can safely just ignore them, or suppress them. Please refer to the following link for more information.

Resolving harmless binding errors in WPF: https://weblogs.asp.net/akjoshi/resolving-un-harmful-binding-errors-in-wpf

0
votes

Add to your App.xaml

<!-- https://stackguides.com/questions/47391020/cannot-find-source-for-binding-with-reference-relativesource-findancestor -->
<Style 
    x:Key="BugFreeListViewItemStyle"
    TargetType="{x:Type ListViewItem}"
    >
    <Setter Property="HorizontalContentAlignment" Value="Left" />
    <Setter Property="VerticalContentAlignment" Value="Top"/>
</Style>

and use it:

<ListView ItemContainerStyle="{StaticResource BugFreeListViewItemStyle}"/>
0
votes

The ComboBox (and other WPF controls) by default use a VirtualizingPanel for the ItemsPanel. If you change your items panel to not be a VirtualizingPanel or if you set the VirtualizingPanel.IsVirtualizing attached property to false, you will not see this runtime error.

<ComboBox Name="cmbVendor"
          SelectedItem="{Binding Vendor, Converter={StaticResource ComboboxConverter}, Mode=TwoWay}"
          IsSynchronizedWithCurrentItem="True"
          VirtualizingPanel.IsVirtualizing="False">
</ComboBox>

or

<ComboBox Name="cmbVendor"
          SelectedItem="{Binding Vendor, Converter={StaticResource ComboboxConverter}, Mode=TwoWay}"
          IsSynchronizedWithCurrentItem="True">
    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel IsItemsHost="True" />
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>
</ComboBox>

This is what worked for me instead of just ignoring the runtime error. I also tried changing the VirtualizationMode property of the VirtualizingStackPanel to both Standard and Recycling but that did not have any effect and the runtimes errors still occurred.

Finally, if you really do need virtualization because your ComboBox may contain many items (e.g. 100s) then I suggest overriding the Style or Template of the ComboBoxItem and setting the HorizontalContentAlignment and VerticalContentAlignment properties explicitly.

You can do this in the XAML Designer in Visual Studio by selecting your ComboBox in the Document Outline window and then right-clicking the ComboBox node and selecting Edit Additional Templates...Edit Generated Item Container (ItemContainerStyle). Then change the generated style for those two properties from:

<Style TargetType="{x:Type ComboBoxItem}">
    ...
    <Setter Property="HorizontalContentAlignment"
            Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
    <Setter Property="VerticalContentAlignment"
            Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
    ...
</Style>

to this or whatever you'd like:

<Style TargetType="{x:Type ComboBoxItem}">
    ...
    <Setter Property="HorizontalContentAlignment"
            Value="Stretch" />
    <Setter Property="VerticalContentAlignment"
            Value="Stretch" />
    ...
</Style>