@TOC


前言

做项目的时候,用到了WPF,总结下来,供以后参考,与诸君共勉。

一、WPF中的复用精神:内联样式和模板 Inline styles and templates

  • Windows Presentation Foundation (WPF) 提供了 Style 对象和模板对象(FrameworkTemplate 子类)作为定义资源中元素的视觉外观的方法,以便 用户可以多次使用它们
    • 因此, 采用 Style 和 FrameworkTemplate 类型的 XAML 中的属性几乎总是对现有样式和模板进行资源引用,而不是定义新的嵌入式样式和模板。

1.嵌入式样式和模板的限制 Limitations of embedded styles and templates:

  • 在 Extensible Application Markup Language (XAML) 中,样式和模板属性在技术上可通过两种方式进行设置
    • 方式一:可以使用属性语法来引用已在资源中定义的样式
      • 如 <objectStyle=”{StaticResourcemyResourceKey}” …/>
      • 定义为嵌入式且未在资源中定义的样式必须仅限于包含元素,由于没有资源键,因此不能轻易重用它
    • 方式二:可以使用属性元素语法来定义嵌入式样式
      1
      2
      3
      4
      5
      <object>
      <object.Style>
      < Style .../>
      对象
      对象
      • 可以将 Style 视为一种将一组属性值应用到多个元素的便捷方法, 可以对从 FrameworkElement 或 FrameworkContentElement(如 Window 或 Button)派生的任何元素使用样式。
        • 声明样式的最常见方法是在 XAML 文件的 Resources 部分中声明为资源。 由于样式是一种资源,声明样式的位置会影响样式的应用范围。
          - 例如,如果在应用定义 XAML 文件的根元素中声明样式,则该样式可以在应用中的任何位置使用。
          - 例如,以下 XAML 代码为 TextBlock 声明了两个样式,一个自动应用于所有 TextBlock 元素,另一个则必须显式引用。
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          <Window.Resources>
          <!-- .... other resources .... -->

          <!--A Style that affects all TextBlocks-->
          <Style TargetType="TextBlock">
          <Setter Property="HorizontalAlignment" Value="Center" />
          <Setter Property="FontFamily" Value="Comic Sans MS"/>
          <Setter Property="FontSize" Value="14"/>
          </Style>

          <!--A Style that extends the previous TextBlock Style with an x:Key of TitleText-->
          <Style BasedOn="{StaticResource {x:Type TextBlock}}"
          TargetType="TextBlock"
          x:Key="TitleText">
          <Setter Property="FontSize" Value="26"/>
          <Setter Property="Foreground">
          <Setter.Value>
          <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
          <LinearGradientBrush.GradientStops>
          <GradientStop Offset="0.0" Color="#90DDDD" />
          <GradientStop Offset="1.0" Color="#5BFFFF" />
          </LinearGradientBrush.GradientStops>
          </LinearGradientBrush>
          </Setter.Value>
          </Setter>
          </Style>
          </Window.Resources>
          //如何使用上面声明的样式
          <StackPanel>
          <TextBlock Style="{StaticResource TitleText}" Name="textblock1">My Pictures</TextBlock>
          <TextBlock>Check out my new pictures!</TextBlock>
          </StackPanel>

2.一般的 Windows Presentation Foundation (WPF) 编程模型原则

  • 将代码中的程序逻辑与标记中的设计分离
    • WPF 样式设置模型的另一项功能是将呈现与逻辑分离
      • 设计者可以仅使用 XAML 处理应用外观,与此同时开发者使用 C# 或 Visual Basic 处理编程逻辑
        • 其实呢,感觉就是用XAML模仿静态三剑客【HTML、CSS、Jabascript】等,然后C#模仿Java全家桶玩前后端分离呗
  • Windows Presentation Foundation (WPF) 样式设置和模板化是指一套功能(样式、模板、触发器和情节提要),可使应用程序、文档和用户界面 (UI) 设计者创建极具视觉表现力的应用程序,并为其产品创建标准化的特定外观。

3.ControlTemplate

  • 在 WPF 中,控件的 ControlTemplate 用于定义控件的外观
    • 可以通过定义新的 ControlTemplate 并将其分配给控件来更改控件的结构和外观,从而无需自行编写自定义控件
  • 每个控件都有一个分配给 Control.Template 属性的默认模板。 该模板将控件的视觉呈现与控件的功能关联起来
    • 在 XAML 中定义了模板,设计器通常允许创建现有模板的副本并进行修改
      • 在 Visual Studio WPF 设计器中,选择一个 CheckBox 控件,然后右键单击并选择“编辑模板”>“创建副本”,此命令会生成一个用于定义模板的样式
    • 控件模板是通过设置 Control.Template 属性来应用的,因此可以使用样式来定义或设置模板
    • 通常在 XAML 文件的 Resources 部分中将模板声明为资源。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      <Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
      <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual1}"/>
      <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background1}"/>
      <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border1}"/>
      <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
      <Setter Property="BorderThickness" Value="1"/>
      <Setter Property="Template">
      <Setter.Value>
      <ControlTemplate TargetType="{x:Type CheckBox}">
      <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
      <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
      <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
      <Grid x:Name="markGrid">
      <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="1" Opacity="0" Stretch="None"/>
      <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph1}" Margin="2" Opacity="0"/>
      </Grid>
      </Border>
      <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
      </Grid>
      <ControlTemplate.Triggers>
      <Trigger Property="HasContent" Value="true">
      <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual1}"/>
      <Setter Property="Padding" Value="4,-1,0,0"/>

      ... content removed to save space ...

4.TemplateBinding

  • 对于模板方案来说,TemplateBinding 是绑定的优化形式,类似于使用 {Binding RelativeSource={RelativeSource TemplatedParent}} 构造的绑定。
    • TemplateBinding 可用于将模板的各个部分绑定到控件的各个属性。
      • 例如,每个控件都有一个 BorderThickness 属性。 可使用 TemplateBinding 管理此控件设置影响模板中的哪个元素

5.ContentControl and ItemsControl

  • 如果在 ContentControl 的 ControlTemplate 中声明了 ContentPresenter,ContentPresenter 将自动绑定到 ContentTemplate 和 Content 属性。
    • ItemsControl 的 ControlTemplate 中的 ItemsPresenter 将自动绑定到 ItemTemplate 和 Items 属性。

6.一个绑定到照片列表的 ListBox 控件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>

//在此示例中,数据是照片列表
//每个自定义 Photo 对象都有一个字符串类型的 Source 属性,用于指定图像的文件路径。 当前,照片对象显示为文件路径。
public class Photo
{
public Photo(string path)
{
Source = path;
}

public string Source { get; }

public override string ToString() => Source;
}
//若要使照片显示为图像,可以将 DataTemplate 创建为资源
<Window.Resources>
<!-- .... other resources .... -->

<!--DataTemplate to display Photos as images
instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
<Border Margin="3">
<Image Source="{Binding Source}"/>
</Border>
</DataTemplate>
</Window.Resources>
//DataTemplate 定义每当存在 Photo 对象时,它都应显示为 Border 内的 Image。
  • 在 WPF 中,使用 DataTemplate 定义数据的视觉表示形式。
    • 放入 DataTemplate 的内容决定了数据在呈现的应用中的外观

7.PropertyTrigger

  • 根据属性的值设置属性值或启动操作的 Trigger 称为属性触发器
    • 要演示如何使用属性触发器,可以使每个 ListBoxItem 在未选中时部分透明。 以下样式将 ListBoxItem 的 Opacity 值设置为 0.5。 但是,当 IsSelected 属性为 true 时,Opacity 设置为 1.0。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      <Window.Resources>
      <!-- .... other resources .... -->

      <Style TargetType="ListBoxItem">
      <Setter Property="Opacity" Value="0.5" />
      <Setter Property="MaxHeight" Value="75" />
      <Style.Triggers>
      <Trigger Property="IsSelected" Value="True">
      <Trigger.Setters>
      <Setter Property="Opacity" Value="1.0" />
      </Trigger.Setters>
      </Trigger>
      </Style.Triggers>
      </Style>
      </Window.Resources>
  • Trigger 类还有 EnterActions 和 ExitActions 属性,这些属性可使触发器执行操作。
  • 另一个触发器类型是 EventTrigger,用于根据某个事件的发生启动一组操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // EventTrigger 对象指定当鼠标指针进入 ListBoxItem 时,MaxHeight 属性在 0.2 秒的时间内动画化为值 90。 当鼠标离开该项时,属性在 1 秒的时间内返回到原始值。 请注意,不需要为 MouseLeave 动画指定 To 值。 这是因为动画能够跟踪原始值。
    <Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
    <Trigger.Setters>
    <Setter Property="Opacity" Value="1.0" />
    </Trigger.Setters>
    </Trigger>
    <EventTrigger RoutedEvent="Mouse.MouseEnter">
    <EventTrigger.Actions>
    <BeginStoryboard>
    <Storyboard>
    <DoubleAnimation
    Duration="0:0:0.2"
    Storyboard.TargetProperty="MaxHeight"
    To="90" />
    </Storyboard>
    </BeginStoryboard>
    </EventTrigger.Actions>
    </EventTrigger>
    <EventTrigger RoutedEvent="Mouse.MouseLeave">
    <EventTrigger.Actions>
    <BeginStoryboard>
    <Storyboard>
    <DoubleAnimation
    Duration="0:0:1"
    Storyboard.TargetProperty="MaxHeight" />
    </Storyboard>
    </BeginStoryboard>
    </EventTrigger.Actions>
    </EventTrigger>
    </Style.Triggers>

8.视觉状态 visual status

  • 大多数控件都有两个状态组:CommonStates 和 FocusStates。
    • 在应用于控件的每个状态组中,控件始终处于每个组的一种状态,例如 CommonStates.MouseOver 和 FocusStates.Unfocused。 但是,控件不能处于同一组中的两种不同状态
  • 控件始终处于特定的状态
    • 例如,当鼠标在控件的表面上移动时,该控件被视为处于公用状态 MouseOver。 没有特定状态的控件被视为处于公用 Normal 状态。

二、例子:为 Button 控件创建新的 ControlTemplate

  • 例子效果:
    • 一个典型的C#标题框,两个按钮:
      • 一个名为Unstyled Button,的按钮,按钮上写Button 1
      • 一个名为Rounded Button,的按钮,按钮上写Button 2

1.何时创建 ControlTemplate When to create ControlTemplate:要自定义设置控件中其他属性无法实现的控件外观时,则创建 ControlTemplate

  • 控件有许多属性,例如 Background、Foreground 和 FontFamily。 这些属性控制控件外观的不同方面,但可通过设置这些属性进行的更改有限。 例如,可以从 CheckBox 中将 Foreground 属性设置为蓝色,并将 FontStyle 设置为斜体。
    • 要自定义设置控件中其他属性无法实现的控件外观时,则创建 ControlTemplate。比如, 在多数用户界面中,按钮的总体外观相同:即一个包含某些文本的矩形。 若想要创建一个圆形的按钮,可以创建一个继承自该按钮或重新创建该按钮功能的新控件。 此外,新用户控件还会提供圆形视觉对象。
      • 通过自定义现有控件的可视布局,可以避免创建新控件。 借助圆形按钮,可创建具有所需可视布局的 ControlTemplate。
      • 另一方面,如果你需要具有新功能、其他属性和新设置的控件,可创建新的 UserControl。

2.创建新的 WPF 应用程序,在 MainWindow.xaml(或选择的其他窗口)的 元素中设置以下属性

  • Title
    • 如:Template Intro Sample
  • SizeToContent
    • 如:WidthAndHeight
  • MinWidth
    • 如:250
1
2
3
4
5
6
7
//将 <Window> 元素的内容设置为以下 XAML:
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//MainWindow.xaml 文件
<Window x:Class="IntroToStylingAndTemplating.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:IntroToStylingAndTemplating"
mc:Ignorable="d"
Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
<StackPanel Margin="10">
<Label>Unstyled Button</Label>
<Button>Button 1</Button>
<Label>Rounded Button</Label>
<Button>Button 2</Button>
</StackPanel>
</Window>

3.创建 ControlTemplate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//为 Button 类创建一个简单的 ControlTemplate:
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Margin="5" Name="grid">
<Ellipse Stroke="DarkBlue" StrokeThickness="2">
<Ellipse.Fill>
<RadialGradientBrush Center="0.3,0.2" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="Azure" Offset="0.1" />
<GradientStop Color="CornflowerBlue" Offset="1.1" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Name="content" Margin="10"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
  • 声明 ControlTemplate 的最常见方法是在 XAML 文件的 Resources 部分中声明为资源。
    • 如果在应用程序定义 XAML 文件的根元素中声明模板,则该模板可以在应用程序中的任何位置使用。 如果在窗口中定义模板,则仅该窗口中的控件可以使用该模板。
      • 模板是资源,因此它们遵从适用于所有资源的相同范围规则。 简言之,声明模板的位置会影响模板的应用范围
  • 步骤一:将 Window.Resources 元素添加到 MainWindow.xaml 文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <Window x:Class="IntroToStylingAndTemplating.Window2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:IntroToStylingAndTemplating"
    mc:Ignorable="d"
    Title="Template Intro Sample" SizeToContent="WidthAndHeight" MinWidth="250">
    <Window.Resources>

    </Window.Resources>
    <StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button>Button 2</Button>
    </StackPanel>
    </Window>
  • 步骤二:使用以下属性集创建新的
    • x:Key
      • roundbutton
    • TargetType
      • Button
        1
        2
        3
        4
        5
        6
        7
        8
        9
        //控件的根元素 Grid
        //用于绘制按钮圆形外观的 Ellipse。<Ellipse> 元素的 Fill 和 Stroke 属性绑定到了控件的 Foreground 和 Background 属性。
        //用于显示用户指定的按钮内容的 ContentPresenter
        <ControlTemplate x:Key="roundbutton" TargetType="Button">
        <Grid>
        <Ellipse Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
        </Grid>
        </ControlTemplate>
    • 步骤三:创建新的 ControlTemplate 时,可能仍然想要使用公共属性更改控件外观。 TemplateBinding 标记扩展将 ControlTemplate 中元素的属性绑定到由控件定义的公共属性。
      • 设置控件属性后,该值将传递到包含 TemplateBinding 的元素。
      • 还将 元素添加到了模板。 此模板专为按钮设计,因此请注意该按钮继承自 ContentControl。 此按钮会显示该元素的内容。 可以在该按钮中设置任何内容,例如纯文本,甚至其他控件。 以下两个按钮均有效:
        1
        2
        3
        4
        5
        6
        7
        <Button>My Text</Button>

        <!-- and -->

        <Button>
        <CheckBox>Checkbox in a button</CheckBox>
        </Button>
        • 若将 ControlTemplate 应用到 ContentControl 类型(例如 Button),将在元素树中搜索 ContentPresenter。 若找到了 ContentPresenter,模板会自动将控件的 Content 属性绑定到 ContentPresenter。
    • 步骤四:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      <StackPanel Margin="10">
      <Label>Unstyled Button</Label>
      <Button>Button 1</Button>
      <Label>Rounded Button</Label>
      <Button>Button 2</Button>
      </StackPanel>
      //将第二个按钮的 Template 属性设置为 roundbutton 资源:将看到此按钮具有圆形背景.此按钮不是一个圆形,而是倾斜的
      <StackPanel Margin="10">
      <Label>Unstyled Button</Label>
      <Button>Button 1</Button>
      <Label>Rounded Button</Label>
      <Button Template="{StaticResource roundbutton}">Button 2</Button>
      </StackPanel>
      //由于 <Ellipse> 元素的工作方式,它始终会扩展并填充可用空间。 将此按钮的 width 和 height 属性更改为同一个值,以使圆形均衡:
      <StackPanel Margin="10">
      <Label>Unstyled Button</Label>
      <Button>Button 1</Button>
      <Label>Rounded Button</Label>
      <Button Template="{StaticResource roundbutton}" Width="65" Height="65">Button 2</Button>
      </StackPanel>
      //监视按钮的 IsMouseOver 属性。 当鼠标位于控件上方时,使用新颜色设置 <Ellipse> 的样式。 此触发器类型称为 PropertyTrigger。必须为 <Ellipse> 添加一个可引用的名称,以便于触发器起作用。 将其命名为“backgroundElement”。
      <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
      //将新的 Trigger 添加到 ControlTemplate.Triggers 集合。 此触发器将监视 IsMouseOver 事件是否为值 true。
      <ControlTemplate x:Key="roundbutton" TargetType="Button">
      <Grid>
      <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
      <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
      </Grid>
      <ControlTemplate.Triggers>
      <Trigger Property="IsMouseOver" Value="true">

      </Trigger>
      </ControlTemplate.Triggers>
      </ControlTemplate>
      //将 <Setter> 添加到 <Trigger>,后者会将 <Ellipse> 的 Fill 属性更改为一种新颜色
      <Trigger Property="IsMouseOver" Value="true">
      <Setter Property="Fill" TargetName="backgroundElement" Value="AliceBlue"/>
      </Trigger>
      //若要将 <PropertyTrigger> 转换为动画效果的可视状态,首先要从模板删除 <ControlTemplate.Triggers> 元素
      <ControlTemplate x:Key="roundbutton" TargetType="Button">
      <Grid>
      <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
      <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
      </Grid>
      </ControlTemplate>
      //在控件模板的 <Grid> 根中,添加 <VisualStateManager.VisualStateGroups>,其中包含 CommonStates 的 <VisualStateGroup>。 定义两种状态:Normal 和 MouseOver。
      <ControlTemplate x:Key="roundbutton" TargetType="Button">
      <Grid>
      <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="CommonStates">
      <VisualState Name="Normal">
      </VisualState>
      <VisualState Name="MouseOver">
      </VisualState>
      </VisualStateGroup>
      </VisualStateManager.VisualStateGroups>
      <Ellipse x:Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
      <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
      </Grid>
      </ControlTemplate>
      //触发 <VisualState> 时,将应用该状态中定义的任何动画。 为每种状态创建动画。 动画位于 <Storyboard> 元素中
      //此状态对椭圆填充进行动画处理,将其还原为控件的 Background 颜色。
      <Storyboard>
      <ColorAnimation Storyboard.TargetName="backgroundElement"
      Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
      To="{TemplateBinding Background}"
      Duration="0:0:0.3"/>
      </Storyboard>
      //MouseOver,此状态对椭圆 Background 颜色进行动画处理,将其更改为新颜色 Yellow。
      <Storyboard>
      <ColorAnimation Storyboard.TargetName="backgroundElement"
      Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
      To="Yellow"
      Duration="0:0:0.3"/>
      </Storyboard>
      <ControlTemplate> 如下所示:将鼠标移到按钮上方时,<Ellipse> 的颜色会进行动画处理。
      <ControlTemplate x:Key="roundbutton" TargetType="Button">
      <Grid>
      <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="CommonStates">
      <VisualState Name="Normal">
      <Storyboard>
      <ColorAnimation Storyboard.TargetName="backgroundElement"
      Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
      To="{TemplateBinding Background}"
      Duration="0:0:0.3"/>
      </Storyboard>
      </VisualState>
      <VisualState Name="MouseOver">
      <Storyboard>
      <ColorAnimation Storyboard.TargetName="backgroundElement"
      Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
      To="Yellow"
      Duration="0:0:0.3"/>
      </Storyboard>
      </VisualState>
      </VisualStateGroup>
      </VisualStateManager.VisualStateGroups>
      <Ellipse Name="backgroundElement" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding Foreground}" />
      <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="Center" VerticalAlignment="Center" />
      </Grid>
      </ControlTemplate>

三、Windows Presentation Foundation (WPF) 数据绑定功能

……开个头,继续看


巨人的肩膀