As for question, I've created a custom UserControl
. It has an ItemsControl
which ItemsSource
is binded to a property bindable as DependecyProperty
.
MyControl XAML:
<Grid>
<ItemsControl ItemsSource="{Binding Path=InternalControl}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
MyControl Code:
public static readonly DependencyProperty SetInternalControlProperty =
DependencyProperty.Register(
nameof(ObservableCollection<dynamic>),
typeof(ObservableCollection<dynamic>),
typeof(UtilityExpander));
public ObservableCollection<dynamic> InternalControl
{
get { return (ObservableCollection<dynamic>)GetValue(SetInternalControlProperty); }
set { SetValue(SetInternalControlProperty, value); }
}
Main XAML:
<Controls:UtilityExpander InternalControl="{Binding GasControl}"/>
Main Code:
private ObservableCollection<UtilityGas> _gasControl;
public ObservableCollection<UtilityGas> GasControl { get => _gasControl; set { _gasControl = value; NotifyPropertyChanged(); } }
When running on the InitializeComponent()
I get
System.Windows.Markup.XamlParseException: 'A 'Binding' cannot be set on the 'InternalControl' property of type 'UtilityExpander'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.'
The reason why I'm using dynamic
is that I want to use the control with different type of IEnumerable<objects>
CodePudding user response:
There are two errors in the declaration of the dependency property.
The second argument of the Register method must be the name of the property, not that of the property's type. The backing field must be named like the property, with the suffix Property
.
public static readonly DependencyProperty InternalControlProperty =
DependencyProperty.Register(
nameof(InternalControl),
typeof(ObservableCollection<dynamic>),
typeof(UtilityExpander));
public ObservableCollection<dynamic> InternalControl
{
get { return (ObservableCollection<dynamic>)GetValue(InternalControlProperty); }
set { SetValue(InternalControlProperty, value); }
}
Besides that, the property would be more usable if it would be declared as IEnumerable<dynamic>
. Whether its value implements INotifyCollectionChanged
or not could be checked at runtime.
The dynamic
collection type argument seems also redundant. When you declare the property like shown below, you can assign any type of collection, even with different element types in a single collection instance. This is by the way how the ItemsSource
property is defined.
public static readonly DependencyProperty InternalControlProperty =
DependencyProperty.Register(
nameof(InternalControl),
typeof(IEnumerable),
typeof(UtilityExpander));
public IEnumerable InternalControl
{
get { return (IEnumerable)GetValue(InternalControlProperty); }
set { SetValue(InternalControlProperty, value); }
}
Finally, you should also avoid to set the DataContext of your control to itself, because that breaks the standard data binding behaviour of the control's properties, e.g. InternalControl="{Binding GasControl}"
.
Bind to the own property in the control's XAML with an ElementName or RelativeSource Binding:
<ItemsControl ItemsSource="{Binding Path=InternalControl,
RelativeSource={RelativeSource AncestorType=UserControl}}" >