October 2, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

XAML Custom Controls and Animation

  • March 8, 2004
  • By Jason Nadal
  • Send Email »
  • More Articles »

Microsoft's upcoming declarative language XAML, primarily for user interfaces and documents, will provide a simple way to separate functionality from design. This article will show how to create custom controls with the new graphical subsystem codenamed "Avalon," as well as show animation and scaling for Windows Code Named "Longhorn" applications. Samples provided will show how to use XAML to display well-formatted elements through Windows Forms applications. Those referring to XAML typically pronounce it as "ZAM-mul," to rhyme with camel.

XAML 101

XAML is actually a codename for the markup language used to declare elements of a user interface, constructed from "eXtensible Avalon Markup Language." From simple controls like SimpleText and Button to more complex and rich data binding and data context controls, XAML allows the designer to separate themselves from the programmer. If they are the same person, there is also the added benefit of separation of code and UI. XAML is not the only new addition to the Windows framework, but it is the most obvious because it is the first thing a user will see.

Effortless Partial Classes

To begin, a XAML document is a partial class, a new feature of the 2.0 (1.2) framework. The XAML file is parsed by the markup compiler as a generated code file, with a ".g.cs" or ".g.vb" (the "g" is for "generated") extension. This is merged with the codebehind portion of the code, with a declaration like:

public partial class MyClass

The great thing about this is all of the control declarations in the XAML file are automatically generated in the ".g" file, and don't clutter your codebehind.

Getting Started with XAML and Styles

So to get started, the following install order is needed to get a project working in Visual Studio codenamed Whidbey: first Longhorn, then Whidbey, then the Longhorn SDK. If the last two are tranposed, then the project templates may not show up. When you create a new project, you will have a list of new projects, including one for Avalon Applications.

There's a main XAML file generated in this project, "Window1.xaml", which is where the UI for the examples in this article will reside. This consists primarily of a <Window> tag, with all elements inside. Within this window, one of a number of different panel objects can be used. Canvas controls allow the designer to place things absolutely, through relative pixels from the Left, Right, Top, or Bottom properties of Canvas. For example, here is a Canvas 15 pixels away from the top and left.

<Canvas Canvas.Top="15px" Canvas.Left="15px">
   <!-- insert user elements here -->
</Canvas>

Alternatively, one can use FlowPanel controls in order to place elements with relative positioning, similar to the standard flow control of Web page design. A third option is to use a DockPanel, which is very similar to the FlowPanel, but anchored to a location. These panels can be used by themselves or in any combination to provide the requisite user experience.

A great new addition for controls with the advent of XAML is that of Styles, which allow a set of properties to be added to all controls which use that particular style. A developer can create a hierarchy of styles with successive derivations of a base style, or have a style containing elements of multiple styles as shown in the attached example. Shown below is a sample style:

<Style def:Name="ButtonText">
   <ContentPresenter />
   <Style.VisualTree>
      <SimpleText FontFamily="arial" Foreground="red"
                  FontWeight="bold" FontSize="20pt">
         <SimpleText.Text>
            <Bind/>
         </SimpleText.Text>
      </SimpleText>
   </Style.VisualTree>
</Style>

What this style declaration is doing is building off of the base ContentPresenter item. Styles have a VisualTree which is the actual representation of the style, in this case a SimpleText control.

Data Binding

Inside the text, it's just a matter of binding the text to the item. In another, you can declare a Canvas with this style, and bind the content to your set data in the content below. First, here is a look at how to inherit from the style and bind the main XAML control's data to the control in the style.

<Style def:Name="ButtonWithText">
   <Button ClipToBounds="False" Margin="20" Cursor="Hand" />
<Style.VisualTree>
   <Canvas def:StyleID="GelTextWithBlur">
   <ContentPresenter ContentControl.Content="*Alias(Target = Content)"
                     ContentControl.ContentStyle="{ButtonText}"
                     Canvas.Top="10" Canvas.Left="15" />
   </Canvas>
</Style.VisualTree>

What this is doing is creating a new Style which is built off of a Button object. Again, the display is altered through the use of Style.VisualTree, but this time, the Button is represented by a Canvas. This permits the developer to have more of a clean slate to work from then a SimpleText object. The ContentPresenter allows data to flow through the inheritance tree through what is known as "property aliasing." This is done by taking a style, in this case ButtonTextStyle, and binding it to the alias. In the ButtonText style above, the Bind statement is on the Text property of the SimpleText element. In the ButtonWithText style, the content of the final element below is set to bind to the parent style. Think of this as the UI element handing data to a messenger to a packager to wrap it correctly. Then the final package gets handed back to the UI element through the messenger.

<Button ID="myFormattedButton"
        Style="{ButtonWithText}">I am red text</Button>

Finally, in the main part of the XAML document, the actual Button is declared, named, given a style to use, and data to bind. That text which is within the tags is known to the property aliasing syntax as the Content. In reality, any property of the element can be aliased. This is particularly useful when you are binding from a data object, as you can reference named elements of that data object in the binding statement. The curly braces surrounding the name of the style are new with XAML as well. In order for the control to have the object set to the style property, rather than the literal text "Button WithText", the "{" and "}" symbols are needed. They can be read as "the object represented by the name of."

XAML Event Handling

Events enacted upon a XAML style element are invoked via a VisualTrigger. This is a change from how things are handled in the code-behind (.NET 1.X methods of handling events are fine in this case). Below is a sample syntax for how an event can be handled on the previous example.

<Style def:Name="ButtonWithText">
   <Button ClipToBounds="False" Margin="20" Cursor="Hand" />
<Style.VisualTree>
   <Canvas def:StyleID="GelTextWithBlur">
      <Rectangle def:StyleID="BlackRectangle" RadiusX="15"
                 RadiusY="15" DockPanel.Dock="Fill" Fill="black"
                 Opacity="0.75"  Stroke="transparent"
                 StrokeThickness="2" />

      <ContentPresenter ContentControl.Content="*Alias(Target =
                                                       Content)"
                        ContentControl.ContentStyle="{ButtonText}"
                        Canvas.Top="10" Canvas.Left="15" />
   </Canvas>
</Style.VisualTree>
<Style.VisualTriggers>
   <PropertyTrigger Property="IsMouseOver" value="true">
      <Set Target="BlackRectangle" PropertyPath="Opacity"
           value="1" />
   </PropertyTrigger>
</Style.VisualTriggers>
</Style>

Quite a lot is going on in this sample. We have added a black rectangle for below the red text, with an opacity of 0.75, or 75%. The bound text is still there, on top of the rectangle, because items are declared from bottom to top, and then positioned via the Canvas.Top and Canvas.Left properties.

The most noticable difference is the addition of the VisualTriggers declaration, with a trigger for the IsMouseOver value. When the IsMouseOver value becomes true, it fires off the "Set" orders that are contained within. The PropertyTrigger may contain several set statements, and the VisualTriggers may contain multiple PropertyTriggers. In this sample, when the user moves the mouse cursor over the button with a "ButtonWithText" style, the actions in the Set are carried out. In this case, the black rectangle becomes fully opaque. One possible use, as in the attached sample, is to change the physical location on the canvas to make it look like the button element was actually pressed down.

In addition to actually handling animations based on events, you can still handle actual events in the code-behind. This is done through the use of properties of the element. For example, the Button object has a property "Click" which defines the name of its event handler.

<Button Style="{ButtonWithText}" ID="MyButton"
        Click="MyButton_Click"
   Height="50" Width="100">Click me!</Button>

Getting Started with Animation

Animation in XAML documents is accomplished through the use of AnimationCollection objects, depending on the type of the property of the element that is being animated. For example, the Height property of a Rectangle object is of a type Length.

<Rectangle Fill="Black" Height="100px" Width="100px"
           Canvas.Bottom="5px" Canvas.Right="5px">
   <Rectangle.Height>
      <LengthAnimationCollection>
         <LengthAnimation From="100" To="50" Duration="3"
                          RepeatDuration="Indefinite" />
      </LengthAnimationCollection>
   </Rectangle.Height>
</Rectangle>

The appropriate collection is declared inside the element that is being animated. The collection contains a LenghAnimation that changes the height from a 100 pixel square to a 100 pixel wide by 50 pixel high rectangle over a timespan of 3 seconds, repeating indefinitely. Keep in mind this can also be done in response to a Trigger, or added via the code-behind in an event handler.

A Look Ahead

The next article will further explore both databinding techniques using the IDataTransformer class and more on animation in XAML, as well as how to draw more complex shapes using curves and paths.

One Last Note

All of the provided code compiles under PDC builds of Longhorn (4051), Whidbey (m2.030828-1205), .NET Framework (1.2.30703), and the Longhorn SDK. The sample application provided, TranslateIt! shows what can be done by implementing the techniques mentioned in the article.






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel