I have a WPF custom Control, which contains a ComboBox. I'd like to bind the ItemsSource of this ComboBox to a Dependency Property of the custom control class via a CollectionViewSource, but I couldn't figure out how to make the CollectionViewSource recognizing the correct DataContext (my custom control properties, in this case).
I googled a lot, read almost all SO custom control\CollectionViewSource\Collection binding\dependency properties binding\etc. questions and tried the solutions of some similar questions like this one and this and some more and it's still not working. I probably miss something, but I don't know what.
Here are the related parts from the Custom Control class:
public class CustomComboBox : Control
{
static CustomComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomComboBox), new FrameworkPropertyMetadata(typeof(CustomComboBox)));
}
public CustomComboBox()
{
CustomItems = new ObservableCollection<ComboBoxItem>();
DataContext = this;
}
internal ObservableCollection<ComboBoxItem> CustomItems
{
get { return (ObservableCollection<ComboBoxItem>)GetValue(CustomItemsProperty); }
set { SetValue(CustomItemsProperty, value); }
}
// Using a DependencyProperty as the backing store for CustomItems. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CustomItemsProperty =
DependencyProperty.Register("CustomItems", typeof(ObservableCollection<ComboBoxItem>), typeof(CustomComboBox), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
private string text;
public string Text
{
get { return text; }
set
{
text = value;
OnPropertyChanged("Text");
}
}
// More properties, events and functions...
}
and the relevant parts of the xaml code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyNamespace"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
x:Class="CustomComboBox">
<CollectionViewSource x:Key="GroupedData"
Source="{Binding Path=CustomItems, RelativeSource={RelativeSource FindAncestor, AncestorType=local:CustomComboBox}, diag:PresentationTraceSources.TraceLevel=High}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="GroupName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<!-- ...Some more Styles and DataTemplates... -->
<Style TargetType="{x:Type local:CustomComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ComboBox IsEditable="True"
Text="{Binding Text}"
ItemsSource="{Binding Source={StaticResource GroupedData}}">
<!-- ...Some more properties... -->
</ComboBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
I get this output from System.Diagnostics:
System.Windows.Data Warning: 58 : Path:'CustomItems'
System.Windows.Data Warning: 60 : BindingExpression (hash=28932383): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=28932383): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=28932383): Attach to System.Windows.Data.CollectionViewSource.Source (hash=23914501)
System.Windows.Data Warning: 66 : BindingExpression (hash=28932383): RelativeSource (FindAncestor) requires tree context
System.Windows.Data Warning: 65 : BindingExpression (hash=28932383): Resolve source deferred
System.Windows.Data Warning: 67 : BindingExpression (hash=28932383): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=28932383): Found data context element: (OK)
System.Windows.Data Warning: 67 : BindingExpression (hash=28932383): Resolving source
System.Windows.Data Warning: 70 : BindingExpression (hash=28932383): Found data context element: (OK)
System.Windows.Data Warning: 67 : BindingExpression (hash=28932383): Resolving source (last chance)
System.Windows.Data Warning: 70 : BindingExpression (hash=28932383): Found data context element: (OK)
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyNamespace.CustomComboBox', AncestorLevel='1''. BindingExpression:Path=CustomItems; DataItem=null; target element is 'CollectionViewSource' (HashCode=23914501); target property is 'Source' (type 'Object')
I tried first this binding:
<CollectionViewSource x:Key="GroupedData"
Source="{Binding CustomItems}">
but then I get the error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=CustomItems; DataItem=null; target element is 'CollectionViewSource' (HashCode=37908782); target property is 'Source' (type 'Object')
BTW- binding inside the ComboBox to another properties works- like the Text binding.
Although @KyloRen's answer is generally correct (and the Inheritance Context is particularly useful), I'd like to offer alternative solution. The key point is that the CollectionViewSource in question should be defined as a resource of a FrameworkElement being a part of the template's visual tree (a good choice in my opinion is the root element). Here's the template:
<ControlTemplate TargetType="{x:Type local:CustomComboBox}">
<Border (...)>
<Border.Resources>
<CollectionViewSource x:Key="GroupedData"
Source="{Binding CustomItems, RelativeSource={RelativeSource TemplatedParent}}">
(...)
</CollectionViewSource>
</Border.Resources>
<ComboBox IsEditable="True"
Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}"
ItemsSource="{Binding Source={StaticResource GroupedData}}">
(...)
</ComboBox>
</Border>
</ControlTemplate>
Here are some advantages of this solution:
DataContext-independent, which lets you use it in any data context without breaking the functionality of the controlNote though that for the latter to be true you should modify the bindings within the template to use RelativeSource={RelativeSource TemplatedParent} (or use TemplateBinding where applicable). Also, I'd recommend removing the DataContext = this; line from the constructor, because it would prevent data context inheritance.
I see a couple of problems in your code and has why have you designed this way? But I'll skip all that part(you must have some reason to do so like setting the DataContext, initialization of CustomItems etc.) and just edit the XAML to make it work. To make your Binding work you need to understand the concept of Inheritance Context in WPF. Google it you'll find plenty of stuff about it. According to that I've changed your code as below:
<local:CustomComboBox>
<local:CustomComboBox.Resources>
<CollectionViewSource x:Key="GroupedData"
Source="{Binding Path=CustomItems}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="GroupName" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</local:CustomComboBox.Resources>
<local:CustomComboBox.Style>
<Style TargetType="{x:Type local:CustomComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ComboBox IsEditable="True"
Text="{Binding Text}"
ItemsSource="{Binding Source={StaticResource GroupedData}}">
</ComboBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:CustomComboBox.Style>
</local:CustomComboBox>
And it works. Above is not perfect but I just made minimal changes(change structure & binding) to make above code work. Also added DataSource as:
public CustomComboBox()
{
CustomItems = new ObservableCollection<ComboBoxItem>();
CustomItems.Add(new ComboBoxItem() { Content = "4" });
CustomItems.Add(new ComboBoxItem() { Content = "5" });
DataContext = this;
}
Output:

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With