Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Lists Switch Animation_MVVM Meets Async-Ctp

0.00/5 (No votes)
24 Nov 2011 1  
Visual representation for a logical items-switching between two lists

Introduction

In this article, I'll show items-switch between two lists with visual representation. I'll do it under MVVM pattern, performing View-ViewModel-View-ViewModel passes while preserving decoupling.
I'll use the Async-Ctp's Awaitable Task for async waiting in the ViewModel. Along the way, I'll show a technique to achieve Bezier-Animation in Silverlight.

Background

Over the last few years, I've come across an understandable misconception regarding the WPF/Silverlight use/necessity/role of animation.

Many programmers, usually with Winforms substantial background tends to regard animation as a decorative addition to the old set of UI. Judging from the abundance of jumping, spinning, color-changing controls, this misconception is fairly understandable. While, in fact, Animation is actually an important additional channel that allows the application to communicate with the user, thus conveying important information regarding the application's state and activities (with or without correspondence to the user actions).

On one occasion, I came across a company that 'held' this wrong point of view, and decided to take upon myself the task of convincing them about the crucial role animation has in a modern UI. Since this company dealt with Basketball (yes, the game...) management-related software, I was looking for a scenario in that area, that would prove my point.

What I came up with was - the 'substitution' management scenario. In short: two lists interface(Five, Bench) that allow the application's operator to perform items(Players) switch between them. Drag&Drop solution, although intuitive, ruled out due to operational ease-of-use and quick response reasons. So, I came up with the 'select player on each list' triggered switch solution.
The nice thing about this scenario is that it is almost impossible to perform under winforms (in an actual UI understandable manner), and it is fairly easy using WPF/Silverlight's animation.

Code Description

Two logical lists of players in ViewModel (five, bench) binded to two Listboxes in the View respectively. When the ViewModel detects Mutual (both Five & Bench) selection, it raises an event that tells the View to commence the 'switch-animation': The View Constructs & Displays the appropriate Arrows and performs the animation. When finished, it notifies the Model which 'Commits' the logical switch in its lists and stores the switch-data for Undo purposes.

Using the Code

MVVM Meets Async-Ctp

The flow can be illustrated like this:

As explained above, the 'Switch Task' actually starts when the ViewModel identifies that two players (one on each list) got selected. Then it performs the StoreAndPerformeSwitch subroutine.

private async void StoreAndPerformeSwitch(Player SwitchBP, Player SwitchFP)

This soubroutine starts with 'waiting' for the 'Visual switch' to perform and finish:

 await TaskEx.Run(() =>PerformSwitch());

All PerformSwitch does is raise BeginSwitchAnimation Event, and wait (its thread) for the ManualResetEvent 'gate' to get open (set),
It does (get open) when the ViewSwitchEnded method gets called from the View (when it finishes the animation).

Then it commits the Logical-Switch and stores the switch-data into the undo stack.

SwitchData sd = new SwitchData() { BenchPlayer = SwitchBP, FivePlayer = SwitchFP };
...
sd.BenchPlayer.IsSelected = false;
sd.FivePlayer.IsSelected = false;
int InxInFive = Five.IndexOf(sd.FivePlayer);
int InxInBench = Bench.IndexOf(sd.BenchPlayer);
Bench[InxInBench] = Players[Players.IndexOf(sd.FivePlayer)];
Five[InxInFive] = Players[Players.IndexOf(sd.BenchPlayer)];

if (!OnUndo) stckSwitches.Push(new SwitchData() 
{ BenchPlayer = Bench[InxInBench], FivePlayer = Five[InxInFive] });    

Finally, it 'renews' the UndoCommand - this causes it to ReEvaluate its IsEnabled state based on the Undo-Stack state.

UndoCommand = new DelegateCommand(Undo, CanUndo);

ArrowPath

Originally, this was a Custom-shape in the WPF's version of this project. This custom-path is responsible for drawing the Arrow, and, along the way, constructs a matching Bezier curve for the movement animation.

Bezier Animation

Although path/curved-line animations in Silverlight are usually done by fragmenting the curve and animating from one fragment to the next, here, we can use a much simpler approach: Bezier is actually a mathematical formula to describe a curve upon giving four control points. Conveniently, it can return x,y position on its curve for any zero-to-one input value. So, all we actually need to do is 'DoubleAnimation' values from zero-to-one, put it into our Bezier formula and get the X,Y we need for positioning our animated element. As the preceding lines imply, there should be an Object that, on the one hand, will hold the Bezier formula, and, on the other will receive animated values.

In a 'perfect world', we will construct an object with two AttachedProperties:

  1. Zero2One (double)
  2. Bezier (AnimatedBezier)

We'll attach it to the targeted element (on which we want to set the corresponding x,y). Something like that (pseudo):

 <TranslateTransform x:Name="ttSTF" Bezier={Binding ElementName="
    ArrowWithMovementBezier", Path="MovementBezier" }/>

Then, we'll only need DoubleAnimation with Storyboard.TargetName="ttSTF" and something like that - Storyboard.TargetProperty="(local:OurObject.Zero2One)".
Inside the Zero2One dependency-property-changed subroutine, we'll set the attached-object's x,y values by calculating against the Bezier formula.

Unfortunately, as it seems, property-path to Custom-Attached-Property is not supported!
i.e., - you can have this propertyPath - '...(Canvas.Left)', but, you can't do this - '...(some_namespace:MyCanvas.MyLeft)'
* if any of the readers knows how to set PropertyPath to a Custom-AttachedProperty, please share.

In 'real life' - there are several ways to overcome this obstacle, I chose to create an inert-proxy Element:

<local:Bezier2DoubleAnimationMediator x:Name="mediator2ttSTB" 
Target="{Binding ElementName=ttSTB}" AnimBezier="{Binding  ElementName=SwitchToBenchArrow,
Path=AnimBezier}"/>

This element has Zero2OneValue dp, AnimBezier dp, as the previous solution, but has an additional Target dp.
Now the animation acts on this element's Zero2OneValue DependencyProperty, and consequently, it will change its Target's X,Y values.

History

  • 24th November, 2011: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here