在Windows 8.1 store 应用中使用Behavior


Behavior一直是广大Windows store应用开发者翘首以盼的一个功能。在理想的情况下,开发人员负责开发提供功能的Behavior库,设计人员就可以通过在XAML中选择相应的Behavior来完成需要的功能,这大大降低了UI代码和应用逻辑代码之间的耦合度。可是在Windows 8 store应用开发中却没有提供对Behavior的支持。虽然说有开发者采用附加依赖属性(Attached dependency property)给出了实现办法,例如Joost van Schaikwinrtbehaviors,但这毕竟不是一个正式的解决方案。幸运的是,最新发布的Blend for Visual Studio 2013 已经提供了为Windows 8.1 Store应用开发添加Behavior的功能,现在我们既可以方便的使用内置的behavior,也可以为您的应用添加自定义的behavior了。本文主要就Blend for Visual Studio 2013中内置BehaviorAction作一个简单的介绍。

所谓Behavior,就是用于附加在某个控件上的行为。既然是行为么,通常就有时间(什么时候触发),地点(在什么对象上触发),事件(有什么功能)。所以设定一个Behavior也无外乎确定这几个方面。

根据触发的时机不同,Blend for Visual studio 2013提供了三种不同的Behavior:

  • EventTriggerBehavior:使用事件(event)来触发行为

  • DataTriggerBehavior:使用数据变化来触发行为

  • IncrementalUpdateBehavior:当GridViewListView控件增量更新时触发的行为。增量更新是这两个控件在Windows 8.1中的新特性。

由于IncrementalUpdateBehavior比较特殊,需要联系到增量更新的功能来讲,我会在一篇文章中介绍。本文中我们只讨论EventTriggerBehaviorDataTriggerBehavior的用法。

Action指的是某个特定的功能,通常需要和Behavior配合起来使用。Blend for Visual Studio 2013提供了七种不同的Action:

  • CallMethodAction:调用某个特定的函数

  • ChangePropertyAction:改变某个属性的值

  • ControlStoryboardAction:控制动画的Storyboard

  • GoToStateAction: 使控件进入某个状态

  • InvokeCommandAction:触发调用Command

  • NavigateToPageAction: 导航到某个页面

  • PlaySoundAction:播放声音

以上这些BehaviorAction基本上涵盖了大多数使用Behavior的场合。当然如果上述BehaviorAction还不能满足你的要求,你也可以通过IBehavior接口和IAction接口来实现自己的BehaviorAction

首先,让我们来看一下EventTriggerBehaviorEventTriggerBehaviorBlend中是缺省的Behavior,也就是说,当你在Blend中拖一个Action到控件上的时候,Blend会自动为这个Action加上一个EventTriggerBehavior。要使用这个EventTriggerBehavior,你需要设置三个属性:EventName, SourceObjectActions

  1. EventName用来设定触发该Behavior的事件,你可以选择SourceObject中指定对象支持的任一事件。
  2. SourceObject用来选择触发该事件的源对象。
  3. Actions是个集合,用来设置当该事件发生时需要执行的Action,因为这是一个集合,所以对每个Behavior都可以设置一组动作。

DataTriggerBehaviorEventTriggerBehavior的区别在于触发方式的不同。DataTriggerBehavior通过属性值的变化来触发动作。所以,它除了有Actions属性以外,还有三个用于设置触发方式的属性:

  1. Binding用于设定需要比较的属性,这个属性需要实现INotifyPropertyChanged接口。
  2. ComparationCondition用于设定比较条件
  3. Value用于设定比较的值

无论是DataTriggerBehavior还是DataTriggerBehavior都需要设置Actions. Blend中内置了七种不同的Action, 接下来就让我们演示一下这七种Action是如何配合Behavior一起的使用的。

首先我们需要使用Blend for Visual Studio 2013创建一个Windows 8.1 store app的项目,如果使用Blend添加Behavior或者Action的话,Blend会在该项目中自动添加一个引用:

         BehaviorsXamlSDKManaged

如果你使用Visual Studio 2013的话,可以在ReferenceWindows Extension中找到它:

 

这样我们就可以在项目中使用BehaviorAction了。

接下来我们用七个不同的实例来演示这七种Action, 根据演示的效果的不同,我们会参差的使用EventTriggerBehaviorDataTriggerBehavior:

 

1. NavigateToPageAction

NavigateToPageAction能只通过XAML代码就完成导航操作。对演示项目中建立的每一个Page,我们都可以通过NavigateToPageAction来方便的进行导航。

首先使用Blend在项目中添加两个页面, 一个是NavigateToPageActionDemo页面,用于演示NavigateToPageAction功能,另一个是为演示后面的CallMethodAction用的,所以就叫CallMethodActionDemo

然后在NavigateToPageActionDemo中添加一个Button,从Asserts中拖一个NavigateToPageActionButton上,修改NavigateToPageAction中的TargetPage属性为CallMethodActionDemo

 

Blend会自动为这个Action添加一个EventTriggerBehavior,它的触发事件为Click。最终生成的XAML代码如下图所示:

<Interactivity:Interaction.Behaviors>

            <Core:EventTriggerBehavior EventName="Click">

<Core:NavigateToPageAction TargetPage="BehaviorDemo.CallMethodActionDemo"/>

            </Core:EventTriggerBehavior>

</Interactivity:Interaction.Behaviors>

Button被点击的时候,通过NavigateToPageAction,我们就可以从NavigateToPageActionDemo进入CallMethodActionDemo页面,整个导航过程不需要写一行code behind代码。

 

2. CallMethodAction

CallMethodActionEventTriggerBehavior配合使用的时候有些类似于Event Handler,也是在事件触发的时候调用后台方法。但是,CallMethodAction并不需要像Event Handler那样放在code behind代码中,而是可以放在单独的库文件中,通常我们可以把方法写在ViewModel中来实现UI和代码逻辑的分离。

这里我通过添加MvvmLight组件,加入了一个简单的ViewModel实现。然后在ViewModel中添加了一个方法SayHello:

        public async void SayHello()

        {

            var msgDlg = new MessageDialog("Hello World");

            msgDlg.DefaultCommandIndex = 1;

            await msgDlg.ShowAsync();

        }

紧接着我们在CallMethodActionDemo页面中加入了一个Button,为这个Button拖一个CallMethodAction上去,绑定它的TargetObjectViewModel,设定MethodNameSayHello.

 

同样这里Blend会为它添加一个缺省的EventTriggerBehavior,所以这里就不需要额外设置EventTriggerBehavior了。最终生成的XAML代码如下所示:

            <Interactivity:Interaction.Behaviors>

                <Core:EventTriggerBehavior EventName="Click">

                    <Core:CallMethodAction

                              TargetObject="{Binding Mode=OneWay}"

                              MethodName="SayHello"/>

                </Core:EventTriggerBehavior>

    </Interactivity:Interaction.Behaviors>

当我们点击Button的时候,就会弹出SayHello的消息对话框。由于Method代码不依赖于UI,所以可以由开发人员实现在ViewModal中,而设计人员也不需要知道Method代码的细节就可以在Blend中通过拖拉的方式添加事件处理函数了。

3.     InvokeCommandAction

InvokeCommandAction的作用和直接使用Command是一样的,但是,Command缺省只支持三种控件:基于Button的控件,基于Menu的控件以及HyperlinkClick事件。而InvokeCommandAction则能够在任何EventTriggerBehaviorDataTriggerBehavior能够触发的地方调用。

这里我实现了一个用于改变TextBox中字体颜色的Command,该Command会在用户键入颜色单词时将字体颜色改变为对应的颜色,同时我在UI上添加了一个Toggle Switch用于控制该功能是否可用。基于MVVM架构,我先将TextBoxText以及Foreground属性绑定到Viewmodal中的两个后台属性InputTextForgroundColor上;再将Toggle SwitchIsOn属性绑定到后台属性EnableChange上;然后定义了以下Command来改变颜色:

        private RelayCommand _changeColorCommand;

        public RelayCommand ChangeColorCommand

        {

            get

            {

                if (_changeColorCommand == null)

                    _changeColorCommand = new RelayCommand(() =>

                            ChangeColor(),()=>CanChange());

                return _changeColorCommand;

            }

        }

        private void ChangeColor()

        {

            //convert from string to color

            var property = typeof(Colors).GetRuntimeProperty(InputText);

            if(property!=null)

            {

                Color c = (Color)property.GetValue(null);

                ForgroundColor = new SolidColorBrush(c);

            }  

        }

         private bool CanChange()

        {

            if (EnableChange)

                return true;

            else

                return false;

        }

由于TextBox的内容每次改变时InputText的值都会作相应的变化,所以这里我们使用DataTriggerBehavior来触发输入文本的改变。BlendDataTriggerBehavior不是缺省的Behavior,我们需要先拖一个DataTriggerBehaviorTextBox控件上,设置其触发条件为InputText不为空,然后再拖一个InvokeCommandActionDataTriggerBehavior上,将其Command属性绑定到ViewModalChangeColorCommand上去。

 

 

 最后生成的代码如下所示:

            <Interactivity:Interaction.Behaviors>

                <Core:DataTriggerBehavior Binding="{Binding InputText}"

                        ComparisonCondition="NotEqual" Value="">

                    <Core:InvokeCommandAction Command="{Binding

                                        ChangeColorCommand, Mode=OneWay}"/>

                </Core:DataTriggerBehavior>

            </Interactivity:Interaction.Behaviors>

这样,当我们把Toggle switch设为On的时候,在TextBox框中输入颜色的名称会自动改变输入文本的颜色,如果把Toggle switch设为Off,那么该功能就会被关闭。

 

4.     ChangePropertyAction

ChangePropertyAction用于改变某个对象属性的值,这个对象可以是UI上的一个控件,也可以是后台的一个数据对象。在XAML中,通常我们使用绑定的方式来改变属性的值,但是无法在特定的时机触发绑定。通过ChangePropertyAction,我们就可以在XAML中动态改变某个属性的值了。

在下面这个示例中,我们用ChangePropertyAction来设置TextBoxBackground颜色以获得高亮的效果,当TextBox获得焦点的时候设置为高亮色,当TextBox失去焦点的时候设置为浅灰色色。在Blend里面,操作非常简单,只需要将两个ChangePropertyAction拖到TextBox上,分别设置EventTriggerBehaviorEventNameGetFocusLostFocus,然后设置ChangePropertyActionPropertyNameBackground,设置Value为相应的颜色值:

   

Blend生成的代码如下:

         <Interactivity:Interaction.Behaviors>

                   <Core:EventTriggerBehavior EventName="GotFocus">

            <Core:ChangePropertyAction PropertyName="Background"

                                        Value="Purple"/>

        </Core:EventTriggerBehavior>

        <Core:EventTriggerBehavior EventName="LostFocus">

            <Core:ChangePropertyAction PropertyName="Background"

                                        Value="LightGray "/>

              </Core:EventTriggerBehavior>

        </Interactivity:Interaction.Behaviors>

运行以上代码,你就能看到TextBox被选中后的高亮效果了。

 

5.     ControlStoryboardAction

ControlStoryboardAction是用来控制控件的时间线动画的,它的作用类似于WPFBeginStoryBoard/StopStoryBoard/PauseStoryBoard/ResumeStoryBoard的集合。我们知道在WinRT中只支持BeginStoryBoard,支持的Event Trigger也只有FrameworkElement.Loaded一种。有了ControlStoryboardAction,我们就可以在XAML中轻松自如的控制动画了。

为了演示如何控制动画,这里我首先添加了一个圆形的Ellipse,并为这个Ellipse创建了一个Storyboard,这个Storyboard的作用是控制Ellipse上下左右移动,代码如下:

<Ellipse x:Name="Ball" Width="80"  Height="80" Fill="Red">

            <Ellipse.RenderTransform>

                <TranslateTransform x:Name="myTranslate0" />

            </Ellipse.RenderTransform>

</Ellipse>

<Storyboard x:Name="myStoryboard" RepeatBehavior="Forever"

                AutoReverse="True">

         <DoubleAnimation From="0" To="400" Duration="0:0:4"

                     AutoReverse="True" RepeatBehavior="Forever"

             Storyboard.TargetProperty="X"

             Storyboard.TargetName="myTranslate"/>

         <DoubleAnimation From="-100" To="100" Duration="0:0:1"

                     AutoReverse="True" RepeatBehavior="Forever"

             Storyboard.TargetProperty="Y"

             Storyboard.TargetName="myTranslate"/>

 </Storyboard>

然后再添加一个按钮,在Blend中为这个按钮添加ControlStoryboardAction,这里选择ControlStoryboardOptionTogglePlayPause,你也可以选择其他选项,比如PlayStopResume等等,设定Storyboard属性为你需要控制的Storyboard:

 

Blend会帮助我们生成如下代码:

    <Interactivity:Interaction.Behaviors>

        <Core:EventTriggerBehavior EventName="Click">

            <Media:ControlStoryboardAction

                Storyboard="{StaticResource myStoryboard}"

                ControlStoryboardOption="TogglePlayPause"/>

        </Core:EventTriggerBehavior>

</Interactivity:Interaction.Behaviors>

这样,仅仅简单的一步,我们就可以用按钮来播放和暂停动画了。

 

6.     GoToStateAction

GoToStateAction可以让控件从一个VisualState状态转变至另一个VisualState状态。通常控件的状态变迁需要通过code behind代码实现,这个Action帮我们省去了这一部分的代码。由于VisualState被广泛用于设置控件的动画效果,所以GoToStateAction的主要作用也是如此。我们可以创建一组VisualState,为每个VisualState设计一组动画,然后通过GoToStateActionUI上的操作提供动画效果。

这里我在Blend中为Ellipse创建了两个VisualState: PressedUnPressed,为两个状态提供了相反的颜色变化效果,代码如下:

<VisualStateManager.VisualStateGroups>

    <VisualStateGroup x:Name="VisualStateGroup">

        <VisualState x:Name="Pressed">

            <Storyboard>

                <ColorAnimation Duration="0:0:0.2" To="Teal"

            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"

            Storyboard.TargetName="ellipse" d:IsOptimized="True"/>

            </Storyboard>

        </VisualState>

        <VisualState x:Name="UnPressed">

            <Storyboard>

                <ColorAnimation Duration="0:0:0.2" To="#FFF4F4F5"

            Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"

            Storyboard.TargetName="ellipse" d:IsOptimized="True"/>

            </Storyboard>

        </VisualState>

    </VisualStateGroup>

</VisualStateManager.VisualStateGroups>

然后为Ellipse添加了两个GoToStateAction,第一个ActionEllipse被点击按下的时候触发,进入Pressed状态,第二个ActionEllipse被点击释放的时候触发,进入UnPressed的状态:

 

Blend会帮我们自动生成以下代码:

    <Interactivity:Interaction.Behaviors>

        <Core:EventTriggerBehavior EventName="PointerPressed">

            <Core:GoToStateAction StateName="Pressed"/>

        </Core:EventTriggerBehavior>

        <Core:EventTriggerBehavior EventName="PointerReleased">

            <Core:GoToStateAction StateName="UnPressed"/>

        </Core:EventTriggerBehavior>

</Interactivity:Interaction.Behaviors>

这样一个点击变色的动画效果就完成了。虽然这样的效果通过ControlStoryboardAction也很容易实现,但是相比直接控制StoryBoard来说, VisualState要更直观,也更容易维护,因此使用也更为广泛。

7.     PlaySoundAction

PlaySoundAction顾名思义就是播放声音的Action,它可以帮助你在触发某个事件的时候播放你想要的音频文件。

以实现一个闹钟为例,首先我们需要在页面加载的时候启动时钟,这里我通过以下代码定义了一个CurrentTime属性,并用DispatcherTimer来控制这个属性的更新。Timer的启动放在了StartTimer方法中,并在XAML中通过CallMethodAction来完成对该方法的调用:

        private DateTime _currentTime;

        public DateTime CurrentTime

        {

            get { return _currentTime; }

            set

            {

                _currentTime = value;

                RaisePropertyChanged("CurrentTime");

            }

        }

         public void StartTimer()

        {

              var timer=new DispatcherTimer();

              timer.Tick+=Tick;

              timer.Interval=new TimeSpan(0,0,1);

              timer.Start();            

        }

         private void Tick(object sender, object e)

        {

            CurrentTime = DateTime.Now;

         }

然后通过DataTriggerBehavior绑定后台属性CurrentTime,设置比较条件和比较值;最后,再加入PlaySoundAction,设置其音频文件和音量,注意这里需要保证音频文件已经加入到该项目中:

 

Blend生成的代码如图所示

    <Interactivity:Interaction.Behaviors>

        <Core:EventTriggerBehavior>

            <Core:CallMethodAction MethodName="StartTimer"

                TargetObject="{Binding Mode=OneWay}"/>

        </Core:EventTriggerBehavior>

        <Core:DataTriggerBehavior Binding="{Binding CurrentTime.Second}"

                ComparisonCondition="GreaterThanOrEqual" Value="0">

            <Media:PlaySoundAction Source="Assets/beep.wav"/>

        </Core:DataTriggerBehavior>

</Interactivity:Interaction.Behaviors>

这样一个有嘀嘀功能的闹钟就实现了,当你打开相应的页面是,会不断的发出一秒一次的嘀嘀声。

 

以上就是对Blend for Visual Studio 2013BehaviorAction功能的一个完整介绍,附件中是我的示例代码。不仅如此,您还可以实现自定义的BehaviorAction,将它们添加到Blend中去。有了这两个强大的助手,相信对我们今后的开发一定能够带来更大的便利。

BehaviorDemo.zip

Comments (2)

  1. Yanzhi说道:

    Great post to introduce behaviors in Win 8.1 API!

  2. Jason说道:

    Great, It is helpful to me!

Skip to main content