XAML 将自订控件中的泛型型别ItemsControl ItemsSource系结至DependencyProperty

cyvaqqii  于 2022-12-07  发布在  其他
关注(0)|答案(1)|浏览(143)

至于问题,我已经创建了一个自定义的UserControl。它有一个ItemsControl,其中ItemsSource被绑定到一个可绑定的属性DependecyProperty
我的控件XAML:

<Grid>
    <ItemsControl ItemsSource="{Binding Path=InternalControl}" >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Grid>

我的控制代码:

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); }
    }

主要XAML:

<Controls:UtilityExpander InternalControl="{Binding GasControl}"/>

主要代码:

private ObservableCollection<UtilityGas> _gasControl;
    public ObservableCollection<UtilityGas> GasControl { get => _gasControl; set { _gasControl = value; NotifyPropertyChanged(); } }

InitializeComponent()上运行时,我得到
System.Windows.Markup.XamlParseException: '无法在型别' UtilityExpander '的' InternalControl '属性上设定' Binding'。只能在DependencyObject的DependencyProperty上设定'Binding'。'
我使用dynamic的原因是我想使用不同类型IEnumerable<objects>的控件

ui7jx7zq

ui7jx7zq1#

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}}" >

相关问题