Okay, I missed Tech Friday, so I decided to make it Tech Weekend instead. I attended the June Mix-It-Up: Re-MIX last Thursday and was able to catch up a bit on the new technologies. I have been busy with a few other things at work and I am ashamed to say, I have been lagging behind! 😐
One of the things that interested me is behaviors in Silverlight 3. If you do a bit of searching you’ll find quite a few articles on behaviors. Basically, it’s the ability to encapsulate “behaviors” that you can reuse on different UIElements that developers can write and have their designers use (with drag and drop of course) to earn brownie points. Expression Blend 3 comes with a few behaviors out of the box. They’re the ones listed in the screen shot below, apart from the first one which is the behavior I created:
My favorite, I would have to say is the MouseDragElementBehavior. To use it, all you have to do is drag and drop it to any element in your app to enable drag and drop capabilities. There’s just so much you can do for interactivity with that behavior alone. I remember this being one of the things I use to code time and time again with previous projects I used to tinker with.
Another behavior that I’ve always wished I could encapsulate was the “bring to front” behavior. Say you have a bunch of UI elements scattered in a pile on the screen, you sometimes want it to behave in such a way that an element you click on gets floated to the top. This is probably a behavior that you would like to use in several scenarios/apps so it makes sense to build a behavior for it, and reuse it across different projects.
WARNING: I think I’m using a different build of Blend so I won’t get into the details of where to get which assemblies. I’ll update this post once Blend 3 is out, but I think what’s more important is understanding how building a behavior would be like more or less.
Basically what I did was build a Silverlight Class Library that inherited from the Behavior generic:
public class BringToTopBehavior : Behavior<Canvas> {}//some code here
When creating your own behaviors, Canvas would be the object you would like to apply your behavior to. Next thing to do is override the onAttached() method:
protected override void OnAttached() { base.OnAttached(); AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded); }
AssociatedObject would be the the actual instance to which the behavior is attached. What I did here is add an event handler to the Loaded event because I wanted to attach another event handler to each of the objects that gets loaded into the canvas. I can only get the children once the UI element has finished loading. My Loaded handler looks something like this:
void AssociatedObject_Loaded(object sender, RoutedEventArgs e) { foreach (var child in AssociatedObject.Children) { child.MouseLeftButtonUp += child_MouseLeftButtonUp; } }
What I’m doing is attaching a handler to the MouseLeftButtonUp event for each of the objects contained in the Canvas in question. This handler is defined as:
void child_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) { BringToTop(sender); }
I created a few private methods to handle sorting the z-orders of the elements contained in the Canvas:
private void BringToTop(object element) { Canvas p = (Canvas)((FrameworkElement)element).Parent; int z = getHighestValue(p); for (int i = 0; i < p.Children.Count; i++) { var child = p.Children[i]; if (element.Equals(child)) { child.SetValue(Canvas.ZIndexProperty, z); } } } private int getHighestValue(Canvas p) { int highest = 0; foreach (var child in p.Children) { var val = (int)child.GetValue(Canvas.ZIndexProperty); if ( val > highest) { highest = val; } child.SetValue(Canvas.ZIndexProperty, val - 1); } return highest; }
This probably isn’t the best implementation of this behavior but it gets the job done 🙂
USING THE BEHAVIOR
Once you’ve finished writing your behavior you can compile the code and reference that dll into any of your existing projects and start using the behaviors. Right-click on references, click Add Reference, and navigate to the compiled dll of your custom behavior.
Once you’re done, you should be able to make use of this behavior through blend. In my case, I can click and drag BringToTopBehavior to my chosen Canvas and I would see the following:
Now you’ll notice, however, when I try to drag and drop the behavior to the Grid panel, it tells me that it’s not a valid drop target.
This is because, in my definition of my behavior, I’d specified that this behavior can be applied only to Canvases. But now, I realize the same behavior can apply to any Panel object. What I can do now is update my behavior definition to, instead of Canvas, have it apply to Panel instead:
public class BringToTopBehavior : Behavior<Panel> { //somecode private void BringToTop(object element) { Panel p = (Panel)((FrameworkElement)element).Parent; //some code } private int getHighestValue(Panel p) { //some code } }
With this change, I should be able to apply the behavior to any UIElement inheriting from Panel. I’ve zipped up and uploaded the code for this project. Feel free to download and explore here: Update: I realized that the file I uploaded wasn’t working. I updated the file, you can download it from the expression gallery on http://gallery.expression.microsoft.com/en-us/BringToFrontBehavior
Want to contribute to the gallery? Visit http://gallery.expression.microsoft.com/en-us/site/create
To see a sample application, check out http://www.badgorilla.net/behaviors
For more about the greatness of behaviors, check out this Mix Video: http://videos.visitmix.com/MIX09/C27M and of course the link to the behavior pack used in the demo: http://gallery.expression.microsoft.com/en-us/MIXBehaviorPack
