XAML, C# & WPF - How to structure your project

For years I have been a big Flash fan and developer. When I first opened up XAML and Expression Blend, I had expectations that I would be developing GUI's and animations the same way I have been in Flash. I found out that although there are similarities, it is not possible to structure your project in the same way.

The best way in Flash

In Flash, the best way to structure your project is to create a lot of separate animations that are self contained add them to your library and then drop them either directly into main time line or into an encapsulating animation which subsequently gets dropped into the main time line.

It is then easy to let your animations jump from one animation to the next. In Flash the animations and the GUI can be highly organized so that as your project gets big, it is still manageable.

Expression doesn't work the same way, I wish it did - how to fix it

Expressions main navigation mechanism is the page. The page gives you some free functionality. Users are able to navigate forward and back exactly the same way they can in a web browser.

So with page navigation, as developers we create separate pages that encapsulate their own GUI, animation and WPF takes care of the rest of the navigation.

There are 2 really big downfalls with pages.

Pages don't allow you to have animation transitions from one page to the next.

And

The GUI makes a "clicking sound" when you press the forward and back button as well as any link you have from one page to the next.

What the hell were they thinking with that click. I have read some articles on it and they say its because it is built into the browser. As of .NET 3.0, there is no way for us to turn off the clicking from our application. The suggested way is to have the user turn it off from their system sound settings. This completely blows.

The second navigation choice - User controls

With Blend and WPF you can create User controls which are independent chunks of GUI, animation and code.

User controls allow us to create an organized and structured project that can scale in size and complexity without getting unwieldy.

So now we can get back to organizing our GUI and animations back in a format that makes sense and is similar to the way Flash does it.

Just one more hurdle to jump

If like me you have used Flash, then you would expect that there would be an easy way to drop user controls into the timeline and they can either play one after another or you can jump to another user control when user clicks on a button. Blend doesn't give you this functionality.

Attempting to do it purely in Blend

As far as I can make out the only way to have animations play one after another or have the animations change to the next user control in Blend is to have all the user controls sitting in the timeline right from the start and have their visibility turned to Hidden or Collapsed.

This is a truly messy solution. It works for trivial projects, but not for full blown applications.

Finally a way to do it

Using pages and using user controls in Blend really are dead ends. I spent a number of days banging my head and searching high and low for the best practice on how to structure WPF.

The solution is to stick with the user controls, have your timeline empty or just have an initial user control in there then drop back into C# to control the adding and removing of user controls to the GUI.

In C# we can hook into the user controls animation events and user interaction events.

Code to animate/fade in a user control


public void FadeIn()
{
myLayoutGrid.Children.Add(this);

Storyboard sbMenuFadeIn = (Storyboard)FindResource("MenuFadeIn");
sbMenuFadeIn.FillBehavior = FillBehavior.HoldEnd;
this.BeginStoryboard(sbMenuFadeIn);
}


This code is inside my user control C# class. Stepping through the code line by line

myLayoutGrid is a property of the class that is points to the main Grid used in my XAML. You can think of the Grid as an empty screen that is ready to put user controls onto.

I have my main window pass the user control a ref to the grid when it initialises the user control.
Using the Children.Add(this) places the user control onto the grid.

The rest of the code finds the storyboard called MenuFadeIn, sets it to stop animating at the end of the storyboard and then actually starts the animation.

MenuFadeIn must be a valid storyboard animation that belongs to the user control. I built the animation in Blend to make it easier.

Code to fade out a user control

fading out the control takes a little bit more work, but not much more.

The steps are

  • Find the resource to the animation
  • Add an event handler to call when the animation ends
  • Play the animation
  • when the animation end event fires, remove the user control from the grid.
And here is the code

public void FadeOut()
{
Storyboard sbMenuFadeOut = (Storyboard)FindResource("MenuFadeOut");

sbMenuFadeOut.Completed += new EventHandler(FadeOutFinished);
this.BeginStoryboard(sbMenuFadeOut);
}

// event that gets fired when the animation completes
void FadeOutFinished(object sender, EventArgs e)
{
myLayoutGrid.Children.Remove(this);
}

So there you have it, now you can happily create some stunning interfaces and be able to structure your code well.

0 comments: