Ryan Dawson on Longhorn

The software we think, but do not write

System Stats Using Avalon Animation Lingo

 

Source Code can be found here

 

Through my skim experience with Avalon/XAML I have found many things that will make the application easier to write, in the end.

1)      Declare as many elements/aspects of your program as you can in XAML.  Declarative languages are intrinsically easy, so use them to your potential.

2)      Objects that are harder to manipulate, or need special processing should definitely go in your C#/VB.NET code.  That is not to say that you should not declare your object in XAML and set any necessary properties.  It is only to say that it will be faster to manipulate your object in code than try and think how it can or should work in XAML.  For example, a TransformDecorator Property that contains an animation is easy to declare in XAML, which takes care of one aspect, but for most applications, it needs to be controlled within the code.

3)      Prior to Avalon/XAML it was the case that designers would create a UI look on paper or in Photoshop and then hand you the ideas.  But, these same designers were able to create full functioning Flash pages.  Why then should they not be able to do the same in XAML…Well they can, and they should.  The point of the story: more than ever does a developer need to isolate the graphics subsystem of the application from everything else.  Let the designer have full access to your XAML files, but have a developer intertwine the details into the C#/VB.NET file.

 

 

Let’s start with the structure:

 

  • If you plan on implementing any custom controls, you are going to need a place to store them.  The parent window has a property specifically designated for this type of storage, and you can access it using the Resources property.  For a window, it will be Window.Resources, or a document will be Document.Resources.  Inside this element you can declare an infinite number of custom styles, which are basically the term for inheritable graphics controls for the Avalon world.  Let’s look at the basics:

 

<Window.Resources>

            <Style def:Name="myStyle" />

</Window.Resources>

 

Now, with some real meat:

 

      <Window.Resources>

            <Style def:Name="myStyle" />

                  <Button/>

                  <Style.VisualTree>

<Canvas Cursor="Hand" Width="100%" Height="100%">

<Rectangle RadiusX="6" RadiusY="6" Width="100%" Height="100%" DockPanel.Dock="Fill" Fill="*Alias(Target=Background)"  Stroke="VerticalGradient transparent #cc000000" StrokeThickness="1" />

<SimpleText TextWrap="Wrap" Width="100%" Height="100%" Text="*Alias(Target=Content)" FontFamily="arial" Foreground="White" FontWeight="bold" FontSize="13pt" />

</Canvas>

</Style.VisualTree>

</Style>

</Window.Resources>

 

There are a couple of things that are going on here…

1)      We have declared a top-level control, or basically the type of control that we are eventually going to reference it from.  In this case, we are creating a Button.

2)      Within the VisualTree property is all of the graphics code, per se.  This is where we create all of the shapes or controls that we want to compose our Button.  The sky is the limit.

3)      An interesting line is this: Text="*Alias(Target=Content)"

This essentially is a databind once you get over the strange syntax.  A SimpleText control has a property named Text, but a Button has the equivalent property named Content.  So the way we get them to represent the same thing is to use the *Alias(..) command, which you guessed it, is an alias, or a redirection to what we want.  All you need to do is point to the field that will be modified in the Button, such as Content, with the Target keyword.

 

 

  • Next, for work-flow sake, I have declared a panel container, which in this case is a Canvas.  Intermingled in my code you will find various containers, but you have access to much more, considering DockPanel, FlowPanel, Canvas, Border…  The point is that there are a lot, and the only key is using the right one in the right situation.  One thing that I have noticed, in order to get a control to behave in the correct manner it is essential that you have a container.  That is the main reason that you will see code like this:

 

<Canvas Width="100%" Height="100%">

<TransformDecorator Canvas.Left="0" Canvas.Top="0" Width="110" Height="70" ID="myID">

                  <Button Width="100%" Height="100%" Content="Hello" />

            </TransformDecorator>

</Canvas>

 

As is visible, I have 2 containers.  The Canvas is my work-flow container, and the TransformDecorator is my animation/transform container.  Both of these just to get 1 Button.  Be aware, as I mentioned earlier, you will learn which containers are needed in which situations.  For example, to my knowledge, in order to get arbitrary positioning you should use the Canvas and set the Width=”100%” and the Height=”100%”.  This will give you relative coordinates for positioning within another parent container, but you also have the ability to specify negative values and easily leave the parent container.  Given that myObject is a DependencyObject, here is how you would position a control.

 

      Canvas.SetLeft(myObject, new Length(50));

Canvas.SetTop(myObject, new Length(50));

 

DockPanel and FlowPanel seem to be very useful containers on the face, but I have not been able to get them to work magic for me.  Let me explain, I am used to having absolute control over coordinates, and the thought and reality of a control picking that for me is foreign.  Plus, they do not serve my intended effect.  I will experiment more and provide updates as to my findings.

 

 

 

System Stats

 

What is it?

It is a simple application for showing the beauty of Avalon, and the functionality of a real program.  It uses WMI to provide system statistics, such as the hardware configuration, running processes, and the current CPU usage.

 

NOTE: The graphic that is used for the background can add significant memory usage.  If you are pressed for resources, you should comment out the Image Declaration on or about line 49.

 

 

How the animations work:

 

  • Off the top of my head, animations at their most primitive states are composed of either DoubleAnimation or LengthAnimation.  Subsequently, multiple animations can be declared for one property by enclosing them in DoubelAnimationCollection or LenghtAnimationCollection, with respect.  A LengthAnimation refers to spatial expansion or compression (for lack of a better phrase, and even less appealing, the definition in the SDK: Used to animate properties that accept a Length value).  Basically, it is used for animating the Width and Height fields.

 

Now, on the other hand there is the DoubleAnimation object, which encompasses everything else.

 

Except for the value types that are to be passed and create the major distinction between the two, they are literally the same.  The most interesting fields include:

    • From
    • To
    • Begin or the method BeginIn(..)
    • Duration

 

They are self-explaining, so I won’t go into their representation.

 

  • In my mind, there are two types of animations:

 

1)      These are the animations that are inside fields such as Opacity, Width and Height.  The extent of their effect is setting fields within the object along a time resolution.

 

From XAML, they are declared as such:

 

<TransformDecorator.Opacity>

<DoubleAnimationCollection>

<DoubleAnimation From="1" To="0" Begin="Indefinite" />

<DoubleAnimation From="0" To="1" Begin="0" Duration="5" />

</DoubleAnimationCollection>

</TransformDecorator.Opacity>

 

From code, you can access the animations from an indexed array.  In this example, one animation is started immediately without code intervention, and the other is started from code explicitly.  Here is the start of the latter supposing it is in relation to a Rectangle:

 

DoubleAnimation da = (DoubleAnimation)(myRectangle.GetAnimations (Rectangle.OpacityProperty))[0];

da.Duration = new Time(1000);

da.BeginIn(0);

 

 

2)      These are the animations which are either more complex than simply setting one field, or apply a transformation on the graphics matrix that represents the object on the display buffer.  Such animations are set through the TransformDecorator.Transform property and can take one of two things: a TransformCollection, or an individual transform such as RotateTransform.  These can be declared in XAML or C#, but use cases will dictate, obviously.  From my experience, 80% of the time will be in C# because you are probably going to use dynamic values.  This actually applies more than the first type of animation, although they both will be dependent on usage.

 

The declaration that exists in XAML:

 

                        <TransformDecorator ID="myTransformDecorator">

                  <ListBox Width="100%" Height="100%" />

</TransformDecorator>

 

                        Adding the Transform in C#:

 

DoubleAnimation da = new DoubleAnimation(0, 360, new Time(1000));

da.Begin = Time.CurrentGlobalTime;

 

RotateTransform rt = new RotateTransform();

rt.AngleAnimations = da;

rt.Center = new Point(10, 10);

 

TransformCollection tc = new TransformCollection();

tc.Add(rt);

 

myTransformDecorator.Transform = tc;

 

In this example, we create a primitive animation type –DoubleAnimation, then we add it to a RotateTransform, and then to a TransformCollection, lastly assigning it to the object we created in XAML.  This will spin the ListBox in 1 full circle over the duration of a second.  Creating the TransformCollection was not necessary, but I think it provides a good programming practice.

 

 

Now that we have animation 101 out of the way, let’s take a look at some of the animations within the sample.

 

The transition of a title button is quite simple.  The complete transition is composed of a RotateTransform and a TranslateTrasnform.

 

 

Each button looks like below in XAML:

 

<Canvas Width="100%" Height="100%">

<TransformDecorator Canvas.Left="200" Canvas.Top="30" Width="130" Height="36" ID="informationTransform">

<Button Width="100%" Height="100%" Style="{TitleButton}" Background="VerticalGradient Blue Cyan" Click="information_Click" Content="Information" />

</TransformDecorator>

</Canvas>

 

The transformation meat looks strikingly similar to the example above, except with the addition of a TranslateTransform (Moving the position of an object).  Once you assign the TransformCollection to the TransformDecorator, the magic happens on its own.

 

 

The next transition is the content area for one of the three buttons in the image above.  It is composed of 4 animations:

1)      Opacity

2)      Height

3)      Width

4)      TranslateTransform

 

The first 3 items are declared and invoked as in animation type # 1, and the 4th item is declared and invoked as in animation type # 2.

 

 

 

 

There are 2 good design choices when it comes to Avalon:

1)      Think of Opacity for an object as you would the Background, which basically means stay away from full Opacity for every object.  In the demo application, the emphasis was placed on the background, because it is very appealing, so in order to reach that goal, I lowered the opacity on everything else.

2)      Use Opacity animations everywhere, it is easy on the eye.

 

PostTypeIcon
10,924 Views