• [No000012F]WPF(7/7)


    • WPF Tutorial : Beginning [^]
    • WPF Tutorial : Layout-Panels-Containers & Layout Transformation [^]
    • WPF Tutorial : Fun with Border & Brush [^]
    • WPF Tutorial - TypeConverter & Markup Extension [^]
    • WPF Tutorial - Dependency Property [^]
    • WPF Tutoriall - Concept Binding [^]
    • WPF Tutorial - Styles, Triggers & Animation [^]
    • 介绍

      也许对于任何WPF应用程序最有趣和最重要的功能是Styling。样式意味着定义控件的样式,并以可重复使用的方式存储,ResourceDictionaries 随后可以通过调用其名称来使用。WPF中的样式可以与CSS样式进行比较。它们在大多数情况下都是相似的,而前者扩展了允许WPF具有大部分功能的特性。在本文中,我将主要讨论如何Style在WPF应用程序中使用它来增强UI的丰富体验。

      在我们继续进行风格之前,让我们记下迄今为止我们已经讨论过的内容。

      所以基本上,如果你已经阅读过系列文章,那么你必须已经了解了如何应用你的风格的大部分内容,你怎么定义风格对象等。在本文中,我将讨论如何更好地编写样式为您的应用程序。

      样式

      WPF Style为每个Control 公开一个属性如果你看看对象层次,样式基本上是暴露的对象的属性Style 在FrameworkElement所以每个对象都可以关联它并定义自定义设置器来操纵控件的基本外观。

      WPFClassHierarchyStyle.JPG

      显然,上面的图显示的关联Style中 FrameworkElement,并从对象分层结构中的每个控制某种方式从继承FrameworkElement,因此样式将提供给它。Style也是一个WPF对象,它是继承的形式DispatcherObject ,它有助于设置UI元素的不同属性。

      风格与主题有何不同?

      在我们进一步进入样式之前,让我们讨论主题。主题与Styles完全不同。主题是在操作系统级别定义的,或者更确切地说,主题可以参与在桌面上传递样式的一部分,同时Styles 限制在WPF窗口的上下文区域。WPF能够检索OS级定义的配色方案。举例来说,如果您没有为应用程序定义样式,屏幕中的元素将自动从外部环境获取样式。举例来说,在XP中,如果将主题更改为其他内容,则会看到TextBoxWPF窗口上的按钮会立即更改其颜色。您甚至可以设置应用程序在您的代码中编程方式 [ ^ ] 使用的主题 

      什么模板?

      每个控件定义一个ControlTemplateControlTemplate定义了控件的整体结构。正如我已经告诉过你的,比如说你有一个Button按钮是由多个控件组成的控件。它会有一个ContentPresenter 写在Text控制之上,它会有一个Rectangle保持等的边界Button。因此,模板是一个特殊的属性与一个控制,它指定了控制将在结构上看起来如何。我们可以轻松定义Template和改变控件的整体结构。

      模板基本上有两种类型:

          1. 控件模板
          2. DataTemplate中 

      ControlTemplate定义了结构Control这意味着举例说,你定义了ControlTemplate一个ComboBox因此,从ControlTemplate您可以轻松地更改 ToggleButtonComboBox哪个打开 关联DropDown,您可以更改该结构TextBox, Popup等等。因此,ControlTemplate允许您更改该控件的整体结构。

      每个控件都由数据组成。举例来说,一个ItemsControl包含许多数据元素的构建Popup内的项目。DataTemplate可能会与ItemsTemplate 并将建立数据块的ComboBox

      templates.JPG

      所以,你应该永远记住,ControlTemplateDataTemplate定义每个单独的数据元素的同时定义整个控件

      如何定义样式?

      通常,样式是用于设置WPF控件样式的唯一对象。每个WPF元素都包含许多依赖属性。依赖项属性定义UI中控件的基本行为和外观。样式维护一个Setters 列举DependencyProperty它的价值的集合

      因此,你可以说风格是一组DependencyProperty设置,当应用在目标上时,它会改变它的行为。

      让我们假设你要设计一个TextBox

      textbox_style.JPG
      <TextBox Text="This is a TextBox without Styles" 
      
                  HorizontalAlignment="Center" 
      
                  VerticalAlignment="Center" 
      
                  CharacterCasing="Lower" 
      
                  FlowDirection="RightToLeft"
      
                  FontSize="20" 
      
                  FontWeight="UltraBlack"
      
                  Width="400"
      
                  Height="40">
          <TextBox.Background>
              <LinearGradientBrush>
                  <GradientStop Color="Cyan" Offset="0.0"/>
                  <GradientStop Color="Yellow" Offset="0.5"/>
                  <GradientStop Color="Red" Offset="1.0"/>
              </LinearGradientBrush>
          </TextBox.Background>
          <TextBox.Foreground>
              <SolidColorBrush Color="Black"/>
          </TextBox.Foreground>
          <TextBox.Effect>
              <DropShadowEffect BlurRadius="40" Color="Maroon"
      
                 Direction="50" Opacity="0.5"/>
          </TextBox.Effect>
      </TextBox>

      所以我刚刚TextBox在上面的代码中设计了一个XAML看起来很简单,我已经配置了TextBox控件的不同属性来创建我的时尚TextBox但是查看代码,您可能想知道,如果您需要为应用程序中定义的每个TextBox重复执行相同的操作,会有多困难。这是问题所在。所以WPF带有一个替代方案stylestyle是将这些行为保存为一个集合的对象Setters所以让我们重新定义一下Styles

      textbox_style2.JPG
      <TextBox>
          <TextBox.Style>
              <Style TargetType="{x:Type TextBox}">
                  <Setter Property="Text" Value="This is a TextBox with Styles"/>
                  <Setter Property="HorizontalAlignment" Value="Center"/>
                  <Setter Property="VerticalAlignment" Value="Center"/>
                  <Setter Property="CharacterCasing" Value="Lower"/>
                  <Setter Property="FlowDirection" Value="RightToLeft"/>
                  <Setter Property="FontSize" Value="20"/>
                  <Setter Property="FontWeight" Value="UltraBlack"/>
                  <Setter Property="Width" Value="400"/>
                  <Setter Property="Height" Value="40"/>
                  <Setter Property="Background">
                      <Setter.Value>
                              <LinearGradientBrush>
                              <GradientStop Color="Cyan" Offset="0.0"/>
                              <GradientStop Color="Yellow" Offset="0.5"/>
                              <GradientStop Color="Red" Offset="1.0"/>
                          </LinearGradientBrush>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Foreground">
                      <Setter.Value>
                          <SolidColorBrush Color="Black"/>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Effect" >
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" 
      
                           Color="Maroon" Direction="50" Opacity="0.5"/>
                      </Setter.Value>
                  </Setter>
              </Style>
          </TextBox.Style>
      </TextBox>

      所以你可以看到,我已经定义了Style里面的TextBox和文本框的外观几乎一样。Setters允许你枚举所有的属性,TextBox并在其中TargetType设置一个样式{x:Type Button}

      现在如何使这个style控件可以重复使用?是的,这可能是你头脑中出现的第一个问题。是的,如果您已阅读我以前的文章,您应该已经知道使用ResourceDictionaries所以在我们的例子中,我将把样式转移到Window的Resource部分,并通过调用Resource中的Resource键来重用代码Textbox

      <Grid>
          <Grid.Resources>
              <ResourceDictionary>
                  <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
                      <Setter Property="Text" Value="This is a TextBox with Styles"/>
                      <Setter Property="HorizontalAlignment" Value="Center"/>
                      <Setter Property="VerticalAlignment" Value="Center"/>
                      <Setter Property="CharacterCasing" Value="Lower"/>
                      <Setter Property="FlowDirection" Value="RightToLeft"/>
                      <Setter Property="FontSize" Value="20"/>
                      <Setter Property="FontWeight" Value="UltraBlack"/>
                      <Setter Property="Width" Value="400"/>
                      <Setter Property="Height" Value="40"/>
                      <Setter Property="Margin" Value="0,20,0,10" />
                      <Setter Property="Background">
                          <Setter.Value>
                              <LinearGradientBrush>
                                  <GradientStop Color="Cyan" Offset="0.0"/>
                                  <GradientStop Color="Yellow" Offset="0.5"/>
                                  <GradientStop Color="Red" Offset="1.0"/>
                              </LinearGradientBrush>
                          </Setter.Value>
                      </Setter>
                      <Setter Property="Foreground">
                          <Setter.Value>
                              <SolidColorBrush Color="Black"/>
                          </Setter.Value>
                      </Setter>
                      <Setter Property="Effect" >
                          <Setter.Value>
                              <DropShadowEffect BlurRadius="40" 
      
                                   Color="Maroon" Direction="50" Opacity="0.5"/>
                          </Setter.Value>
                      </Setter>
                  </Style>
              </ResourceDictionary>
          </Grid.Resources>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="*"/>
          </Grid.RowDefinitions>
          <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="0" />
          <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="1" 
      
                      Text="The Style is modified here"
      
                      FlowDirection="LeftToRight"/>
      </Grid>
      textbox_style3.JPG

      所以在这里我已经将风格转移到资源部分,并使用MyTextBoxStyle键来引用每个TextBox我定义。值得注意的是,两个文本框的风格都保持不变,而你可以看到我也覆盖了控件本身的某些设置,它的工作原理是一样的。我已经修改了Text第二个文本框,以“风格在此处修改”,并作出FlowDirectionLeftToRight

      另一个重要的事情是,你应该时刻牢记,如果你没有为“资源中的风格”部分定义Key元素,它将自动应用于TextBox你定义的所有元素

      <Style TargetType="{x:Type TextBox}">
      </Style>

      style你定义不包含任何密钥。因此,所有这些TextBoxes都会在出现时自动应用样式。你最终可以使用

      <TextBox Style="{x:Null}"/>

      恢复风格。

      风格的成员

      style_snap.JPG

      WPF控件的样式由一个叫做类的帮助组成Style样式对象公开了一些帮助你定义各种行为的属性。让我们看看这些属性:

          • 资源:它包含ResourceDictionaryStyle定义位置的参考
          • Setters:这是一个集合,它包含DependencyProperty整个控件的所有配置。
          • TargetTypeTargetType定义可应用Style的控件的类型。所以根据TargetTypeStyle定义的风格来定义。所以如果你为你定义一个样式TextBox不能使用Content作为属性Setter
          • BasedOn:这用于允许Style继承。您可以使用现有样式键将所有属性继承为新的Style
          • 触发器:根据特定条件应用的Setters集合。

      使用这些属性可以定义自己的样式。

      显式和隐式样式怎么样?

      WPF控件可以有两种与之相关的样式。控件可以具有在应用程序中定义的样式并应用于其Style属性。如果您的控件使用a Style来定义其外观,或者您的控件基本上已将Style对象设置为其Style属性,则它将使用ExplicitStyle。

      另一方面,如果您的控件从外部环境(主题)获取样式并将Style属性设置为Null,则您的控件正在使用Implicit样式。基本上任何WPF控件都会自动为它定义一个DefaultStyle,以便您只能设置需要更改的那部分控件。

      比如说,你有一个Button如果你想让它Text变成红色,你只需要改变它的前景Button您不需要定义整个样式。如果没有为按钮定义默认样式,则需要单独定义所有属性以使其显示。因此,Text如果未另外定义则黑色的默认颜色是黑色。

      触发器

      Triggers是一组适用于特定条件的样式。您可以认为Trigger作为其中的一部分,Style只有在Trigger满足为其定义的条件时才会设置

      有几种类型的触发器:

          1. 属性触发器:只有当DependencyProperty某个对象被设置为a 时才会被设置Value
          2. 数据触发器:适用于任何普通属性使用Binding
          3. 事件触发器:只有在控件触发某些事件时才能使用。

      现在来演示让我们看看下面的代码:

      <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
          <Setter Property="Text" Value="This is a TextBox with Styles"/>
          <Setter Property="HorizontalAlignment" Value="Center"/>
          <Setter Property="VerticalAlignment" Value="Center"/>
          <Setter Property="CharacterCasing" Value="Lower"/>
          <Setter Property="FlowDirection" Value="RightToLeft"/>
          <Setter Property="FontSize" Value="20"/>
          <Setter Property="FontWeight" Value="UltraBlack"/>
          <Setter Property="Width" Value="400"/>
          <Setter Property="Height" Value="40"/>
          <Setter Property="Margin" Value="0,20,0,10" />
          <Setter Property="Background">
              <Setter.Value>
                  <LinearGradientBrush>
                      <GradientStop Color="Cyan" Offset="0.0"/>
                      <GradientStop Color="Yellow" Offset="0.5"/>
                      <GradientStop Color="Red" Offset="1.0"/>
                  </LinearGradientBrush>
              </Setter.Value>
          </Setter>
          <Setter Property="Foreground">
              <Setter.Value>
                  <SolidColorBrush Color="Black"/>
              </Setter.Value>
          </Setter>
          <Setter Property="Effect" >
              <Setter.Value>
                  <DropShadowEffect BlurRadius="40" Color="Maroon" Direction="50"
      
                               Opacity="0.5"/>
              </Setter.Value>
          </Setter>
          <Style.Triggers>
              <Trigger Property="IsFocused" Value="True">
                  <Setter Property="Effect">
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" Color="Red" 
      
                                Direction="50" Opacity="0.9"/>
                      </Setter.Value>
                  </Setter>
              </Trigger>
              <MultiTrigger>
                  <MultiTrigger.Conditions>
                      <Condition Property="IsFocused" Value="True"/>
                      <Condition Property="IsMouseOver" Value="True"/>
                  </MultiTrigger.Conditions>
                  <Setter Property="Effect">
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" Color="Violet" 
      
                                  Direction="50" Opacity="0.9"/>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Foreground" Value="White" />
                  <Setter Property="Background" Value="Maroon" />
              </MultiTrigger>
          </Style.Triggers>
      </Style>

      在这里你可以看到我使用Property Trigger来改变DropShadowEffectTextBox的焦点时。每个WPF控件都暴露了很少的属性来处理属性触发器,根据控件外观的变化,属性触发器将被设置为true。你可以使用像这样的属性 IsFocused, IsMouseDown来解决属性触发器问题。

      第二次,我定义了一个MultiTriggerMultiTrigger允许您提及条件,以便在满足条件的所有条件时应用该对象MultiTrigger的属性Setters

      styletrigger.JPG

      因此,您可以看到何时将鼠标悬停在该TextBox文本框上,并且您的文本框的焦点在其中,只有这样才能看到TextBox出现在Maroon背景和Violet DropShadow效果中。

      动画基础

      另一个有趣的事情,你可能认为非常有趣的是WPF的动画支持。基本上,通过动画这个词,我们通常会想到3D空间中的大型纹理图形,这可能会在3DS MAX studio或MAC等中创建。但是相信我在WPF的情况下没有什么可担心的。WPF简化了概念动画是随着时间的推移而变化的属性。

      比如,假设你想要textbox随着时间的推移改变它的颜色,你会写一个简单的彩色动画来做到这一点,或者说你想Opacity在一段时间内改变一个Border元素,你需DoubleAnimation要这样做。如果你清楚它的工作原理,动画就很酷。

      动画类型

      我必须说,不要因为看到动画的类型而让自己变得更加困惑。实际上,动画的分类方式与对变量进行分类的方式相同。比如说:

          1. DoubleAnimation:这将使Double Value从一个值变为另一个值。所以如果你想改变一段TextBox时间的宽度,你需要DoubleAnimation
          2. ColorAnimation:如果更改元素的类型为Color,则与上面相同,则需要ColorAnimation。
          3. SingleAnimation, RectAnimation, PointAnimation, Int32Animaition, ThicknessAnimation 他们每个人都有相同的含义。

      所以基本上动画类型的基础是基于您希望动画工作的属性的类型。

      动画还可以分为两种基本方式:

          1. 无KeyFrame的动画:这些动画只需要两个值,From和To。它为您提供基于动画Timeline.DesiredFramerate属性的流畅动画。
          2. 动画与KeyFrames:允许您指定一个KeyFrame集合,让您可以在指定的时间定义KeyFrame值。以便您可以根据特定的时间间隔调整自己的动画。

      让我们来看看几个例子,让你了解WPF的动画特性:

      <Window.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                  <BeginStoryboard>
                      <Storyboard RepeatBehavior="Forever">
                          <DoubleAnimation Storyboard.TargetProperty="Width" 
      
            From="300" To="200" AutoReverse="True" Duration="0:0:5" ></DoubleAnimation>
                          <DoubleAnimation Storyboard.TargetProperty="Height" 
      
            From="300" To="200" AutoReverse="True" Duration="0:0:5"></DoubleAnimation>
                      </Storyboard>
                  </BeginStoryboard>
              </EventTrigger>
          </Window.Triggers>

      在上面的代码中,我定义了一个EventTrigger让你有一个DoubleAnimation(如宽度是双值)WidthWindow我们使用LoadedEvent来启动一个StoryBoard

      什么是StoryBoard?

      StoryBoard可以定义为TimeLines动画时间轴的容器或动画时间轴的集合,动画时间轴中指定的对象Target将制作动画。我们用它StoryBoard来指定动画。

      几个重要的属性StoryBoard

          • RepeatBehaviour:指定StoryBoard重复动画的次数。
          • 目标:指定storyboard将应用于的目标
          • TargetName:定义目标并通过其名称属性对其进行引用。
          • TargetProperty:指定要为其应用动画的属性。
          • AccelerationRatio / DecelerationRatio:定义动画的加速或减速。
          • AutoReverse:定义是否StoryBoard自动翻转。这是一个非常酷的概念,它可以让你获得由WPF自动生成的故事板时间线的反转。

      动画也可以从代码应用。

      DoubleAnimation myDoubleAnimation = new DoubleAnimation();
      myDoubleAnimation.From = 1.0;
      myDoubleAnimation.To = 0.0;
      myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));

      在上面的代码中,我声明了一个DoubleAnimation从1.0开始的代码,并在5秒内移动到0.0。

      动画与KeyFrames

      可以使用KeyFrames或不使用动画来定义动画KeyFramesKeyFrame允许您定义一个中间帧,以便为每个单独的帧间隔发生动画。一个插值有三种类型AnimationwithKeyFrames

          1. 线性
          2. 分离
          3. 仿样

      线性

      让我们创建一个动画使用KeyFrames

      <Border Background="Violet" 
      
                    HorizontalAlignment="Stretch"
      
                    VerticalAlignment="Stretch" >
                  <Border.Triggers>
                      <EventTrigger RoutedEvent="Border.MouseLeftButtonDown">
                          <EventTrigger.Actions>
                              <BeginStoryboard>
                                  <Storyboard>
                                      <DoubleAnimationUsingKeyFrames 
      
                                            Storyboard.TargetName="transformObj"
      
                                            Storyboard.TargetProperty="X"
      
                                            Duration="0:0:15">
                                          <LinearDoubleKeyFrame Value="500"
      
                      KeyTime="0:0:3" />
                                          <LinearDoubleKeyFrame Value="50" 
      
                      KeyTime="0:0:7" />
                                          <LinearDoubleKeyFrame Value="300" 
      
                      KeyTime="0:0:13" />
                                      </DoubleAnimationUsingKeyFrames>
                                  </Storyboard>
                              </BeginStoryboard>
                          </EventTrigger.Actions>
                      </EventTrigger>
                  </Border.Triggers>
                  <Border.RenderTransform>
                      <TranslateTransform x:Name="transformObj" X="0" Y="0" />
                  </Border.RenderTransform>
              </Border>

      这里应用动画作为LinearDoubleKeyFrame,这意味着动画将会平滑,同时我们基于KeyTime定义每个KeyFrame值。在这里,我们改变TranslationBorder基础上不同的KeyTime规定,使得在3秒,矩形会移动到500,在7秒它会在50和13日在第二它会在300动画是LinearDouble这样的动画是光滑稳定。

      分离

      如果我将动画更改为DiscreteAnimation仅将其放置在KeyTime指定的位置

      <DoubleAnimationUsingKeyFrames 
      
              Storyboard.TargetName="transformObj"
      
              Storyboard.TargetProperty="X"
      
              Duration="0:0:15">
          <DiscreteDoubleKeyFrame Value="500" KeyTime="0:0:3" />
          <DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:7" />
          <DiscreteDoubleKeyFrame Value="300" KeyTime="0:0:13" />
      </DoubleAnimationUsingKeyFrames>

      从而改变了LinearDoubleDiscreteDouble使它改变其位置,一下子基础上,KeyTime指定动画。

      仿样

      SplineAnimation用于为您的控件定义更逼真的动画行为。它可以让你控制动画的加速和减速。使用KeySpline,您可以使用Spline关键帧定义立方bazier曲线。让我们看看这个例子

      <DoubleAnimationUsingKeyFrames 
      
              Storyboard.TargetName="transformObj"
      
              Storyboard.TargetProperty="X"
      
              Duration="0:0:15">
          <SplineDoubleKeyFrame Value="500" KeyTime="0:0:3" KeySpline="0.0,0.1 0.1,0.1" />
          <SplineDoubleKeyFrame Value="50" KeyTime="0:0:7" KeySpline="0.0,0.1 0.1,0.1"/>
          <SplineDoubleKeyFrame Value="300" KeyTime="0:0:13" KeySpline="0.0,0.1 0.1,0.1"/>
      </DoubleAnimationUsingKeyFrames>

      因此,您可以看到KeySpline,您可以定义一个平滑的动画,以加速度开始并以最高速度在中间,最终再次减速。

      您也可以使用EasingFunction指定动画的自定义公式。欲了解更多信息,请尝试: KeyFrameAnimation [ ^ ]

    • WPF Tutorial - Styles, Triggers & Animation

      Introduction

      Perhaps the most interesting and most important feature for any WPF application is Styling. Styling means defining styles for controls, and store in reusable ResourceDictionaries and hence forth, it could be used later on by calling its name. Styles in WPF could be well compared with CSS styles. They are both similar in most of the cases, while the former extends the feature allowing most of the features which WPF have. In this article I will discuss mostly how you could use Style in your WPF application to enhance the Rich experience of your UI.

      So basically if you have already gone through the articles in the series, you must have already know most of the things on how you could apply your styles, how could you define style objects etc. In this article I will discuss how you could write better styles for your application.

      Style

      WPF exposes a property Style for every Control. If you look into the object Hierarchy, the Style is basically a property which exposes an object of Style in FrameworkElement. So each object can associate it and define custom setters to manipulate the basic look and feel of a control.

      WPFClassHierarchyStyle.JPG

      Clearly, the above diagram shows the association of Style in FrameworkElement and from the object hierarchy every control somehow inherits from FrameworkElement and hence style will be available to it. Style is also a WPF object which is inherited form DispatcherObject which helps in setting different properties of your UI Element.

      How Style differs from Theme ?

      Before we move further into Styles lets talk about Themes. Theme is totally different from Styles. Themes are defined at OS level, or more precisely a Theme can take part of delivering styles all over the Desktop while Styles are restricted to the contextual area of a WPF window. WPF are capable of retrieving the color scheme which is defined in OS level. Say for instance, if you do not define style for your application, the elements in the screen will automatically get styles from external environment. Say for instance, in XP if you change the theme to something else you would see that the buttons, TextBox on your WPF window will change its color instantly. You can even set the Theme which the application would useprogrammatically [^] from your code.

      What about Templates ?

      Every control defines a ControlTemplate. A ControlTemplate defines the overall structure of the control. As I have already told you, say for instance you have a Button. Button is a control that is made up of more than one control. It would have a ContentPresenter which writes the Text over the control, it would have a Rectangle which keeps the boundary of the Button etc. So Template is a special property associated with a Control which specifies how the control will look like structurally. We can easily define our Template and change the overall structure of a control.

      Templates are basically of 2 types :

          1. ControlTemplate
          2. DataTemplate 

      ControlTemplate defines the structure of the Control. It means say for instance, you define the ControlTemplate for a ComboBox. So from ControlTemplate you can easily change theToggleButton associated with the ComboBox which opens the DropDown, you can change the structure of the TextBox, the Popup etc. So ControlTemplate allows you to change the overall structure of the Control.

      Each control is made up of Data. Say for instance a ItemsControl contains a number of Data Element which builds the items inside the Popup. The DataTemplate could be associated with ItemsTemplateand will build up the Data Block for the ComboBox.

      templates.JPG

      So, you should always remember, ControlTemplate defines the whole Control while the DataTemplatedefines each individual Data Element.

      How to define Style?

      Normally a style is an unique object which is used to style WPF controls. Each WPF element contains a number of Dependency Properties. A dependency property defines the basic behavior and look of the control in UI. Styles maintains a collection of Setters which enumerates a DependencyProperty with its value.

      Thus you can say a style is a collection of DependencyProperty settings which when applied on a Target will change the behavior of it.

      Let us suppose you are going to style a TextBox.

      textbox_style.JPG
      <TextBox Text="This is a TextBox without Styles" 
      
                  HorizontalAlignment="Center" 
      
                  VerticalAlignment="Center" 
      
                  CharacterCasing="Lower" 
      
                  FlowDirection="RightToLeft"
      
                  FontSize="20" 
      
                  FontWeight="UltraBlack"
      
                  Width="400"
      
                  Height="40">
          <TextBox.Background>
              <LinearGradientBrush>
                  <GradientStop Color="Cyan" Offset="0.0"/>
                  <GradientStop Color="Yellow" Offset="0.5"/>
                  <GradientStop Color="Red" Offset="1.0"/>
              </LinearGradientBrush>
          </TextBox.Background>
          <TextBox.Foreground>
              <SolidColorBrush Color="Black"/>
          </TextBox.Foreground>
          <TextBox.Effect>
              <DropShadowEffect BlurRadius="40" Color="Maroon"
      
                 Direction="50" Opacity="0.5"/>
          </TextBox.Effect>
      </TextBox>

      So I have just designed a TextBox in the above code. The XAML looks straight forward, where I have configured different properties of the TextBox control to create my stylish TextBox. But looking at the code, you might wonder how difficult it would be if you need to redo the same thing again and again for every TextBox you define in your application. This is what the problem is. So WPF comes with an alternative with style. A style is an object that holds this behaviors into a collection of Setters. So lets redefine the same with Styles.

      textbox_style2.JPG
      <TextBox>
          <TextBox.Style>
              <Style TargetType="{x:Type TextBox}">
                  <Setter Property="Text" Value="This is a TextBox with Styles"/>
                  <Setter Property="HorizontalAlignment" Value="Center"/>
                  <Setter Property="VerticalAlignment" Value="Center"/>
                  <Setter Property="CharacterCasing" Value="Lower"/>
                  <Setter Property="FlowDirection" Value="RightToLeft"/>
                  <Setter Property="FontSize" Value="20"/>
                  <Setter Property="FontWeight" Value="UltraBlack"/>
                  <Setter Property="Width" Value="400"/>
                  <Setter Property="Height" Value="40"/>
                  <Setter Property="Background">
                      <Setter.Value>
                              <LinearGradientBrush>
                              <GradientStop Color="Cyan" Offset="0.0"/>
                              <GradientStop Color="Yellow" Offset="0.5"/>
                              <GradientStop Color="Red" Offset="1.0"/>
                          </LinearGradientBrush>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Foreground">
                      <Setter.Value>
                          <SolidColorBrush Color="Black"/>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Effect" >
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" 
      
                           Color="Maroon" Direction="50" Opacity="0.5"/>
                      </Setter.Value>
                  </Setter>
              </Style>
          </TextBox.Style>
      </TextBox>

      So you can see, I have defined the Style inside the TextBox and the textbox looks almost the same. The Setters allows you to enumerate all the properties for the TextBox and produced a style inside it whose TargetType is set to {x:Type Button}

      Now how this style can be made reusable for many controls ? Yes, this might be your first question that arose in your mind. Yes, if you have read my previous articles, you should already know the use of ResourceDictionaries. So in our case I will shift the style to Resource section for the Window and reuse the code just by calling the Resource key from the Textbox.

      <Grid>
          <Grid.Resources>
              <ResourceDictionary>
                  <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
                      <Setter Property="Text" Value="This is a TextBox with Styles"/>
                      <Setter Property="HorizontalAlignment" Value="Center"/>
                      <Setter Property="VerticalAlignment" Value="Center"/>
                      <Setter Property="CharacterCasing" Value="Lower"/>
                      <Setter Property="FlowDirection" Value="RightToLeft"/>
                      <Setter Property="FontSize" Value="20"/>
                      <Setter Property="FontWeight" Value="UltraBlack"/>
                      <Setter Property="Width" Value="400"/>
                      <Setter Property="Height" Value="40"/>
                      <Setter Property="Margin" Value="0,20,0,10" />
                      <Setter Property="Background">
                          <Setter.Value>
                              <LinearGradientBrush>
                                  <GradientStop Color="Cyan" Offset="0.0"/>
                                  <GradientStop Color="Yellow" Offset="0.5"/>
                                  <GradientStop Color="Red" Offset="1.0"/>
                              </LinearGradientBrush>
                          </Setter.Value>
                      </Setter>
                      <Setter Property="Foreground">
                          <Setter.Value>
                              <SolidColorBrush Color="Black"/>
                          </Setter.Value>
                      </Setter>
                      <Setter Property="Effect" >
                          <Setter.Value>
                              <DropShadowEffect BlurRadius="40" 
      
                                   Color="Maroon" Direction="50" Opacity="0.5"/>
                          </Setter.Value>
                      </Setter>
                  </Style>
              </ResourceDictionary>
          </Grid.Resources>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto" />
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="*"/>
          </Grid.RowDefinitions>
          <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="0" />
          <TextBox Style="{StaticResource MyTextBoxStyle}" Grid.Row="1" 
      
                      Text="The Style is modified here"
      
                      FlowDirection="LeftToRight"/>
      </Grid>
      textbox_style3.JPG

      So here I have shifted the Style into Resource section and used MyTextBoxStyle key to refer for each TextBox i defined. Notably, the style of both the textboxes remains same, while you can see I have also overridden certain settings in the control itself and it works the same. I have modified the Text of the 2nd TextBox to "The Style is modified here" and also made the FlowDirection to LeftToRight.

      Another important thing, that you should always keep into mind, that if you do not define the Key element for the Style in Resource section, it will automatically be applied to all the TextBox you define.

      <Style TargetType="{x:Type TextBox}">
      </Style>

      Say the style you define does not contain any Key. So all the TextBoxes will automatically apply the style when appeared. You can eventually use

      <TextBox Style="{x:Null}"/>

      to revert the style.

      Members of Style

      style_snap.JPG

      The styling of WPF controls is made up with the help of a class called Style. The style object exposes few properties which help you to define various behavior. Lets look into the properties:

          • Resources : It holds the reference for the ResourceDictionary where the Style is defined.
          • Setters : It is a collection which holds all the DependencyProperty configuration for the whole control.
          • TargetType : TargetType defines the type of the control for which the Style can be applied. So based on the TargetType the Style setters are defined to. So if you define a style for TextBox you cannot use Content as property Setter.
          • BasedOn : This is used to allow Style inheritance. You can use an existing style key to inherit all the properties to a new Style.
          • Triggers : A collection of Setters which would be applied based on certain conditions.

      Using those properties you can define your own styles.

      What about Explicit and Implicit Styles ?

      WPF controls can have two type of styles associated with it. A control can have a style defined in the application and applied to its Style property. If your control is using a Style to define its look and feel or basically your control has set an object of Style into its Style property, then it is using an Explicit Style.

      On the other hand, if your control takes the style from external environment (Theme) and the Style property is set to Null, then your control is using Implicit Style. Basically any WPF control automatically defines a DefaultStyle for it, so that you can set only the portion of the control which you need to change.

      Say for instance, you have a Button. If you want to have its Text to be colored Red, you just need to change the Foreground of the Button. You need not to define the whole style. If there is no Default Style defined for Buttons, you need to define all the properties individually to make it appear. Thus the default color of the Text is Black if not defined otherwise.

      Triggers

      Triggers are a set of styles that work on a particular condition. You can think Trigger as a part of Stylewhich will be set only when the Condition defined for the Trigger is met.

      There are few types of Triggers :

          1. Property Trigger : Will be set only when the DependencyProperty of a certain object has been set to a Value.
          2. Data Trigger : Will work for any normal Properties using on Binding.
          3. Event Trigger : Will work only when some event is triggered from the control.

      Now to demonstrate let us look into the code below :

      <Style TargetType="{x:Type TextBox}" x:Key="MyTextBoxStyle">
          <Setter Property="Text" Value="This is a TextBox with Styles"/>
          <Setter Property="HorizontalAlignment" Value="Center"/>
          <Setter Property="VerticalAlignment" Value="Center"/>
          <Setter Property="CharacterCasing" Value="Lower"/>
          <Setter Property="FlowDirection" Value="RightToLeft"/>
          <Setter Property="FontSize" Value="20"/>
          <Setter Property="FontWeight" Value="UltraBlack"/>
          <Setter Property="Width" Value="400"/>
          <Setter Property="Height" Value="40"/>
          <Setter Property="Margin" Value="0,20,0,10" />
          <Setter Property="Background">
              <Setter.Value>
                  <LinearGradientBrush>
                      <GradientStop Color="Cyan" Offset="0.0"/>
                      <GradientStop Color="Yellow" Offset="0.5"/>
                      <GradientStop Color="Red" Offset="1.0"/>
                  </LinearGradientBrush>
              </Setter.Value>
          </Setter>
          <Setter Property="Foreground">
              <Setter.Value>
                  <SolidColorBrush Color="Black"/>
              </Setter.Value>
          </Setter>
          <Setter Property="Effect" >
              <Setter.Value>
                  <DropShadowEffect BlurRadius="40" Color="Maroon" Direction="50"
      
                               Opacity="0.5"/>
              </Setter.Value>
          </Setter>
          <Style.Triggers>
              <Trigger Property="IsFocused" Value="True">
                  <Setter Property="Effect">
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" Color="Red" 
      
                                Direction="50" Opacity="0.9"/>
                      </Setter.Value>
                  </Setter>
              </Trigger>
              <MultiTrigger>
                  <MultiTrigger.Conditions>
                      <Condition Property="IsFocused" Value="True"/>
                      <Condition Property="IsMouseOver" Value="True"/>
                  </MultiTrigger.Conditions>
                  <Setter Property="Effect">
                      <Setter.Value>
                          <DropShadowEffect BlurRadius="40" Color="Violet" 
      
                                  Direction="50" Opacity="0.9"/>
                      </Setter.Value>
                  </Setter>
                  <Setter Property="Foreground" Value="White" />
                  <Setter Property="Background" Value="Maroon" />
              </MultiTrigger>
          </Style.Triggers>
      </Style>

      Here you can see I have used Property Trigger to change the DropShadowEffect of TextBox when it is focussed. Every WPF control exposes few properties to work with Property Triggers, which will be set to true based on the control appearance changes. You can use these properties like IsFocused, IsMouseDown etc to work around with Property Triggers.

      On the second occasion, I have defined a MultiTriggerMultiTrigger allows you to mention Condition, so that when all the conditions of the MultiTrigger is met, the Property Setters for the object is applied.

      styletrigger.JPG

      So you can see when you hover your mouse over the TextBox and your textbox has its focus in it, only then you see the TextBox to appear in Maroon background and Violet DropShadow effect.

      Animation Basics

      Another interesting thing that you might think very interesting is the support of Animation for WPF. Basically, by the word Animation, we generally think of large Texture graphics in 3D space, which would probably be created in 3DS MAX studio or MAC etc. But believe me there is nothing to worry about this in case of WPF. WPF simplifies the concept Animation to be the change of a property over time.

      Say for instance, say you want your textbox to change its color over time, you would write a simple color animation to do this or say you want to change the Opacity of a Border element during time, you need DoubleAnimation to do this. Animation is cool if you are clear about how it works.

      Type of Animation

      I must say, don't make yourself more confused by seeing the types of Animation. Animation is actually categorized in the same way as you categorize variables. Say for instance :

          1. DoubleAnimation : This will animate a Double Value from one value to another. So if you want to change the Width of a TextBox over time you need DoubleAnimation.
          2. ColorAnimation : Same as the above if the type of Changing element is Color, you need ColorAnimation.
          3. SingleAnimation, RectAnimation, PointAnimation, Int32Animaition, ThicknessAnimation etc each of them bears the same meaning.

      So basically the basis of Animation types is based on the type of the property for which you want your animation to work on.

      Animation can also be categorized into two basic ways :

          1. Animation Without KeyFrames : These are animation that only needs two values, From and To. It gives you a smooth animation based on the Timeline.DesiredFramerate property for the animation.
          2. Animation With KeyFrames : Allows you to specify a KeyFrame collection which lets you define the KeyFrame value on a specified time. So that you can adjust your own animation based on specific time intervals.

      Let us take a look at a few examples to make you understand animation feature of WPF:

      <Window.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                  <BeginStoryboard>
                      <Storyboard RepeatBehavior="Forever">
                          <DoubleAnimation Storyboard.TargetProperty="Width" 
      
            From="300" To="200" AutoReverse="True" Duration="0:0:5" ></DoubleAnimation>
                          <DoubleAnimation Storyboard.TargetProperty="Height" 
      
            From="300" To="200" AutoReverse="True" Duration="0:0:5"></DoubleAnimation>
                      </Storyboard>
                  </BeginStoryboard>
              </EventTrigger>
          </Window.Triggers>

      In the above code, I have defined an EventTrigger which lets you have a DoubleAnimation(as Width is double value) on Width of the Window. We use Loaded Event to start a StoryBoard.

      What is a StoryBoard ?

      StoryBoard can be defined as a Container for TimeLines or a collection of animation timelines for which the object specified in Target will animate. We use StoryBoard to specify Animation within it.

      Few important properties of StoryBoard :

          • RepeatBehaviour : Specifies the number of times for which the StoryBoard repeat the animation.
          • Target : Specifies the Target for which the storyboard will be applied to.
          • TargetName : Defines the target and reference it by its name attribute.
          • TargetProperty : Specifies the property for which the animation will be applied for.
          • AccelerationRatio / DecelerationRatio : Defines the acceleration or deceleration for the animation.
          • AutoReverse : Defines whether the StoryBoard will be reversed automatically. This is really cool concept, which allows you to get the reverse of the storyboard timeline automatically generated by the WPF.

      Animation can also be applied from code.

      DoubleAnimation myDoubleAnimation = new DoubleAnimation();
      myDoubleAnimation.From = 1.0;
      myDoubleAnimation.To = 0.0;
      myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(5));

      In the above code I have declared a DoubleAnimation which starts From 1.0 and moves to 0.0 in 5 seconds.

      Animation with KeyFrames

      Animation can be defined either using KeyFrames or without KeyFramesKeyFrame allows you to define an intermediate frame so that the animation occurs for each individual frame intervals. There are three types of interpolation for an AnimationwithKeyFrames.

          1. Linear
          2. Discrete
          3. Spline

      Linear

      Lets create an animation using KeyFrames :

      <Border Background="Violet" 
      
                    HorizontalAlignment="Stretch"
      
                    VerticalAlignment="Stretch" >
                  <Border.Triggers>
                      <EventTrigger RoutedEvent="Border.MouseLeftButtonDown">
                          <EventTrigger.Actions>
                              <BeginStoryboard>
                                  <Storyboard>
                                      <DoubleAnimationUsingKeyFrames 
      
                                            Storyboard.TargetName="transformObj"
      
                                            Storyboard.TargetProperty="X"
      
                                            Duration="0:0:15">
                                          <LinearDoubleKeyFrame Value="500"
      
                      KeyTime="0:0:3" />
                                          <LinearDoubleKeyFrame Value="50" 
      
                      KeyTime="0:0:7" />
                                          <LinearDoubleKeyFrame Value="300" 
      
                      KeyTime="0:0:13" />
                                      </DoubleAnimationUsingKeyFrames>
                                  </Storyboard>
                              </BeginStoryboard>
                          </EventTrigger.Actions>
                      </EventTrigger>
                  </Border.Triggers>
                  <Border.RenderTransform>
                      <TranslateTransform x:Name="transformObj" X="0" Y="0" />
                  </Border.RenderTransform>
              </Border>

      Here the animation is applied as LinearDoubleKeyFrame, which means the animation would be smooth while we define each KeyFrame value based on KeyTime. Here we change the Translation of the Border based on different KeyTime specified such that on 3rd second, the Rectangle will move to 500, at 7th second it will be at 50 and at 13th second it will be at 300. The animation is LinearDouble so the animation is smooth and steady.

      Discrete

      If I change the animation to DiscreteAnimation it will place the object only at the KeyTime specified

      <DoubleAnimationUsingKeyFrames 
      
              Storyboard.TargetName="transformObj"
      
              Storyboard.TargetProperty="X"
      
              Duration="0:0:15">
          <DiscreteDoubleKeyFrame Value="500" KeyTime="0:0:3" />
          <DiscreteDoubleKeyFrame Value="50" KeyTime="0:0:7" />
          <DiscreteDoubleKeyFrame Value="300" KeyTime="0:0:13" />
      </DoubleAnimationUsingKeyFrames>

      Thus changing the LinearDouble with DiscreteDouble makes it change its position all of a sudden based on the KeyTime specified for the animation.

      Spline

      SplineAnimation is used to define more realistic animation behavior for your control. It lets you control acceleration and deceleration of the animation. With KeySpline you can define the the cubic bazier curve using Spline Key frame. Lets look at the example

      <DoubleAnimationUsingKeyFrames 
      
              Storyboard.TargetName="transformObj"
      
              Storyboard.TargetProperty="X"
      
              Duration="0:0:15">
          <SplineDoubleKeyFrame Value="500" KeyTime="0:0:3" KeySpline="0.0,0.1 0.1,0.1" />
          <SplineDoubleKeyFrame Value="50" KeyTime="0:0:7" KeySpline="0.0,0.1 0.1,0.1"/>
          <SplineDoubleKeyFrame Value="300" KeyTime="0:0:13" KeySpline="0.0,0.1 0.1,0.1"/>
      </DoubleAnimationUsingKeyFrames>

      Thus you can see KeySpline allows you to define a smooth animation that starts with acceleration and with highest speed in the middle and ultimately decelerates back again.

      You can also use EasingFunction to specify the custom formulas for the animation. For further information try : KeyFrameAnimation [^]

      Conclusion

      I hope you like my article. I am also happy that I have covered almost most of the topics that you need to know in WPF. There are still a few things left behind, which I would cover in my next article. Please put your valuable feedback for the article.

      Thank you for reading.

  • 相关阅读:
    PID控制算法的C语言实现六 抗积分饱和的PID控制算法C语言实现
    PID控制算法的C语言实现五 积分分离的PID控制算法C语言实现
    python获取两个dict的不同
    将nosetests的echo结果保存到本地文件
    django 配置中STATICFILES_DIRS 和STATIC_ROOT不能同时出现
    Windows下使用最新的JDK1.7.0_51以上版本连接Jenkins出现SecurityException
    jenkins 启动slave,出现com.sun.deploy.net.FailedDownloadException: Unable to load resource: http://127.0.0.1:8080/jnlpJars/remoting.jar
    windows 下查看端口占用情况
    phpstorm + x-debug 进行php调试
    jenkins 升级jdk到1.8.0 报java.io.IOException:Unable to read /var/lib/jenkins/config.xml
  • 原文地址:https://www.cnblogs.com/Chary/p/No000012F.html
Copyright © 2020-2023  润新知