(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows 8.1: Behaviors SDK - Part #1

(2 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
0 comments   /   posted on Sep 30, 2013
Categories:   Windows 8

Tweet

Together with Silverlight 3.0 RTM, hidden inside the release of Blend 3.0 in later 2009, Microsoft released an interesting feature that in the projects had to support Sketckflow users. These small pieces of codes, at the first sight appeared as a "draggable" logic, made to increase the designers productivity by adding specific behaviors to objects, directly from the design surface. The "behaviors", this is the name given to these new elements, were part of the Blend 3.0 SDK as a separate download and this contributed to them success. Infact, the behaviors have gained an important part into the scene, just because they are not only part of Blend, but also and expecially because they was easy to be created by developers that started to take advantage of them immediately.

After the first release, behavior continued to live not only in Silverlight, but also in Windows Phone and they have been always appreciated for the capability of abstracting an user interface behavior, including it into a small reusable class. This is really important, not only to grant designers to add these behaviors without the need of knowing how to write code, but also, because they are able to strip away some UI logic that previously required programming the codebehind of pages and controls. So they are an important piece into the daily fight to the mixing of business logic and interface logic.

Windows 8.1 and the Behaviors SDK

This said, it is very understandable the disappointment that comes when people discovered that behaviors have been pulled out of XAML for Windows 8. This disappointment was really justified because behaviors have been a filler for missing aspects in Silverlight and, when they have been removed, nothing have been put in place to fill these same gaps. One of the most common usage of behaviors was the invocation of ICommand instances in ViewModel, to attach a command to almost every available event in the View. With their departure this problem returned to the writing into codebehind.

Luckily, the team behind XAML for Windows 8 listened carefully to people and in the latest release of Windows 8.1 they added the support for a subset of Behaviors, that is still reduced if compared with Silverlight and Windows Phone but they suffice to resolve the most of the problems they originally covered.

imageBehavior SDK, to confirm a tradition, is not directly part of the core libraries, but it comes as a separate set of assemblies, shipped together with Visual Studio 2013. To add behaviors to your application, the first stop is the "references dialog", where you can include the "Behavior SDK (XAML)" item into the "Windows -> Extensions" tab.

After having added this reference, the behaviors become available and the first place where they appear is its natural location: Blend. To experiment with behaviors, just create a simple blank application and the open it in Blend for Visual Studio 2013. Behaviors appears (again) in the "assets" tab under the omonimous section. There is a small set of predefined behaviors in place and we can directly start to familiarize with them.

imageBefore to start, it is important to understand some subtle differences, you may have noticed in the figure on the left side.

There are two type of items in this list. The ones that have a small arrow as the icon and the ones that have a gear. The first are called Actions and the second are Behaviors. The differences come from the fact that Actions needs a behavior to run while behaviors can be directly attached to objects and they are able to attach its properties and events. Leaving on the side the "IncrementalUpdateBehavior" - it is specially tailored to add incremental loading to ItemsControl -there are two remaining behaviors: EventTriggerBehavior and DataTriggerBehavior.

An easy example: EventTriggerBehavior

As the name suggests, EventTriggerBehavior is able to attach a specific event of the object which it is attached to, and when the event is raised it executes one or more actions. Let me do an example using a simple Action called ChangePropertyAction. Drag inside the canvas of the page two rectangles of 100x100 of size. Then, dragging it from the Assets tab, add an instance of this action to one of the rectangles.

image

The resulting structure in the Project is reported on the right side. Blend automatically added a "EventTriggerBehavior" and then it added the ChangePropertyAction to it. Now we have to configure the behavior. After having selected it in the structure, in the properties tab we are able to choose an event from a dropdown list. These are the events published by the "Rectangle". For this example simply choose the PointerPressed event. The "Source object" property should be set to the rectangle where the behavior has been attached.

Now its time to select the inner action. This action has a "Target object" property that is initially set to the same object to which the behavior has been applied. Dragging the circle on the right side over the target object we have to select the secondary rectangle as the target. This way the dropdown list will show the name of all the Dependency Properties of the target rectangle. Selecting a property we are presented with a panel that let us select the desired value. As an example if you choose the "Background" property the designer presents the color selector. Select your preferred color and then run the example.

Please take note that the codebehind of the application is still empty a no other code have been added. Running the exaample we should see the secondary rectangle changing its fill color when the primary is clicked. As an experiment you can add some other ChangePropertyAction instances and connect it with other properties of the same target or of others.

Introducing the DataTriggerBehavior

DataTriggerBehavior instead is able to detect the change of a property, basing it works on the INotifyPropertyChanged interface. Using this trigger you can monitor a property and have the actions called when a specific value has been set. To make an example we should try to add a ViewModel to the page. This ViewModel should implment INotifyPropertyChanged and it have a "Now" property that contains the most recent time. Here is the code:

   1: public class MainPageViewModel : INotifyPropertyChanged
   2: {
   3:     public event PropertyChangedEventHandler PropertyChanged;
   4:  
   5:     public CoreDispatcher Dispatcher { get; set; }
   6:     public Timer Timer { get; set; }
   7:  
   8:     private DateTime now;
   9:     public DateTime Now 
  10:     {
  11:         get { return now; }
  12:         set
  13:         {
  14:             if (now != value)
  15:             {
  16:                 now = value;
  17:                 this.RaisePropertyChanged("Now");
  18:             }
  19:         }
  20:     }
  21:  
  22:     private void RaisePropertyChanged(string propertyName)
  23:     {
  24:         PropertyChangedEventHandler handler = PropertyChanged;
  25:  
  26:         if (handler != null)
  27:             handler(this, new PropertyChangedEventArgs(propertyName));
  28:     }
  29:  
  30:     public MainPageViewModel()
  31:     {
  32:         this.Dispatcher = Window.Current.Dispatcher;
  33:         this.Timer = new Timer(Tick, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
  34:     }
  35:  
  36:     private async void Tick(object state)
  37:     {
  38:         await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.Now = DateTime.Now);
  39:     }
  40: }

The example ViewModel also have a Timer instance that automatically changes the value of the "Now" property every half a second. This result to a notification to the View that we may connect with a DataTriggerBehavior. Let see the XAML code; now it is time to go back to Visual Studio 2013 to have a look to the code, but remember that you can accomplish the same using only the Blend's designer.

   1: <Page
   2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:     xmlns:local="using:XPG.WinRTBehaviors"
   5:     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   6:     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   7:     xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
   8:     xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
   9:     x:Class="XPG.WinRTBehaviors.MainPage"
  10:     x:Name="page" mc:Ignorable="d">
  11:     
  12:     <Page.DataContext>
  13:         <local:MainPageViewModel />
  14:     </Page.DataContext>
  15:  
  16:     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  17:         <Interactivity:Interaction.Behaviors>
  18:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="0">
  19:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r9}" PropertyName="Visibility">
  20:                     <Core:ChangePropertyAction.Value>
  21:                         <Visibility>Visible</Visibility>
  22:                     </Core:ChangePropertyAction.Value>
  23:                 </Core:ChangePropertyAction>
  24:             </Core:DataTriggerBehavior>
  25:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="6">
  26:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r8}" PropertyName="Visibility">
  27:                     <Core:ChangePropertyAction.Value>
  28:                         <Visibility>Visible</Visibility>
  29:                     </Core:ChangePropertyAction.Value>
  30:                 </Core:ChangePropertyAction>
  31:             </Core:DataTriggerBehavior>
  32:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="12">
  33:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r7}" PropertyName="Visibility">
  34:                     <Core:ChangePropertyAction.Value>
  35:                         <Visibility>Visible</Visibility>
  36:                     </Core:ChangePropertyAction.Value>
  37:                 </Core:ChangePropertyAction>
  38:             </Core:DataTriggerBehavior>
  39:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="18">
  40:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r6}" PropertyName="Visibility">
  41:                     <Core:ChangePropertyAction.Value>
  42:                         <Visibility>Visible</Visibility>
  43:                     </Core:ChangePropertyAction.Value>
  44:                 </Core:ChangePropertyAction>
  45:             </Core:DataTriggerBehavior>
  46:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="24">
  47:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r5}" PropertyName="Visibility">
  48:                     <Core:ChangePropertyAction.Value>
  49:                         <Visibility>Visible</Visibility>
  50:                     </Core:ChangePropertyAction.Value>
  51:                 </Core:ChangePropertyAction>
  52:             </Core:DataTriggerBehavior>
  53:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="30">
  54:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r4}" PropertyName="Visibility">
  55:                     <Core:ChangePropertyAction.Value>
  56:                         <Visibility>Visible</Visibility>
  57:                     </Core:ChangePropertyAction.Value>
  58:                 </Core:ChangePropertyAction>
  59:             </Core:DataTriggerBehavior>
  60:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="36">
  61:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r3}" PropertyName="Visibility">
  62:                     <Core:ChangePropertyAction.Value>
  63:                         <Visibility>Visible</Visibility>
  64:                     </Core:ChangePropertyAction.Value>
  65:                 </Core:ChangePropertyAction>
  66:             </Core:DataTriggerBehavior>
  67:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThan" Value="42">
  68:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r2}" PropertyName="Visibility">
  69:                     <Core:ChangePropertyAction.Value>
  70:                         <Visibility>Visible</Visibility>
  71:                     </Core:ChangePropertyAction.Value>
  72:                 </Core:ChangePropertyAction>
  73:             </Core:DataTriggerBehavior>
  74:             <Core:DataTriggerBehavior Binding="{Binding Now.Second, Mode=OneWay}" ComparisonCondition="GreaterThan" Value="48">
  75:                 <Core:ChangePropertyAction TargetObject="{Binding ElementName=r1}" PropertyName="Visibility">
  76:                     <Core:ChangePropertyAction.Value>
  77:                         <Visibility>Visible</Visibility>
  78:                     </Core:ChangePropertyAction.Value>
  79:                 </Core:ChangePropertyAction>
  80:             </Core:DataTriggerBehavior>
  81:         </Interactivity:Interaction.Behaviors>  
  82:         <Grid.ColumnDefinitions>
  83:             <ColumnDefinition/>
  84:             <ColumnDefinition/>
  85:         </Grid.ColumnDefinitions>
  86:         <StackPanel Grid.Column="1" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="True">
  87:             <Rectangle x:Name="r1" Fill="#FF007D91" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  88:             <Rectangle x:Name="r2" Fill="Blue" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  89:             <Rectangle x:Name="r3" Fill="#FF6800FF" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  90:             <Rectangle x:Name="r4" Fill="#FFE800FF" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  91:             <Rectangle x:Name="r5" Fill="Red" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  92:             <Rectangle x:Name="r6" Fill="#FFFF4600" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  93:             <Rectangle x:Name="r7" Fill="#FFFF8B00" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  94:             <Rectangle x:Name="r8" Fill="#FFF3FF00" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  95:             <Rectangle x:Name="r9" Fill="White" HorizontalAlignment="Center" Stroke="Black" VerticalAlignment="Center" Width="25" Height="25" RenderTransformOrigin="0.5,0.5" Margin="0,5,0,0" Visibility="Collapsed"/>
  96:         </StackPanel>
  97:         <TextBlock TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Now.Second, Mode=OneWay}" FontSize="96" FontFamily="Consolas"/>
  98:     </Grid>
  99: </Page>

In this XAML we have 9 small rects stacked and each rectangle is connected with a DataTriggerBehavior that checks the "Second" value of the DateTime in "Now". Each trigger is set to launch a ChangePropertyAction at different values (0, 6, 12, 18, 24, etc...) with a condition od "GreateThanOrEquals". The property to change is "Visibility". When you run the example you have the Second property continuously increasing and the triggers are set at increasing values. This makes the rects appear one every 6 seconds in a sequence. The strange thing is that once the trigger is set, it is not reset when the value fall back to less then the threshold. This is because the DataTriggerBehavior changes always the property from Collapsed to Visible. It does not have any knowledge of the inverse operation. To make the opposite operation you have to attach another series of triggers with the LessThan clause switching a ChangePropertyAction that set again the Collapsed value.

About Other Actions

With this example I've only scratched the surface. Side by side with ChangePropertyAction there are a number of other actions ready out of the box. Here is a brief list:

  • CallMethodAction: the action will call a method on a target object. If used with EventTriggerBehavior it is like attaching an event handler in code.
  • ControlStoryBoardAction: it is able to control a storyboard, invoking Play, Stop and other actions.
  • GoToStateAction: It is useful to change a state of the VisualStateManager.
  • InvokeCommandAction: This action is useful for MVVM programming since it is capable of invoking a command on the ViewModel. USed with the EventTriggerBehavior lets you attach commands to each event of every control.
  • NavigateToPage: This is useful to trigger navigation to another page in your project
  • PlaySoundAction: Intuitively this is for plating a sound when a condition is hit.

A combination of one or more types from this list can generate complex logic, without impacting the codebehind or the viewmodel. This often becomes too much complex, requiring long design sessions, but be aware that you can also write your own behaviors and triggers to include logic that are not available with predefined actions or to reduce the complexity. I'll be back on the building of custom behavior and action in the next article.


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series