无外观控件/主题





5.00/5 (3投票s)
无外观控件/主题
WPF 的一个优点是它将控件的功能与其外观分离,这被称为“无外观控件”。这很好,但我们如何确保自定义控件既能按预期工作,又能拥有默认外观呢? 这篇简短的文章将尝试向您展示无外观控件的工作原理。
确保控件按我们想要的方式工作
我首先会谈谈如何创建一个可以正确工作的拆分设计器/开发人员项目。
开发人员应该做的第一件事是创建一个 TemplatePartAttribute
,以便将其捕获在元数据中,文档工具可以使用该元数据。 通过使用此 TemplatePartAttribute
,开发人员可以告诉设计器正确的控件操作的预期。
以下是我制作的一个小控件的示例
1: [TemplatePart(Name = “PART_DropDown”,
2: Type = typeof(ComboBox))]
3: public class DemoControl : Control
4: {
5: }
这应该提醒设计器,他们需要创建控件 Template
的一部分,该部分应该是一个 ComboBox
,并且应该命名为“PART_DropDown
”。 这是第一部分,接下来开发人员应该重写 OnApplyTemplate
并查找任何预期的部件,这些部件对于使控件正常工作以及连接所需的事件是必需的。 以下是一个示例。
1: public override void OnApplyTemplate()
2: {
3: base.OnApplyTemplate();
4:
5: //Obtain the dropdown and create the items
6: dropDown =
7: base.GetTemplateChild(
8: “PART_DropDown”) as ComboBox;
9: if (dropDown != null)
10: dropDown.SelectionChanged +=
11: new SelectionChangedEventHandler(
12: dropDown_SelectionChanged);
13:
14:
15: }
16:
17: void dropDown_SelectionChanged(object sender,
18: SelectionChangedEventArgs e)
19: {
20:
21:
22: }
另一种方法是依赖 RoutedCommands
,设计器应该在 XAML 控件模板中使用它们。 然后可以按如下方式使用它们
1:
2: // Make sure the command is bound, so that it will work when called to
3: CommandBindings.Add(new
4: CommandBinding(DemoCommands.SayHello,
5: //The actual command handler code
6: (s, e) => {
7: MessageBox.Show(“Hello”);
8: }));
无外观控件
为了创建一个真正的无外观控件,我们应该执行以下操作
重写与控件关联的默认 Style
,这是通过更改元数据完成的。 以下是一个示例
1: static DemoControl()
2: {
3: //Provide a default set of visuals for a custom control
4: DefaultStyleKeyProperty.OverrideMetadata(
5: typeof(DemoControl),
6: new FrameworkPropertyMetadata(
7: typeof(DemoControl)));
8: }
接下来,我们需要了解 WPF 中主题的工作方式。 有一个程序集级别的属性称为 ThemeInfoAttribute
,通常如下创建
1: [assembly: ThemeInfo(
2: ResourceDictionaryLocation.None,
3: //where theme specific resource dictionaries are located
4: //(used if a resource is not found in the page,
5: // or application resource dictionaries)
6: ResourceDictionaryLocation.SourceAssembly
7: //where the generic resource dictionary is located
8: //(used if a resource is not found in the page,
9: // app, or any theme specific resource dictionaries)
10: )]
这可以用来指示控件的 Style
的位置。 通常,这就像我刚刚展示的那样创建。 如果您没有指定要查找的外部 DLL,那么接下来检查的位置是 Themes\generic.xaml,因此您应该将自定义控件的默认 Style
/Template
放在此处。
因此,通常您会创建一个 generic.xaml 文件,其中包含默认控件 Style
/Template
。
对于附带的演示项目,我的 generic.xaml 只是包含一堆合并的资源字典对象,如下所示
1: <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
2: xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”>
3:
4: <!– Merge in all the available themes –>
5: <ResourceDictionary.MergedDictionaries>
6: <ResourceDictionary
7: Source=”/CustomControls;component/Themes/Default.xaml” />
8: <ResourceDictionary
9: Source=”/CustomControls;component/Themes/Blue.xaml” />
10: <ResourceDictionary
11: Source=”/CustomControls;component/Themes/Red.xaml” />
12: </ResourceDictionary.MergedDictionaries>
13:
14:
15:
16: </ResourceDictionary>
如果我们更仔细地研究其中一个,比如“Blue
”,我们可以看到它也使用 ComponentResourceKey
标记扩展。
1: <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
2: xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
3: xmlns:local=”clr-namespace:CustomControls”>
4:
5: <Style x:Key=”{ComponentResourceKey {x:Type local:DemoControl}, Blue }”
6: TargetType=”{x:Type local:DemoControl}”>
7: <Setter Property=”Background” Value=”Blue”/>
8: <Setter Property=”Margin” Value=”10″/>
9: <Setter Property=”Template”>
10: <Setter.Value>
11: <ControlTemplate TargetType=”{x:Type local:DemoControl}” >
12: <Border Background=”{TemplateBinding Background}”
13: CornerRadius=”5″ BorderBrush=”Cyan”
14: BorderThickness=”2″>
15: <StackPanel Orientation=”Vertical”
16: Margin=”{TemplateBinding Margin}”>
17: <Button x:Name=”btnSayHello”
18: Margin=”{TemplateBinding Margin}”
19: Background=”LightBlue”
20: Foreground=”Black”
21: Command=”{x:Static
22: local:DemoCommands.SayHello}”
23: Content=”Say Hello” Height=”Auto”
24: Width=”Auto” />
25: <ComboBox x:Name=”PART_DropDown”
26: Margin=”{TemplateBinding Margin}”
27: Background=”LightBlue”
28: Foreground=”Black”>
29: <ComboBoxItem Content=”Blue”/>
30: <ComboBoxItem Content=”Red”/>
31: </ComboBox>
32: </StackPanel>
33: <Border.LayoutTransform>
34: <ScaleTransform CenterX=”0.5″
35: CenterY=”0.5″
36: ScaleX=”3.0″
37: ScaleY=”3.0″/>
38: </Border.LayoutTransform>
39: </Border>
40: </ControlTemplate>
41: </Setter.Value>
42: </Setter>
43: </Style>
44:
45:
46: </ResourceDictionary>
那么,这为我们做了什么呢? 简而言之,它允许我们使用 Type/Id
查找资源,从而以另一种方式选择资源。 以下是一个示例
1: Style style = (Style)TryFindResource(
2: new ComponentResourceKey(
3: typeof(DemoControl),
4: styleToUseName));
5:
6: if (style != null)
7: this.Style = style;
正在运行的应用程序只是允许用户在无外观控件的 3 种不同的样式之间切换。 您可以下载它并使用 演示项目 进行试验。
尽情享用!