zoukankan      html  css  js  c++  java
  • WPF学习(9)样式和行为

    在asp.net世界中,我们的美工人员会为我们准备好静态页面,它注意包括三个部分:html、css和js。而在WPF世界里,也同样有着类似这三个部分的静态页面:Xaml、Style和Behaviors,当然,它们和前面三者的作用并不对等。Style几乎完成了css和js的功能,而Sliverlight 3中引入的Behaviors(封装到Expression Blend 3中和Expression Blend 3 SDK中)只是为了方便代码的复用,我们在后面详细来说。本文主要从Style样式和Behaviors行为两个方面来讲。

    1.Style

     先来看看Style类的属性:

    1.1Setters

    Setters是Style默认的内容属性,是Setter对象或者EventSetter对象的集合,所以可以省略Style.Setters而直接使用Setter或EventSetter。

    Setter是用于设置属性值的,这个属性还得是依赖属性。EventSetter是自动关联事件处理程序的。举个例子:

    xaml代码:

    <Window x:Class="StyleDemo.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <Style x:Key="labelStyle" TargetType="Label">
                <Setter Property="Foreground">
                    <Setter.Value>Red</Setter.Value>
                </Setter>
                <Setter Property="Control.FontSize" Value="28" />
                <Setter Property="Slider.FontFamily" Value="宋体" />
                <EventSetter Event="MouseMove" Handler="Label_MouseMove" />
            </Style>
            <Style x:Key="buttonStyle">
                <Setter Property="Button.FontSize" Value="22" />
                <Setter Property="Button.FontFamily" Value="SimSun" />
                <Setter Property="Button.FontWeight" Value="Bold" />
                <Setter Property="Button.Background" Value="Red" />
            </Style>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <Label Content="Hi,WPF" Style="{StaticResource labelStyle}" />
                <TextBlock Text="Sliverlight 5" Style="{StaticResource buttonStyle}"/>
                <Button Content="Click">
                    <Button.Style>
                        <Style>
                            <Setter Property="Control.Background">
                                <Setter.Value>
                                    <LinearGradientBrush>
                                        <GradientStop Offset="0" Color="Red" />
                                        <GradientStop Offset="0.5" Color="Blue" />
                                        <GradientStop Offset="1" Color="Yellow" />
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                        </Style>
                    </Button.Style>
                </Button>
                <Button Content="DoubleClick" Style="{StaticResource buttonStyle}"/>
            </StackPanel>
        </Grid>
    </Window>

     cs代码:

    private void Label_MouseMove(object sender, MouseEventArgs e)
    {
          MessageBox.Show("Mouse Move!!");
    }

     看下效果:

    需要说明一下几点:

    1)以资源的形式要比内嵌的形式更具灵活性

    2)在内嵌的形式中,设置属性值时,要么指定TargetType,要么使用Class.Property的形式

    3)"<Setter Property="Control.Background">...</setter>"与"<Setter Property="Background">...</setter>"的区别在于,前者先去设置Control的Backgroud属性,然后应用该样式的控件继承,而后者直接去设置应用该样式的控件的属性

    1.2Triggers

    Triggers,即为触发器,使用它可以自动完成简单的样式改变。主要有以下几种触发器:

    具个例子,一并说明下这五种触发器:

    xaml代码:

    <Window x:Class="StyleDemo.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:StyleDemo"
            Title="Window1" Height="300" Width="300">
        <Window.Resources>
            <!--Trigger属性触发器-->
            <Style x:Key="triggerKey">
                <Style.Triggers>
                    <Trigger Property="Control.IsMouseOver" Value="true">
                        <Setter Property="Control.Foreground" Value="Red" />
                        <Setter Property="Control.FontSize" Value="20" />
                    </Trigger>
                </Style.Triggers>
            </Style>
            <!--MultiTrigger多条件属性触发器-->
            <Style x:Key="multiTriggerKey">
                <Style.Triggers>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="Control.Foreground" Value="Red"></Condition>
                            <Condition Property="Control.IsMouseOver" Value="true"></Condition>
                        </MultiTrigger.Conditions>
                        <!--<MultiTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetProperty="(FrameworkElement.Width)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.0020000" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.3450000" Value="95"/>
                                    </DoubleAnimationUsingKeyFrames>
                                    <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"  Storyboard.TargetProperty="(FrameworkElement.Height)">
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.0020000" Value="0"/>
                                        <SplineDoubleKeyFrame KeyTime="00:00:00.3450000" Value="54"/>
                                    </DoubleAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiTrigger.EnterActions>-->
                        <MultiTrigger.Setters>
                            <Setter Property="Control.ToolTip" Value="Background:Red,FontSize" />
                        </MultiTrigger.Setters>
                    </MultiTrigger>
                </Style.Triggers>
            </Style>
            <!--EventTrigger事件触发器-->
            <Style x:Key="eventTriggerKey">
                <Style.Triggers>
                    <EventTrigger RoutedEvent="Mouse.MouseEnter">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="FontSize" To="22" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                    <EventTrigger RoutedEvent="Mouse.MouseLeave">
                        <EventTrigger.Actions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Duration="0:0:0.2" Storyboard.TargetProperty="FontSize" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger.Actions>
                    </EventTrigger>
                </Style.Triggers>
            </Style>
            <!--DataTrigger数据触发器-->
            <local:L2BConverter x:Key="l2bCvt" />
            <Style x:Key="dataTriggerKey">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=Text.Length,Converter={StaticResource l2bCvt}}" Value="false">
                        <Setter Property="Control.BorderBrush" Value="Red" />
                        <Setter Property="Control.BorderThickness" Value="1" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
            <!--MultiDataTrigger多条件数据触发器-->
            <local:L2BConverter x:Key="l2bCvt1" />
            <local:S2BConverter x:Key="s2bCvt" />
            <Style x:Key="multiDataTriggerKey">
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding RelativeSource={RelativeSource Self},Path=Text.Length,Converter={StaticResource l2bCvt1}}" Value="false" />
                            <Condition Binding="{Binding RelativeSource={RelativeSource Self},Path=Text,Converter={StaticResource s2bCvt}}" Value="false" />
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.Setters>
                            <Setter Property="Control.BorderBrush" Value="Red" />
                            <Setter Property="Control.BorderThickness" Value="1" />
                        </MultiDataTrigger.Setters>
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <Label Style="{StaticResource triggerKey}" Content="Hi,WPF" />
                <TextBlock Style="{StaticResource multiTriggerKey}" Foreground="Red" FontSize="16" Text="RIA World" />
                <Button x:Name="button" Style="{StaticResource eventTriggerKey}" Content="MouseEnter" />
                <TextBox Style="{StaticResource dataTriggerKey}"/>
                <TextBox Style="{StaticResource multiDataTriggerKey}"/>
            </StackPanel>
        </Grid>
    </Window>

     用到的两个Converter:

    字符串长度转布尔类:

        public class L2BConverter:IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return (int)value > 6 ? true : false;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                return null;
            }
        }

     字符串转布尔类

        class S2BConverter:IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                Regex regex = new Regex(@"^d*$");
                return !regex.IsMatch(value.ToString());
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

     效果大家可以copy代码运行看看。

    1.3Resources

    Style的Resources属性是ResourceDictionary类型,可以放一些在style中需要共享的对象资源。来看个例子:

    xaml代码:

    <Window x:Class="StyleDemo.Window2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window2" Height="300" Width="300">
        <Window.Resources>
            <Style x:Key="buttonKey">
                <Style.Resources>
                    <LinearGradientBrush x:Key="lgbKey">
                        <GradientStop Offset="0" Color="Red" />
                        <GradientStop Offset="0.5" Color="Green" />
                        <GradientStop Offset="1" Color="Blue" />
                    </LinearGradientBrush>
                </Style.Resources>
                <Setter Property="Control.Background" Value="{StaticResource lgbKey}" />
            </Style>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <Button Content="button" Style="{StaticResource buttonKey}" />
            </StackPanel>
        </Grid>
    </Window>

     效果如下:

    1.4BaseOn

    Style的BaseOn属性,可以实现Style的继承,从而实现多层样式。来看个简单的例子:

    xaml代码:

    <Window x:Class="StyleDemo.Window2"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window2" Height="300" Width="300">
        <Window.Resources>
            <Style x:Key="buttonKey">
                <Style.Resources>
                    <LinearGradientBrush x:Key="lgbKey">
                        <GradientStop Offset="0" Color="Red" />
                        <GradientStop Offset="0.5" Color="Green" />
                        <GradientStop Offset="1" Color="Blue" />
                    </LinearGradientBrush>
                </Style.Resources>
                <Setter Property="Control.Background" Value="{StaticResource lgbKey}" />
                <Setter Property="Control.FontFamily" Value="Times New Roman" />
                <Setter Property="Control.FontWeight" Value="Bold" />
                <Setter Property="Control.FontSize" Value="18" />
            </Style>
            <Style x:Key="buttonInheritKey" BasedOn="{StaticResource buttonKey}">
                <Setter Property="Control.Foreground" Value="DarkOrange" />
                <Setter Property="Control.FontSize" Value="22" />
            </Style>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <Button Content="button" Style="{StaticResource buttonKey}" />
                <Button Content="button1" Style="{StaticResource buttonInheritKey}" />
            </StackPanel>
        </Grid>
    </Window>

     效果图如下:

    1.5TargetType

    这个属性,指定的是应用该Style的控件的类型。我们以一个例子为切入点来说明下:

    xaml代码:

    <Window x:Class="StyleDemo.Window3"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Window3" Height="300" Width="300">
        <Window.Resources>
            <!--TargetType属性-->
            <Style TargetType="Button">
                <Setter Property="Foreground" Value="Yellow" />
                <Setter Property="FontSize" Value="28" />    
            </Style>
            <Style x:Key="buttonKey">
                <Setter Property="Control.FontSize" Value="12" />
            </Style>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <TextBox />
                <Button Content="Click" />
                <Button Content="Click2" Style="{StaticResource buttonKey}"/>
            </StackPanel>
        </Grid>
    </Window>

     效果如下:

    首先,你可能会奇怪,指定了TargetType的Style这里没有x:key来标识该资源,这是因为xaml解析器会自动以其对象类型来当作它的key,类似这样:x:key="{x:Type Button}"。

    另外,需要说明一下几点:

    1)设置了TargetType类型的Style会应用该种类型的所有控件。

    2)如果某该类型控件另外还设置了Style,会进行Merge的操作(由StaticResource或DynamicResource和TargetType确定的Style和ThemeStyle的合并)。StaticResource或DynamicResource和TargetType相同的Setter属性值,前者优先级高,不同的Setter属性值均起作用。

    2.Behaviors

    Style提供了重用一组属性设置的方法,为帮助构建统一良好的界面迈出了重要的一步,但是,还是有很多的限制,比如对于动画的支持不够。我们知道,通常要设计一个动画效果,需要很多的xaml代码。而这样的动画也经常会在其他的地方使用,但是我们却不得不复制那一大块代码,为了DRY,微软在Expression Blend 3推出了Behaviors行为的特性。

    首先,我们需要了解Behaviors这样几个关键点:

    1)Behaviors可复用代码集合(UI功能),可以被任何对象附加使用

    2)设计人员和开发人员只需要将它附加到元素上,而无需写任何的逻辑代码

    3)一个Behaviors可以被多个对象元素同时使用

    与Behaviors相关的程序集:

    System.Windows.Interactivity.dll,该链接库定义了Behaviors(行为)基础类,有了该链接库支持,即可支持Behaviors(行为);

    Microsoft.Expression.Interactions.dll,该链接库提供了一些扩展行为类库,以及一些Action和Trigger类,作为演示实例;

    Behaviors主要包括Trigger、Action和Behavior三个部分。需要注意的是,这里的Trigger和WPF的Trigger并不完全一样,可以同时使用它们。

    IAttachObject接口的定义很简单:

        // 摘要:
        //     供可以附加到另一个对象的对象使用的接口。
        public interface IAttachedObject
        {
            // 摘要:
            //     获得关联的对象。
            //
            // 备注:
            //     代表此实例附加到的对象。
            DependencyObject AssociatedObject { get; }
    
            // 摘要:
            //     附加到指定的对象。
            //
            // 参数:
            //   dependencyObject:
            //     要附加到的对象。
            void Attach(DependencyObject dependencyObject);
            //
            // 摘要:
            //     将此实例与其关联的对象分离。
            void Detach();
        }
    View Code

     AssociatedObject是一个只读的依赖对象属性,它指定行为的应用者。

    2.1自定义一个Behavior

    下面我们来自己定义一个Behaviors看看,要实现的效果是在Canvas中的控件可任意拖动:

    先定义一个继承自Behavior<UIElement>的DragInCanvasBehavior类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    
    namespace CustomBehaviorsLib
    {
        public class DragInCanvasBehavior:Behavior<UIElement>
        {
            protected override void OnAttached()
            {
                base.OnAttached();
                //Hook up event handlers
                this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
                this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
                this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
            }
    
            void AssociatedObject_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                if (isDragging)
                {
                    AssociatedObject.ReleaseMouseCapture();
                    isDragging = false;
                }
            }
    
            void AssociatedObject_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
            {
                if (isDragging)
                {
                    Point point = e.GetPosition(canvas);
                    AssociatedObject.SetValue(Canvas.TopProperty, point.Y-mouseOffset.Y);
                    AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
                }
            }
            private Canvas canvas;
            private bool isDragging = false;
            private Point mouseOffset;
            void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
            {
                if (canvas == null)
                    canvas = (Canvas)VisualTreeHelper.GetParent(this.AssociatedObject);
                isDragging = true;
                mouseOffset = e.GetPosition(AssociatedObject);
                AssociatedObject.CaptureMouse();
            }
            protected override void OnDetaching()
            {
                base.OnDetaching();
                //detach event handlers
                this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
                this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
                this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
            }
        }
    }
    View Code

    然后在window4.xaml中添加对DragInCanvasBehavior类所在类库的引用,同时添加System.Window.Interactivity.dll的引用。

    xaml代码如下:

    <Window x:Class="StyleDemo.Window4"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            xmlns:custom="clr-namespace:CustomBehaviorsLib;assembly=CustomBehaviorsLib"
            Title="Window4" Height="300" Width="300">
        <Grid>
            <Canvas x:Name="p_canvas">
                <Canvas x:Name="c_canvas1" Width="100" Height="100" Background="Yellow" />
                <Canvas x:Name="c_canvas2" Width="100" Height="100" Canvas.Left="110" Background="Red">
                    <i:Interaction.Behaviors>
                        <custom:DragInCanvasBehavior />
                    </i:Interaction.Behaviors>
                </Canvas>
                <Canvas x:Name="c_canvas3" Width="100" Height="100" Canvas.Top="110" Background="Blue" />
            </Canvas>
        </Grid>
    </Window>
    View Code

    通过向BehaviorCollection类型的Interaction.Behaviors附加属性添加DragInCanvasBehavior实例。

    效果如下:

    这里只是给红色的Canvas添加了DragInCanvasBehavior,所以只有红色的Canvas可以Drag。

    Expression Blend 3 以及之后的版本(4和4.5)都有Behaviors提供了很好的支持,内部提供了好多封装好的Behaviors。况且,使用blend可以很方便简单的使用它。

    2.2Expression Blend中Behaviors

    Expression Blend是和Visual Studio配套使用的,也就是说对应的采用的是相同的解决方案文件格式,Blend 3对应VS2008,Blend 4对应VS2010,Blend 5对应VS2012。

    这里提供许多现成的行为,也包含自定义的Behaviors。用法基本类似,这里我们以MouseDragElementBehavior为例,来介绍下在Expression Blend中对Behaviors的使用。

    先向界面拖放一个主Canvas,里面放三个子Canvas,背景颜色分别设为Yello、Red和Blue。然后将MouseDragElementBehavior拖放到红色的子Canvas上面,代码便自动完成了,实现了与上面相同的效果,及其方便快捷。

    有关Expression Blend的使用技巧,将会单独来讲,在后面模板和动画的学习中还将会提到。

  • 相关阅读:
    补码原理
    清风徐来,水波不兴
    月尾和周尾
    又一春又一季
    9.11
    晨光无限
    9.18
    心悠
    小事一桩
    一周岁啦
  • 原文地址:https://www.cnblogs.com/jellochen/p/3551915.html
Copyright © 2011-2022 走看看