65.9K
CodeProject 正在变化。 阅读更多。
Home

无外观控件/主题

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2009年6月17日

CPOL

3分钟阅读

viewsIcon

33098

downloadIcon

373

无外观控件/主题

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 种不同的样式之间切换。 您可以下载它并使用 演示项目 进行试验。

尽情享用!

© . All rights reserved.