如何使用预建项目实现自定义 ItemsControl?

How to implement a Custom ItemsControl with pre-built items?

我想创建一个自定义控件,其作用类似于 ToolBar 控件,但除了用户提供的元素之外还包含预构建元素。

例如,对于下面的代码,我需要一个包含预定义元素的工具栏,然后是两个用户提供的按钮:

<MyToolBar>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</MyToolBar>

这相当于:

<ToolBar>
    <Button>Predefined button 1</Button>
    <Button>Predefined button 2</Button>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</ToolBar>

我尝试继承 ToolBar class 并将 CompositeCollection 指定为 ToolBar.ItemSource,但是它失败了,抛出

Cannot find governing FrameworkElement or FrameworkContentElement for target element

<!-- Wrong Code -->
<Style TargetType="{x:Type local:MyToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyToolBar}">
                <ToolBar>
                    <ToolBar.ItemsSource>
                        <CompositeCollection>
                            <CollectionContainer Collection="{Binding ItemsSource,RelativeSource={RelativeSource TemplatedParent}}" />
                            <Button>Predefined button 1</Button>
                            <Button>Predefined button 2</Button>
                        </CompositeCollection>
                    </ToolBar.ItemsSource>
                </ToolBar>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

基于 this questionCollectionContainer.Collection 上的绑定似乎不会在源内容更改时更新容器内容。
您必须使用 CollectionViewSource 有一个代理:

<Style x:Key="MyToolBarStyle" TargetType="{x:Type ToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToolBar}">
                <ToolBar>
                    <ToolBar.Resources>
                        <CollectionViewSource x:Key="TemplateItems" Source="{TemplateBinding ItemsSource}" />
                    </ToolBar.Resources>
                    <ToolBar.ItemsSource>
                        <CompositeCollection>
                            <Button>Predefined button 1</Button>
                            <Button>Predefined button 2</Button>
                            <CollectionContainer Collection="{Binding Source={StaticResource TemplateItems}}" />
                        </CompositeCollection>
                    </ToolBar.ItemsSource>
                </ToolBar>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

定义一个 read-only collection-type dependency property 并在 class:

中添加默认项目
[ContentProperty(nameof(ItemsSource))]
public class MyToolBar : Control
{
    private static readonly DependencyPropertyKey s_itemsSourcePropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: nameof(ItemsSource),
          propertyType: typeof(List<FrameworkElement>),
          ownerType: typeof(MyToolBar),
          typeMetadata: new FrameworkPropertyMetadata()
        );

    public MyToolBar()
    {
        SetValue(s_itemsSourcePropertyKey, new List<FrameworkElement>()
        {
            new Button(){ Content = "Predefined button 1" },
            new Button(){ Content = "Predefined button 2" }
        });
    }

    public List<FrameworkElement> ItemsSource =>
        (List<FrameworkElement>)GetValue(s_itemsSourcePropertyKey.DependencyProperty);
}

模板:

<Style TargetType="{x:Type local:MyToolBar}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyToolBar}">
                <ToolBar ItemsSource="{Binding ItemsSource,
                            RelativeSource={RelativeSource AncestorType=local:MyToolBar}}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

用法:

<local:MyToolBar>
    <Button>User button 1</Button>
    <Button>User button 2</Button>
</local:MyToolBar>

结果: