Welcome to RioterDeckers' HeadQuarter Sign in | Join | Help

3-D visual effect in 2-D (part 2): the fresh elliptic control

Here is the second part of the post about the control displaying items around the path of an ellipse in order to obtain a visual effect close to 3-D.
As promises this post describes a customizable and templatable control with code used for its design on the one hand and its consuming on the other hand.
If you didn't read the preceding post, I advise you to preliminary study it in order to understand each concept. Go and read here.
The figure below gives you a preview of the control.

Figure 1: the elliptic control preview

It will be easily consumed using the following Xaml declarations:

<c:EllipticItemSelector x:Name="_selector" ItemWidth="90" ItemHeight="90" EllipticWidth="225" EllipticHeight="40" RotationSpeed="0:0:4" Angle="-25" Canvas.Top="300" Canvas.Left="225">
 
<
c:EllipticItem x:Name="_avalonboyItem" Text="AvalonBoy" Style="{StaticResource AvalonBoy_EllipticItem_Style}" Selected="_OnSelected" Unselected="_OnUnselected" />
 
<
c:EllipticItem x:Name="_chazItem" Text="Chaz" Style="{StaticResource Chaz_EllipticItem_Style}" Selected="_OnSelected" Unselected="_OnUnselected" />
 
<
c:EllipticItem x:Name="_raskalItem" Text="Raskal" Style="{StaticResource Raskal_EllipticItem_Style}" Selected="_OnSelected" Unselected="_OnUnselected" />
 
<
c:EllipticItem x:Name="_thorItem" Text="Thor" Style="{StaticResource Thor_EllipticItem_Style}" Selected="_OnSelected" Unselected="_OnUnselected" />
 
<
c:EllipticItem x:Name="_wozoiItem" Text="WoZoI" Style="{StaticResource Wozoi_EllipticItem_Style}" Selected="_OnSelected" Unselected="_OnUnselected" />
</
c:EllipticItemSelector>

 

Creating templatable items

The items placed on the ellipse are defined by a class derived from the Control base class. Ths class provides essential properties used to define any visual element and its advantage is the publication of a ControlTemplate used to define the appearance and then the control rendering.

It's strongly interesting because I gonna be able to define the exact appearance that I need for each item.
Simply notice that I wanted to equip the control of a EllipticItemDefinition class, used to remember its position in the item collection, the position on the ellipse and its associated storyboard.
The EllipticItem control provides two DependencyProperty objects used to set a text and a description in Xaml, defined and registered as follow:

public static readonly DependencyProperty TextProperty;
public static readonly DependencyProperty DescriptionProperty;

static EllipticItem()
{
 
TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(EllipticItem), new FrameworkPropertyMetadata(string.Empty));
 
DescriptionProperty =
DependencyProperty.Register("Description", typeof(string), typeof(EllipticItem), new FrameworkPropertyMetadata(string.Empty));
}

public string Text
{
 
get { return (string)base.GetValue(TextProperty); }
 
set { base.SetValue(TextProperty, value); }
}
public string Description
{
 
get { return (string)base.GetValue(DescriptionProperty); }
 
set { base.SetValue(DescriptionProperty, value); }
}

Now, I add a feature to the elliptic itel to allow notifying when it is selected or unselected, with two events of RoutedEvent type: Selected and Unselected.
They are defined and registered in the static constructor:

public static readonly RoutedEvent SelectedEvent;
public static readonly RoutedEvent UnselectedEvent;

public event RoutedEventHandler Selected
{
 
add { base.AddHandler(SelectedEvent, value); }
 
remove { base.RemoveHandler(SelectedEvent, value); }
}
public event RoutedEventHandler Unselected
{
 
add { base.AddHandler(UnselectedEvent, value); }
 
remove { base.RemoveHandler(UnselectedEvent, value); }
}

static EllipticItem()
{
 
...
 
SelectedEvent = EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(EllipticItem));
 
UnselectedEvent =
EventManager.RegisterRoutedEvent("Unselected", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(EllipticItem));
 
EventManager.RegisterClassHandler(typeof(EllipticItem), SelectedEvent, new RoutedEventHandler(_OnSelectedEvent));
 
EventManager.RegisterClassHandler(typeof(EllipticItem), UnselectedEvent, new RoutedEventHandler(_OnUnselected));
}

The last two lines of code are very useful because they enable to execute methods addressed by the delegate when the corresponding event is raised.
If you're like me, you may already use and also take advantage of triggers. They are very useful to intercept events, behaviors or specific states during the life cycle of controls. So I decide to provide the IsSelected property that is easy-to-use by a trigger in order to define the adequat behavior of the item.
I simply define two coupled fields of DependencyProperty and DependencyPropertyKey types:

public static readonly DependencyProperty IsSelectedProperty;
private static readonly DependencyPropertyKey IsSelectedPropertyKey;

static EllipticItem()
{
 
...
 
IsSelectedPropertyKey = DependencyProperty.RegisterReadOnly("IsSelected", typeof(bool), typeof(EllipticItem), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(EllipticItem._OnSelected)));
 
IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty;
 
...
}

public bool IsSelected
{
 
get { return (bool)base.GetValue(IsSelectedProperty); }
}

I also decided to publish a ControlTemplate that creates the visual rendering of the control through the templated method GetTemplateContainer. I warn you that a constraint exists. You know, when you want to define a particular rendering of a textbox, you must use a scrollviewer and set a specific name. I followed the same philosophy. So you must explicitly named the root element of the template as PART_Container.

 

The elliptic control

The main container is a ContentControl because it already implements the IAddChild interface enalbing to add items in a collection. It also defines a DataTemplate but I will explain the data binding mechanism in a next post.
I define the ellipse and declare the template of the Storyboard later associated to each item.

<ContentControl
 
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
 
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
 
x:Class="RD.Controls.EllipticItemSelector"
 
Loaded="_OnLoad">

  <ContentControl.Resources>
   
<
EllipseGeometry x:Key="EllipseGeometry_Template">
     
<
EllipseGeometry.Transform>
       
<
RotateTransform Angle="0" />
     
</
EllipseGeometry.Transform>
   
</
EllipseGeometry>

    <Storyboard x:Key="SB_Template">
     
<
ParallelTimeline>
       
<
DoubleAnimationUsingPath FillBehavior="HoldEnd" RepeatBehavior="Forever"
                                 
Storyboard.TargetProperty="(Canvas.Left)" Source="X" />
       
<
DoubleAnimationUsingPath FillBehavior="HoldEnd" RepeatBehavior="Forever"
                                 
Storyboard.TargetProperty="(Canvas.Top)" Source="Y" />
     
</
ParallelTimeline>
     
<
ParallelTimeline>
       
<
DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Viewbox.Width)" RepeatBehavior="Forever" />
       
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Viewbox.Height)" RepeatBehavior="Forever" />
     
</ParallelTimeline>
   
</
Storyboard>

  </ContentControl.Resources>

  <Canvas Name="_itemsContainer" />

</ContentControl>

Notice that the ContentControl class accept only one root control. So I define a canvas to enable adding and positioning each item around the ellipse. But how is it possible to add elements to canvas and not to the ContentControl ?
I simply override the AddChild protected method.

protected override void AddChild(object value)
{
 
if (this.Content == null)
   
base.AddChild(value);
 
else
 
{
   
if (_isInitialized) return;
   
if (value is EllipticItem)
   
{
     
EllipticItem __ellipticItem = (EllipticItem)value;
     
__ellipticItem.Width =
this.ItemWidth;
     
__ellipticItem.Height =
this.ItemHeight;
     
this._AttachToVisualTree(__ellipticItem);
   
}
   
else
     
throw new InvalidOperationException("Incorrect type value");
 
}
}

private void _AttachToVisualTree(EllipticItem ellipticItem)
{
 
Viewbox __viewbox = new Viewbox();
 
__viewbox.Height = ellipticItem.Height;
 
__viewbox.Width = ellipticItem.Width;

  __viewbox.Child = ellipticItem;
 
__viewbox.MouseLeftButtonDown +=
new System.Windows.Input.MouseButtonEventHandler(__ellipticItem_MouseLeftButtonDown);

  (this.Content as Canvas).Children.Add(__viewbox);
}

Keep in mind that the canvas itself must be added using this method. The first test ensures it will be correctly connected the first time.
I add several DependencyProperty objects to set the size of the control, the angle, the size of the items and the rotation speed.
I also add two events of RoutedEvent type: Click and SelectionChanged.

 

The consuming



A few of code is now needed for the control to be functional.
The first thing to do is to exactly define the rendering of the items. Very simple using a ControlTemplate within a style tag:

<Style TargetType="{x:Type c:EllipticItem}" x:Key="Chaz_EllipticItem_Style">
 
<
Setter Property="Template">
   
<
Setter.Value>
     
<
ControlTemplate>
       
<
StackPanel Name="PART_Container">
         
<
Image Source="images/chaz.rd.release.png" Width="70" Height="70" />
         
<
Grid>
           
<
TextBlock Name="PART_Text" Text="{Binding Path=Text, RelativeSource=/TemplatedParent}" Style="{StaticResource RDText_Style}" />
         
</Grid>
         
<
Grid Name="PART_Abilities" Visibility="Collapsed" Margin="20" Width="225">
           
<
ColumnDefinition />
           
<
ColumnDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
RowDefinition />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" FontSize="14" Margin="0,0,0,10" HorizontalAlignment="Center">
             
<
TextBlock Text="{Binding ElementName=PART_Text, Path=Text}" />
             
<
TextBlock Text=" abilities" />
           
</
TextBlock>
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="1" Text="DotNet drilling" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="1" Text="16" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="2" Text="Analysis gathering" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="2" Text="14" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="3" Text="DataBlaster" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="3" Text="15" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="4" Text="Early adopter" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="4" Text="15" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="5" Text="UI Arts" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="5" Text="18" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="6" Text="Project handling" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="6" Text="15" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.Row="7" Text="Business opportunist" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="1" Grid.Row="7" Text="12" HorizontalAlignment="Right" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="8" Margin="0,10,0,0" Text="Master in technologeek" />
           
<
TextBlock Style="{StaticResource RDAbility_Style}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="9" Margin="0,20,0,0" HorizontalAlignment="Center" FontStyle="Italic" TextWrapping="Wrap" Text="'I kicked the first, I smashed the second! And I swam!'" />
          
</
Grid>
        
</
StackPanel>
        
<
ControlTemplate.Triggers>
         
<
EventTrigger RoutedEvent="c:EllipticItem.MouseEnter">
           
<
EventTrigger.Actions>
             
<
BeginStoryboard Storyboard="{StaticResource RDTextAnimation_Enter}" />
           
</
EventTrigger.Actions>
         
</
EventTrigger>
         
<
EventTrigger RoutedEvent="c:EllipticItem.MouseLeave">
           
<
EventTrigger.Actions>
             
<
BeginStoryboard Storyboard="{StaticResource RDTextAnimation_Leave}" />
            
</
EventTrigger.Actions>
         
</
EventTrigger>
        
</
ControlTemplate.Triggers>
      
</
ControlTemplate>
    
</
Setter.Value>
  
</
Setter>
</
Style>

In this sample, the abilities of each RioterDecker member appear in the right panel. These abilities are information define within the template of each item. Therefor, to appear in the appropriate container, I just disconnect from the StackPanel to connect to the right panel (when the corresponding item is selected of course):

private void _OnSelected(object sender, RoutedEventArgs e)
{
 
EllipticItem item = (EllipticItem)sender;
 
StackPanel __panel = item.GetTemplateContainer<StackPanel>();

  Grid __grid = (Grid)__panel.FindName("PART_Abilities");
 
if (__panel.Children.Contains(__grid))
 
{
   
__panel.Children.Remove(__grid);
 
}

  _rdContent.Children.Add(__grid);
 
__grid.Visibility =
Visibility.Visible;
}

 

Conclusion

As you can see, the control enables many rendering possibilities by exploiting WPF ControlTemplate objects. Even if your 3-D knowledges are limited, you are able to use 2-D visual effects to simulate 3-D.
However I know what you gonna say... You think there are too many Xaml declarations and too many redundancies and I agree. In a next post, I will modify this control to integrate DataBinding and DataTemplate to create the visual items. But step by step...

Download the sources

(WinFX January CTP)

posted by Chaz | 0 Comments
Filed Under:

WinFX January CTP Released!

Cool! A new CTP is available but beyond your enthusiasm, it seems that there's nothing new in this CTP for WPF. Indeed, according to Karsten's blog, the Dec CTP and Jan CTP are binary compatible. Nevertheless, you will have Go Live licenses for WCF and WWF available with this CTP. If you wonder why there is no Go Live license for WPF, just see the post of Arik Cohen on his blog to have explainations...

However, you could always read what is new in the December CTP in WPF in this article.

As you intalled this CTP, you will be able to install the excellent Microsoft Expression Interactive Designer CTP (aka Sparkle) after registration. Go get it!

 

posted by Chaz | 0 Comments
Filed Under:

3D Visual Effect in 2D

Introduction

Since Avalon is available, I am impressive by the power of the visual effects such as animations and transformations, orchestrated by storyboards. But the integration of 3-D models in our business applications fascinate myself. Until now it's often a privilege of the video games, the 3-D effects make UI more rich and provide a user experience completely inovative.
However 3-D modelisations, animations, such as rotation transformations for example, need more specific skills. I also noticed a clear increase in the ressources when I apply a visual element on a 3-D model. I think that this powerful feature cannot be used to design real and rich applications with this actual version (I'm coding with the CTP Nov. 2005). Did you see the Healthcare application ? It's awesome! If not, you'd better go to Channel9 right now!

I also noticed that with a few of tricks, it's possible to design controls presenting items with a 3-D appearance by leaning on optical effects.
To illustrate this concept, I will consider items which turn following the path of an ellipse. The item in the first plan is the current selected item.

The illusion of 3-D depth is realized by the translation of the items on the ellipse and by the scale transformation of the items during the animation.
I enjoyed developing a control where elements are ellipse objects with a different color which move according to the path of an ellipse (not visible in the case).

Let's see the code.

Defining items

First I define each element, placed on a canvas. In order to simplify the code, I decided to use simple Ellipse object to represent items on the path.

<Canvas Name="_itemsContainer">
  <Ellipse Name="_item1" Width="10" Height="10" Fill="Yellow" />
  ...
</Canvas>

You can already notice that I don't set the initial absolute position of each element because it needs to be determined according to the number of elements with an equal distance between them.

Defining the animation using the path of the ellipse

Each element is moved by following the path of the ellipse and is animated by a storyboard. WPF provides the timeline DoubleAnimationUsingPath that enables to set an object of PathGeometry type. I set the FillBehavior property to HoldEnd in order to be able to control the execution of the storyboard, as descibes further.

<Storyboard>
  <ParalleleTimeline
>
    <DoubleAnimationUsingPath Duration="0:0:4" FillBehavior="HoldEnd" RepeatBehavior="Forever" Storyboard.TargetProperty="(Canvas.Left)" Source="X" />
    <DoubleAnimationUsingPath Duration="0:0:4" FillBehavior="HoldEnd" RepeatBehavior="Forever" Storyboard.TargetProperty="(Canvas.Top)" Source="Y" />
  </ParalleleTimeline>
</Storyboard>

The first difficulty is that DoubleAnimationUsingPath provides the PathGeometry DependencyProperty which can be set with a SVG instruction corresponding to the attended shape. Unfortunatly, my memories of math are so far and I don't remember the formula to create an ellipse :)! The only solution consists in setting data to create an ellipse either using two beziers segments or two arcs (i.e. PathGeometry="M0,0 C...". Furthermore, I posted another suggestion on the Microsoft Product Feedback website, just right here.
Another solution consists in creating an EllipseGeometry object (for example in a ResourceDictionary) and applying it to the Timeline by code.

[Xaml]
<EllipseGeometry x:Key="Ellipse_Template" RadiusX="125" RadiusY="20" />

[C#]
DoubleAnimationUsingPath __animX = (DoubleAnimationUsingPath)(__storyboard.Children[0] as ParallelTimeline).Children[0];
DoubleAnimationUsingPath __animY = (DoubleAnimationUsingPath)(__storyboard.Children[0] as ParallelTimeline).Children[1];
__animX.PathGeometry = new PathGeometry();
__animY.PathGeometry = new PathGeometry();

__animX.PathGeometry.AddGeometry((EllipseGeometry)this.Resources["Ellipse_Template"]);
__animY.PathGeometry.AddGeometry((EllipseGeometry)this.Resources["Ellipse_Template"]);

Bonus: it's easy to add a RotateTransform to the ellipse rendering. It's more fun...

<EllipseGeometry x:Key="Ellipse_Template" RadiusX="125" RadiusY="20">
  <EllipseGeometry.Transform>
    <RotateTransform Angle="25" />
  </EllipseGeometry.Transform>
</EllipseGeometry>

Defining the scale transformation


The rendering and so the scale of the elements can be easily transformed by using the DoubleAnimationUsingKeyFrames timeline object. Indeed, it enables to modify the value of a DependencyProperty by intercepting a position of time during the animation. Notice that the more number of positions will be important, the more transformation will be fluid.

<Storyboard>
...
 
<ParalleleTimeline>
   
<
DoubleAnimationUsingKeyFrames Duration="0:0:4" Storyboard.TargetProperty="Width" RepeatBehavior="Forever">
     
<
LinearDoubleKeyFrame KeyTime="6.25%" Value="11.25" />
     
<
LinearDoubleKeyFrame KeyTime="12.5%" Value="12.5" />
     
<
LinearDoubleKeyFrame KeyTime="25%" Value="20" />
     
<
LinearDoubleKeyFrame KeyTime="37.5%" Value="12.5" />
     
<
LinearDoubleKeyFrame KeyTime="43.75%" Value="11.25" />
     
<
LinearDoubleKeyFrame KeyTime="50%" Value="10" />
     
<
LinearDoubleKeyFrame KeyTime="56.25%" Value="8.75" />
     
<
LinearDoubleKeyFrame KeyTime="62.5%" Value="7.5" />
     
<
LinearDoubleKeyFrame KeyTime="75%" Value="5" />
     
<
LinearDoubleKeyFrame KeyTime="87.5%" Value="7.5" />
     
<
LinearDoubleKeyFrame KeyTime="93.75%" Value="8.75" />
     
<
LinearDoubleKeyFrame KeyTime="100%" Value="10" />
    
</
DoubleAnimationUsingKeyFrames>
 
</
ParalleleTimeline>
 
<
ParalleleTimeline>
   
<
DoubleAnimationUsingKeyFrames Duration="0:0:4" Storyboard.TargetProperty="Height" RepeatBehavior="Forever">
     
<
LinearDoubleKeyFrame KeyTime="6.25%" Value="11.25" />
     
<
LinearDoubleKeyFrame KeyTime="12.5%" Value="12.5" />
     
<
LinearDoubleKeyFrame KeyTime="25%" Value="20" />
     
<
LinearDoubleKeyFrame KeyTime="37.5%" Value="12.5" />
     
<
LinearDoubleKeyFrame KeyTime="43.75%" Value="11.25" />
     
<
LinearDoubleKeyFrame KeyTime="50%" Value="10" />
     
<
LinearDoubleKeyFrame KeyTime="56.25%" Value="8.75" />
     
<
LinearDoubleKeyFrame KeyTime="62.5%" Value="7.5" />
     
<
LinearDoubleKeyFrame KeyTime="75%" Value="5" />
     
<
LinearDoubleKeyFrame KeyTime="87.5%" Value="7.5" />
     
<
LinearDoubleKeyFrame KeyTime="93.75%" Value="8.75" />
     
<
LinearDoubleKeyFrame KeyTime="100%" Value="10" />
   
</
DoubleAnimationUsingKeyFrames>
 
</
ParalleleTimeline>
</
Storyboard>

Another solution consists in using the RenderTransform.ScaleTransform property, however in this simple case, transform the size of elements uses less ressources.

Defining the initial position around the ellipse

Now it remains the position of each element to be defined along the path of the ellipse. I simply call the GetPointAtFractionLength method of the PathGeometry object. Thanks to Mark Grinols for this information. As you can see below, this method returns two Point objects as output parameter. The first Point will provide the x and y coordinates of the element.

[Metadata]
public void GetPointAtFractionLength (
 double progress,
 out Point point,
 out Point tangent
)

[C#]
double __ratio = 1.0 / _itemsContainer.Children.Count;

Point __point1 = new Point();
Point __point2 = new Point();
DoubleAnimationUsingPath __timeline = (__storyboard.Children[0] as ParallelTimeline).Children[0] as DoubleAnimationUsingPath;
__timeline.PathGeometry.GetPointAtFractionLength(__ratio, out __point1, out __point2);

Canvas.SetLeft(_item, __point1.X);
Canvas.SetTop(_item, __point1.Y);

Refreshing the animation

To finish, I add the necessary code to start the animations when any item is clicked.
So the position of each element must be refreshed but don't forget that the animations must be paused when an item become the current, and so the selected element. To control elements during the animation, I just need to intercept the CurrentTimeInvalidated event of the storyboards. To easily retrieve items and their associated storyboard, I've created an AnimElement class that contains the definition of each item (this class will be replaced by an embedded Definiton class).

__storyboard.CurrentTimeInvalidated += delegate(object sender, EventArgs e)
{
 
if (_selectedItem == null)
  {
   
AnimElement __element = _transforms[((sender as ClockGroup).Timeline as Storyboard)];
   
if (_selectedIndex != __element.Index && Math.Round(__element.ObjectContext.Width) == 20 && Math.Round(__element.ObjectContext.Height) == 20)
      {
        _selectedIndex = __element.Index;
       
Dictionary<Storyboard, AnimElement>.ValueCollection.Enumerator __enumerator = _transforms.Values.GetEnumerator();
       
while (__enumerator.MoveNext())
          __enumerator.Current.Storyboard.Pause(__enumerator.Current.ObjectContext);
       }
    }
   
else if (Math.Round(_selectedItem.Width) == 20 && Math.Round(_selectedItem.Height) == 20)
    {
     
Dictionary<Storyboard, AnimElement>.ValueCollection.Enumerator __enumerator = _transforms.Values.GetEnumerator();
     
while (__enumerator.MoveNext())
        __enumerator.Current.Storyboard.Pause(__enumerator.Current.ObjectContext);
    }
 
}
};

I would like to warn you that you can see an unattended visual effect during the animation. Indeed, it would seem that when an element is moving along the path and is arriving on one extremity, the animation is speeding up. However, the elements are placed according to an equal distance.
In a next post, I will apply these principles to create a reusable and customizable control.

Download the source code (WinFX CTP Nov. 2005)

posted by Chaz | 0 Comments
Filed Under:

SkewTransform limitation

Among the multiple transformation features of WPF, the SkewTransform class is useful to change the rendering appearance of a visual element. It can easily participate in a simulation of 3-D depth effect in 2-D. It provides several DependencyProperty objects to define de center of the transformation, the angle of the x and y coordinates.
Now, considering a rectangle with its 4 coordinates, you may want to transform the shape by the x1 and x2 coordinates in order to simulate a 3-D depth. The figure below illustrates the concept.

Because apply a visual element on a 3-D model needs more resources, SkewTransform would enable to have a similar appearance and bring more impressive visual effects by simulating 3-D effects. For example, it could be combined with the VisualBrush, within a Viewbox to simulate the 3-D depth as shown by the figure below:

This figure above illustrates a new window (it's a custom object, not a WPF Window object) that could be transformed using this new feature of the SkewTransform. The mirror effect is possible using a VisualBrush and all are placed within a simple Viewbox.

You will find my suggestion about this new feature on the Microsoft product feedback website, right here.

posted by Chaz | 0 Comments
Filed Under:

Listen media element events

When you play a media element, sound or video, you may want to be notified when the player starts or stops. The player object publishes two interesting events named MediaOpened and MediaEnded. The first event is raised when the video or the sound is over whereas the MediaEnded event is raised when the media is played again.

To be notified that the player starts and stops, I suggest to use the Clock object of the associated player. Indeed, it publishes the CurrentStateInvalidated event that returns the current state of the clock object for my MediaElement. It can have the Active, Filling and Stopped values. Just subscribe to the event as below:

_mediaElement.Player.Clock.CurrentStateInvalidated += new EventHandler(Clock_CurrentStateInvalidated);

void Clock_CurrentStateInvalidated(object sender, EventArgs e)
{
      Clock __clock = (Clock)sender;
      if(__clock.CurrentState == ClockState.Stopped) //DoSomething...
}

Note that there is an issue if the RepeatBehavior property of the Timeline object is set to RepeatBehavior.Forever (see the preceding post), the current state of the clock is never set to Stopped. You can find my feedback here.

Stay tuned...

posted by Chaz | 0 Comments
Filed Under:

Play media elements in the Nov CTP

If you are developping multimedia applications with the powerfull WPF, you may use the MediaElement to play sounds and videos. I recently had issues to listen the sound file several time, by clicking on a button for example. The sound is played in a more hazardous way that I thought. At this point in time, I suggest to use the media element by code in order to play it controlling the media player. Note that the MediaElement object must be created out of the click method scope, otherwise the sound won't be played correctly.

So I first initialize my media element and immediately stop the player to avoid playing the sound.

_mediaElement = new MediaElement();
_mediaElement.Source = new Uri("./mysoundfile.wav", UriKind.Relative);
_mediaElement.Player.Stop();

Next time, I play the sound in the click method by following 3 steps: stop the player, reinit the position to 0 before playing the sound.

_mediaElement.Player.Stop();
_mediaElement.Player.Position = new TimeSpan(0);
_mediaElement.Player.Start();

It's also possible to have the same behavior by controlling the clock of the associated player. The sound will be played correctly at each click and doesn't need more resources. Note that I must set the RepeatBehavior property to the Forever value to correctly play the sound more than only one time.

MediaTimeline __timeline = new MediaTimeline(new Uri("./mysoundfile.wav", UriKind.Relative));
_mediaElement.Player.Clock = __timeline.CreateClock();
_mediaElement.Player.Clock.Timeline.RepeatBehavior = RepeatBehavior.Forever;

In the code above, I don't need to stop the player because the clock is created each time I click on the button. As you use the player to pause and resume the media, you can also use the Controller (associated to the Clock) to pause and resume it. You will find more info about media elements in the draft article on the Nov CTP.

Download the source code

posted by Chaz | 0 Comments
Filed Under:

Exception management using injection of dependency

Hello that!
In this first post, I just would like to share with you my vision of a component model that could be more and more rich during the life cycle of an application.
Thus, I propose an article that shows how to raise and manage exception objects in business applications without having a breaking code drawback and so without having a fall of performance.
The solution offers a generic and abstract component model designed using one of the multiple forms of the pattern Inversion of Control (IoC) in order to define dependencies between objects that share a same execution context, by re-introducing lightweight containers. With this component model, the article explains how the exception management service is designed based on this component model and how it works.

Read this article here

posted by Chaz | 0 Comments
Filed Under:
More Posts « Previous page