Brushes画刷集合

clipboard.png
clipboard.png

  • 主框架界面

实现效果:

  1. 左侧的可折叠/展开选项单。
  2. 界面选项单内容的层次结构TreeView
  3. 根据选择页面项 导航展示各页面内容

关注词:

  1. CommandBinding+InputBinding组合
  2. Expander控件标题+内容自定义
  3. TreeView背景毛玻璃+Item玻璃效果
  4. Xml+分层数据模板
  • 菜单项命令的输入绑定及命令绑定组合
  1. 设置当前命令为系统已设定的退出命令,并在之后附加执行命令方法
  2. 当前命令的输入命令为ALT+F4键盘绑定,它们一起组合成通过键盘操作即可执行自定义操作。
<MenuItem Header="_File" >
    <MenuItem Command="{x:Static examples:SampleViewer.ExitCommand}">
      <MenuItem.CommandBindings>
        <CommandBinding 
          Command="{x:Static examples:SampleViewer.ExitCommand}" 
          Executed="ExecuteExitCommand" />
      </MenuItem.CommandBindings>
      <MenuItem.InputBindings>
        <KeyBinding 
          Command="{x:Static examples:SampleViewer.ExitCommand}"
          Key="F4" Modifiers="Alt" />
      </MenuItem.InputBindings>
    </MenuItem>
</MenuItem>
  • Expander控件中标题内容自定义

1、控件扩展方向设为左侧,标题中文本方向设置LayoutTransform转换90°垂直

<Expander ExpandDirection="Left">
    <Expander.Header>
      <TextBlock Foreground="#99000000"
        FontFamily="Verdana" FontWeight="Bold" FontSize="24pt" 
        Text="Contents">
        <TextBlock.LayoutTransform>
          <RotateTransform Angle="90" />
        </TextBlock.LayoutTransform>
      </TextBlock>
    </Expander.Header>

2、控件内容区包含设置两个Border边框,其背景色使用图像画刷ImageBrush资源填充,显示出青灰色及白色图像画刷的叠加效果。
3、边框内设置TreeView控件,其控件的上下方向键导航设置为循环,默认为Continue。
4、TreeView控件设置背景色透明,子项模板ItemTemplate:分层数据模板HierarchicalDataTemplate;子项ItemContainerStyle
5、数据绑定源key为XmlDataProvider提供,其Source为xml文件、路径为根Examples的空引用。
6、TreeView设有事件触发器,选择改变时会触发Fame区不透明度为0动画,即显示为空白区。此项动画由当前状态验证CurrentStateInvalidated绑定的方法判断。

<!-- Lists the different brush samples. The list is
       defined in the sampleResources\TOC.xml file. -->
  <TreeView Name="myPageList"
    KeyboardNavigation.DirectionalNavigation="Cycle" 
    BorderBrush="Transparent"
    Background="Transparent"
    ItemTemplate="{DynamicResource ExamplesDataTemplate}"
    ItemContainerStyle="{StaticResource TreeViewItemHeaderStyle}"
    SelectedValuePath="Example">
    <TreeView.ItemsSource>
      <Binding Source="{StaticResource ExampleData}" XPath="*"/>
    </TreeView.ItemsSource>
    <TreeView.Triggers>
      <EventTrigger RoutedEvent="TreeView.SelectedItemChanged">
        <BeginStoryboard>
          <Storyboard>
            <DoubleAnimation 
              Storyboard.TargetName="myFrame"
              Storyboard.TargetProperty="Opacity" 
              From="1" To="0" Duration="0:0:0.1" 
              CurrentStateInvalidated="TransitionAnimationStateChanged"/>
          </Storyboard>
        </BeginStoryboard>
      </EventTrigger>
    </TreeView.Triggers>
  </TreeView>

TreeView选择子项时导航到对应页面情况:

  1. 在页切换间有个0.1s的小动画,使Frame显示区为空白。
  2. 在动画线开始后ClockState.Filling下就进行后续判断操作
 private void TransitionAnimationStateChanged(object sender, EventArgs args)
{
    var transitionAnimationClock = (AnimationClock) sender;
    if (transitionAnimationClock.CurrentState == ClockState.Filling)
    {
        FadeEnded();
    }
}

Frame区显示判断:选择项的值转换为Xml元素,获取其特性 Uri值导航页面显示。若值为空则保持动画后的空白模样。

private void FadeEnded()
{
    var el = (XmlElement) myPageList.SelectedItem;
    var att = el.Attributes["Uri"];
    if (att != null)
    {
        myFrame.Navigate(new Uri(att.Value, UriKind.Relative));
    }
    else
    {
        myFrame.Content = null;
    }
}

页面导航时Frame对空白恢复显示:
代码获取动画资源,设置到Frame的不透明度属性上。

private void MyFrameNavigated(object sender, NavigationEventArgs args)
{
    var myFadeInAnimation = (DoubleAnimation) Resources["MyFadeInAnimationResource"];
    myFrame.BeginAnimation(OpacityProperty, myFadeInAnimation, HandoffBehavior.SnapshotAndReplace);
}
  • TreeViewItem样式内容:

TreeViewItem自定义控件模板:

以下图中TreeView的2层背景色(白色)是背景方框取消后。
1、最底层方框:透明填充,大圆角RadiusXY=10,Rectangle.Stroke默认为null;当选中为焦点时,变现Stroke设为亮蓝。
clipboard.png

<Rectangle x:Name="outerRectangle" 
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
    Fill="{TemplateBinding Background}" RadiusX="10" RadiusY="10" 
    StrokeThickness="5" Grid.ColumnSpan="2" Opacity="1" />
    
<Trigger Property="IsFocused" Value="true">
    <Setter Property="Rectangle.Stroke" 
    Value="{x:Static SystemColors.HighlightBrush}" 
    TargetName="outerRectangle" />    

2、次层方框显示为玻璃效果,使用图像ImageBrush
填充,但Opacity为0不显示,设置圆角10,Stroke宽度为1,但还是设置了上透明下灰线变色,与方框的外发光OuterGlowBitmapEffect及凹凸BevelBitmapEffect位图效果进行适配;当选择焦点时,触发器是方框的Opacity=1显现效果。

clipboard.png

3、标题前部的图标(收缩为十字,展开为一字)样式:

  1. 扩展收缩IsChecked值绑定到自身已设置值。
  2. 触发器设置探测本项无子项时即图标显示隐藏。
  3. 点击事件ButtonBase.ClickMode设置到为按下按钮触发

clipboard.png

<ToggleButton 
    Name="ExpanderIcon"
    Grid.Row="0" Grid.Column="0"
    Style="{StaticResource ExpandCollapseToggleStyle}"
    IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
    ClickMode="Press" />

<Trigger Property="HasItems"
   Value="False">
            
            <Setter TargetName="ExpanderIcon"
    Property="Visibility"
    Value="Hidden"/>    
    

以上切换按钮ToggleButton图标的样式ExpandCollapseToggleStyle:

  1. 设置好长宽,不可焦点属性,其模板设置Path及Data值
  2. ControlTempalte.Triggers又设置点击时的十/一字切换。
<Style x:Key="ExpandCollapseToggleStyle"
      TargetType="{x:Type ToggleButton}">
    <Setter Property="Focusable"
      Value="False"/>
    <Setter Property="MinWidth"
      Value="19"/>
    <Setter Property="MinHeight"
      Value="13"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border MinWidth="19"
            MinHeight="13"
            Background="{StaticResource GlassBrushResource}">
                    <Border MinWidth="9"
              MinHeight="9"
              BorderThickness="1"
              BorderBrush="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}"
              Background="Transparent"
              SnapsToDevicePixels="true">
                        <Path x:Name="ExpandPath"
          Stretch="Uniform"
              Margin="1,1,1,1"
              Fill="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
              Data="M 0 2 L 0 3 L 2 3 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 3 2 L 3 0 L 2 0 L 2 2 Z"/>
                        <Border.BitmapEffect>
                            <DropShadowBitmapEffect />
                        </Border.BitmapEffect>
                    </Border>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked"
               Value="True">
                        <Setter Property="Data"
                TargetName="ExpandPath"
                Value="M 0 2 L 0 3 L 5 3 L 5 2 Z"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

4、标题头Icon处的另一个图像:

  1. 与上个十/一图标一起设置,但设置可见性隐藏;绑定源为DrawingImage-DrawingGroup-GeometryDrawing内容。
  2. 当本项触发无子项(下级内容)事件时,设置可见。

clipboard.png

clipboard.png

<Image 
    Name="ItemIcon"
    Grid.Row="0" Grid.Column="0"
    Source="{StaticResource PaperDrawingImage}"
    Visibility="Hidden"
    Margin="2" />

5、标题头内容显示:名称设为PART_Header,内容源为Header

<ContentPresenter 
    Name="PART_Header" Grid.Column="1" Margin="10,5,10,5" 
    TextBlock.Foreground="Black"
    ContentSource="Header" />

6、点击Item展开和收缩以分层使用的ToggleButton(必备):

  1. 显示分层结构的标题头样式,绑定上级IsExpanded,内容控件中方框背景色为透明
  2. 当本项没有子项时,触发可见性隐藏,即点击本层一次时,下层展开显示或隐藏。

clipboard.png

<ToggleButton 
    Name="Expander"   
   Grid.Column="0" Grid.ColumnSpan="2"
   Visibility="Visible"
   IsChecked="{Binding Path=IsExpanded,RelativeSource={RelativeSource TemplatedParent}}"
   ClickMode="Press">
                    <ToggleButton.Template>
                        <ControlTemplate>
                            <Rectangle Fill="Transparent" />
                        </ControlTemplate>
                    </ToggleButton.Template>
                </ToggleButton>
                
<Trigger Property="HasItems"
   Value="False">
            <Setter TargetName="Expander"
    Property="Visibility"
    Value="Hidden"/>             

7、Items主容器(必备)

<ItemsPresenter x:Name="ItemsHost"
                 Grid.Row="1"
                 Grid.Column="1" />

8、TreeViewItem控件触发器ControlTemplate.Triggers:

  1. 当鼠标选择及悬浮控件上时,开始执行或退出执行对次层方框的透明度动画
  2. 当选中/取消选中本项事件触发时,执行玻璃样式的透明度、边框、位图颜色动画
  3. 收缩/扩展时触发属性设置主容器的可见度为Collapsed
<ControlTemplate.Triggers>
    <MultiTrigger>
        <MultiTrigger.Conditions>
            <Condition Property="IsMouseOver" Value="true" />
            <Condition Property="IsSelected" Value="False" />
        </MultiTrigger.Conditions>
        <MultiTrigger.EnterActions>
            <RemoveStoryboard BeginStoryboardName="UncheckedBeginStoryboard" />
            <BeginStoryboard Name="MouseOverBeginStoryboard">
                <Storyboard>
                    <DoubleAnimation 
    Storyboard.TargetName="glassCube"
    Storyboard.TargetProperty="Opacity"
    To="0.75"
    Duration="0:0:0.1" />
                    <DoubleAnimation 
    Storyboard.TargetName="glassCubeBrush"
    Storyboard.TargetProperty="Opacity"
    From="0.75" To="0.5" AutoReverse="True"
    RepeatBehavior="Forever"
    Duration="0:0:0.75" />
                </Storyboard>
            </BeginStoryboard>
        </MultiTrigger.EnterActions>
        <MultiTrigger.ExitActions>
            <RemoveStoryboard BeginStoryboardName="MouseOverBeginStoryboard" />
        </MultiTrigger.ExitActions>
    </MultiTrigger>
    
    <EventTrigger RoutedEvent="TreeViewItem.Selected">
        <BeginStoryboard>
            <Storyboard>
                <DoubleAnimation 
      Storyboard.TargetName="glassCube"
      Storyboard.TargetProperty="Opacity"
      To="0.75" 
      Duration="0:0:0.1" />
                <DoubleAnimation 
      Storyboard.TargetName="glassCube"
      Storyboard.TargetProperty="StrokeThickness"
      To="2"
      Duration="0:0:0.1" />
                <ColorAnimation 
      Storyboard.TargetName="glowBitmapEffect"
      Storyboard.TargetProperty="GlowColor"
      To="{x:Static SystemColors.HighlightColor}"
      Duration="0:0:1" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
    <EventTrigger RoutedEvent="TreeViewItem.Unselected">
        <BeginStoryboard Name="UncheckedBeginStoryboard">
            <Storyboard>
                <DoubleAnimation 
      Storyboard.TargetName="glassCube"
      Storyboard.TargetProperty="Opacity"
      To="0"
      Duration="0:0:0.1" />
                <DoubleAnimation 
      Storyboard.TargetName="glassCube"
      Storyboard.TargetProperty="StrokeThickness"
      To="1"
      Duration="0:0:0.1" />
                <ColorAnimation 
      Storyboard.TargetName="glowBitmapEffect"
      Storyboard.TargetProperty="GlowColor"
      To="{x:Static SystemColors.ControlColor}"
      Duration="0:0:0.1" />
            </Storyboard>
        </BeginStoryboard>
    </EventTrigger>
    <Trigger Property="IsFocused" Value="true">
        <Setter Property="Rectangle.Stroke" 
    Value="{x:Static SystemColors.HighlightBrush}" 
    TargetName="outerRectangle" />
        <Setter Property="Rectangle.Opacity" Value="1" TargetName="glassCube" />
    </Trigger>
    
    <Trigger Property="IsExpanded" Value="false">
        <Setter TargetName="ItemsHost"
    Property="Visibility"
    Value="Collapsed"/>
    
    </Trigger>

可改进:Treeview内容应有滚动条及在鼠标在内容区能滚动操作,需控件处在ScrollView中显示
关注问题:

  • 点击TreeView项时,怎样引导Frame区的改变及显示。
  • 选择TreeView的标题时,怎样判断选择项及Frame变成空白区

扩展:

  1. KeyboardNavigation 类:在可获得焦点的对象之间提供逻辑和方向导航。

    1. KeyboardNavigation 类负责在按下导航键之一时执行默认键盘焦点导航。 这些导航键为:Tab、Shift+Tab、Ctrl+Tab、Ctrl+Shift+Tab、向上键、向下键、向左键和向右键。
    2. 逻辑导航的一个示例是使用 Tab 键来移动焦点。方向导航的一个示例是使用箭头键来移动焦点。
  2. Timeline.CurrentStateInvalidated 事件:在时间线 Clock 的 CurrentState 属性更新时发生。

    1. 如果希望在时间线的 Clock 开始、停止或填充时收到通知,请使用 CurrentStateInvalidated 事件。
    2. 暂停 Clock 不会更改其 CurrentState。 若要在时钟变为暂停时收到通知,请使用 CurrentGlobalSpeedInvalidated 事件。
    3. 虽然此事件在 ClockState 变为无效时发生,但并不一定意味着 ClockState 已更改:在同一个计时周期中将 Clock 从 Active 切换为 Filling,然后再切换为 Active 将导致引发此事件,但它的 CurrentState 属性实际并不会更改。
    4. EventHandler 事件处理程序的 Object 参数是为此时间线创建的 Clock。虽然此事件处理程序看上去与时间线相关联,但它实际向为此时间线创建的 Clock 注册。

李志玮
22 声望34 粉丝

求索~~


引用和评论

0 条评论