如何实现可向内置CommandBar直接传递内容的自定义控件
这个需求完全可实现,属于自定义控件开发中非常常规的内容重定向场景,不需要特殊黑科技,按照以下步骤实现即可。
核心实现逻辑
你不需要修改预期的XAML使用语法,核心只需要完成三个关键逻辑:
- 给自定义控件
MyControl单独声明一个用来接收外部传入按钮的集合属性,不要占用默认的Content属性,避免冲掉内置的控件结构 - 给
MyControl标记内容属性特性,让XAML编译器自动把写在<MyControl>标签内部的MyAdditionalButton元素,自动归集到上面声明的按钮集合里 - 在
MyControl的默认控件模板中,把预留的占位符位置和这个按钮集合做绑定,让外部传入的按钮自动渲染到内置DefaultButton的后面
具体实现代码(以WPF为例,UWP/WinUI/Avalonia等XAML框架逻辑通用)
- 第一步:定义自定义按钮类型
MyAdditionalButton
这个类型不需要复杂逻辑,直接继承对应框架中命令栏支持的按钮基类即可,空类也能正常运行:
// 如果你用UWP/WinUI,这里可以直接继承AppBarButton,适配原生CommandBar的项类型 public class MyAdditionalButton : Button { // 有自定义需求可以在这里加专属依赖属性,没有的话不需要额外写代码 }
- 第二步:定义
MyControl控件,声明附加按钮集合属性
注意一定要加ContentProperty特性,这是实现预期的「直接在标签内写按钮」效果的关键:
using System.Collections.ObjectModel; using System.Windows; using System.Windows.Controls; [ContentProperty(nameof(AdditionalButtons))] public class MyControl : Control { public ObservableCollection<MyAdditionalButton> AdditionalButtons { get => (ObservableCollection<MyAdditionalButton>)GetValue(AdditionalButtonsProperty); set => SetValue(AdditionalButtonsProperty, value); } public static readonly DependencyProperty AdditionalButtonsProperty = DependencyProperty.Register( nameof(AdditionalButtons), typeof(ObservableCollection<MyAdditionalButton>), typeof(MyControl), new PropertyMetadata(new ObservableCollection<MyAdditionalButton>())); static MyControl() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl))); } }
- 第三步:编写
MyControl的默认样式模板
把样式放到项目的Themes/Generic.xaml资源字典里,在之前预留占位符的位置绑定AdditionalButtons集合即可:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:YourProjectNamespace"> <Style TargetType="{x:Type local:MyControl}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type local:MyControl}"> <StackPanel> <Label Content="Hello" /> <CommandBar> <!-- 内置默认按钮 --> <Button Command="{Binding DCom1, RelativeSource={RelativeSource TemplatedParent}}" Content="默认操作1" /> <Button Command="{Binding DCom2, RelativeSource={RelativeSource TemplatedParent}}" Content="默认操作2" /> <!-- 外部传入按钮的渲染占位区 --> <ItemsControl ItemsSource="{TemplateBinding AdditionalButtons}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal" /> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </CommandBar> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
如果你用的是UWP/WinUI这类框架,原生
CommandBar的PrimaryCommands是专属集合类型,不支持直接放ItemsControl,你只需要重写MyControl的OnApplyTemplate方法,拿到模板里的CommandBar实例,监听AdditionalButtons的CollectionChanged事件,把新增的按钮同步添加到CommandBar.PrimaryCommands、移除的按钮同步删除即可,整体逻辑和WPF版本没有本质区别。
使用效果
代码写完之后,你就可以完全按照预期的写法调用控件:
<local:MyControl> <local:MyAdditionalButton Command="{Binding Com1}" Content="自定义操作1" /> <local:MyAdditionalButton Command="{Binding Com2}" Content="自定义操作2" /> </local:MyControl>
运行后渲染顺序和预期完全一致:先展示两个内置默认按钮,后面依次追加传入的两个自定义按钮。
内容的提问来源于stack exchange,提问作者Siggi
相关产品推荐
相关产品推荐

