如何使用预建项目实现自定义 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 question,CollectionContainer.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>
结果:
我想创建一个自定义控件,其作用类似于 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 question,CollectionContainer.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>
结果: