As weird as the title sounds, I love what behaviours using attached properties bring to Silverlight and WPF. They are kind of what Javascript is to HTML elements - the wonder that extends controls beyond their limited initial intentions.

Now, with respect nRoute, what I did was map every event in almost all the controls in SL 2 base library to do three things, one navigate, two take an action and three execute an ICommand (sorry this is like a little mantra I keep repeating). The core point being, this is a very extensive body of abstraction that should help with over 75% of your UI interaction at the very least. Also, this is based on the Prism 2's behaviours' code pattern, but in their instance they were limited to Buttons - here it is starts right at the bottom from UIElement. Tabulated below are the behaviours available for events in nRoute:

Control Behaviour for Control's Event NAV ACN CMD
UIElement GotFocus x x x
  LostFocus x x x
  KeyDown x x x
  KeyUp x x x
  LostMouseCapture x x x
  LeftMouseButtonDown (named MouseDown) x x x
  LeftMouseButtonUp (named MouseUp) x x x
  MouseEnter x x x
  MouseLeave x x x
  MouseMove x x x
         
FrameworkElement BindingValidationError (Silverlight Only, N/A in WPF) x x x
  LayoutUpdated x x x
  Loaded x x x
  SizeChanged x x x
         
Control EnableChanged x x x
         
ButtonBase Click x x x
         
RangeBase ValueChanged x x x
         
Selector SelectionChanged x x x
         
ToggleButton Indeterminate, Checked, UnChecked (into one StateChanged Event) x x x
         
TextBox SelectionChanged x x x
  TextChanged x x x
         
PasswordBox PasswordChanged x x x
         
Scrollbar Scroll x x x
         
Image ImageFailed x x x
         
Timeline* Completed x x x
         
Abbreviations: NAV = Navigation Support, ACN=Actions Support and CMD = ICommand Support

Believe it or not these are almost all the events exposed by SL2 base-library controls with exception of four events (Tooltip's opened and closed events, and ComboBox's DropDownOpen and DropDownClosed events) and three other controls' (MediaScaleImage, MediaElement, and Thumb Control) events. Note, the Timeline object is a DependencyObject and not an UIElement derivative, so it exhibits a bit different usage semantics (explained ahead).

How do we use it? In Visual Studio, you import the relevant namespaces and basically attach them to what ever element you want. Below we have behaviours attached to a Button, Border, ItemsControls and it can be almost anything deriving for any of the classes listed above so thing like ListBox, CheckBoxes, Storyboard or your custom controls.

Declare XML Namespaces
Showing Navigation Behaviours Avaliable to a Border Control  Showing Loaded Action Behaviours Parameters for an Items Control

Showing Command Behaviours Parameters Avaliable to a Button Control
Samples of attached behaviours on various controls

Let me explain the three behaviours attached to the buttons in the screenshot just above. In the first case we have attached a behaviour that on the click event of the button executes the NewOrderCommand. In the second example, we have an action behaviour attached to the mouse-enter event of the button, and the action is identified with the Url shown. Also the action can have zero or more handlers at the back end, which can do whatever as necessitated by the business logic. Lastly, we are attaching a behaviour that will execute if there is a binding validation error, and it will navigate to the shown Url. Now, where (as in which container) the navigation occurs is configurable, but you can say put up a side panel that could show the content from that Url.

I just want to raise this point here again, you can attach to all the listed events above and execute any of the three categories of behaviours from just about any control, including your own. If you see the code for the demo app posted, I used these behaviours and had exactly zero code-behind the xaml files (except where I had to do element to element binding or triggering property updates - both these facilities are now there in SL3). This is very significant for both M-V-VM design pattern, and in general to raise the level of abstraction (along with flexibility, changeability etc benefits) from having to deal with Urls rather than instance types.

Now, the "bhv" namespace imported above is a bit special, it is specifically used to attach multiple behaviours. And because of how the internals of Silverlight work, it has one limitation which is you can't use binding expressions with it (the same is the case with the timeline behaviour), but you can use static resources binding expressions just fine. Below is an example of multiple bindings where the arrow keys are mapped to some action Url.

<!-- KEYBOARD ACTION MAPPINGS, MAPPED TO/WITH THE BORDER -->
<Border>
    <bhv:Behaviours.Multiple>
        <bhv:BehavioursCollection>
            <bhv:KeyDownAction Key="Up" ActionUrl="Move/North" />
            <bhv:KeyDownAction Key="Right" ActionUrl="Move/East" />
            <bhv:KeyDownAction Key="Left" ActionUrl="Move/West" />
            <bhv:KeyDownAction Key="Down" ActionUrl="Move/South" />
        </bhv:BehavioursCollection>
    </bhv:Behaviours.Multiple>
</Border>

Next, I wanted to explain the common properties you will see for each of the three categories of behaviours. As an exception note, KeyUp and KeyDown behaviours have two added properties specifying the key and the key modifier, rest all have the same properties as show below.

Navigation Behaviours

NavigationBehaviours

  • NavigateHandler: Here you can specify (rather bind to) any object that implements INavigationHandler to handle the navigation response. Note this is also a way consume navigation requests and responses in non-visual ways.
  •  NavigateHandlerElementName: Alternatively if the handler is an element in the user interface you can give it's element name and it will be asked to handle the result of the navigation. NavigateParameters: These are name-value collections you can pass with the request.
  • NavigateUrl: Obviously the Url to navigate to, and it is required.
  • NavigateUrlStringFormat: This is used to provide a format for the NavigateUrl property, it is similar to what was introduced in .NET 3.5sp1 binding and it helps in that it can avoid the use of type convertors in some scenarios.

One thing to know, and this is important, is how the behaviour chooses the container that will handle the response. Firstly, if you have specified the handler (either by binding or by an ElementName) it will just use that and life is simple. If you do not specify, it will look in it's visual tree first and try and find the first parent that implements INavigationHandler interface. This is also means that it works kinda like a browser whereby when you click a link the immediate most container (think IFrames here) handles the navigation for you. If no handler is found in the parental visual tree, it will look at the current application object and see if it has is a registered container (of type INavigationContainer) and use that. If it still doesn't find a container at the application level, well it gives up and throws an exception.

Action Behaviours

ActionBehaviours

  • ActionHandler: Here like above you can specify (rather bind to) any object that implements IActionHandler interface to handle the action request. Note the handler doesn't need to be registered against with the ActionService, and so this is also a way to bypass the registered Url handlers ie. they will not be called.
  •  ActionHandlerElementName: Alternatively if the handler is an element in the visual tree you can give it's element name and it will be asked to handle the action. Again, as above this is a way to bypass the registered action handlers.
  • ActionParameters: Like before these are name-value collections you can pass with the request.
  • ActionUrl: Quite intuitively the Url for the action, and it is a required parameter.
  • ActionUrlStringFormat: This is used to provide a format for the ActionUrl property, and helps in that it can avoid the use of type convertors sometimes.

The resolution of the action is basically similar to the navigation behaviours, but with exception that it doesn't look up in the visual tree for anything that implements IActionHandler and directly goes onto any registered handlers for the action Url.

Command Behaviours

CommandBehaviours

  • Command: You basically specify, like WPF, any object that implements the ICommand interface.
  • CommandParameter: And the accompanying parameter can be specified here.

In this case the hooking up is rather direct, you must have specified the ICommand implementing handler and the behaviour will execute it for you whilst also passing in the parameter. This helps overcome the lack of build-in support for ICommand but also extends the usage semantics in WPF.

As this has gotten quite long I'll skip the demo for another post, but I hope you can see how these behaviours bridge the element's events with a handler/consumer either directly (like with ICommands) or indirectly (via Urls) saving you a lot of work and raising the abstraction level. In fact off the 35,000+ lines of code in nRoute 60% are dedicated to bring you these behaviours (over 75 of them) - so it's quite a bite.

Comments

trackback
Ork Pad
on 17-Mar-09 3:56 AM
Trackback from Ork Pad

nRoute: Silverlight abstractions of the higher kind

trackback
DotNetShoutout
on 20-Mar-09 11:29 AM
Trackback from DotNetShoutout

nRoute: Comes complete with a full set of behavioural teeth

trackback
DotNetKicks.com
on 20-Mar-09 11:29 AM
Trackback from DotNetKicks.com

nRoute: Comes complete with a full set of behavioural teeth

Mike Brown
Mike Brown United States
on 21-Mar-09 8:21 AM
Very ambitious undertaking you have done here. I've leveraged the Blend 3 behavior framework to create a TriggerAction that executes an ICommand. The TriggerAction in general does the equivalent of your Action and I've also created a NavigationAction that is a trigger action that causes navigation. The TriggerActions can fire in response to any Silverlight Event. I don't think the framework does anything that isn't supported in SL2.

Rishi
Rishi
on 21-Mar-09 1:59 PM
@Mike, thanks. Now when I did write this I had no idea what was in store in either SL3 or Blend. And I created these as a way to get away from the code-behind glue, and raise the level of abstraction. As for Blend behaviours I've seen the Mix demos and they are super cool, but really I haven't looked into its workings - though I've got couple of posts by Christian Schormann open for reading right now. All the same, I think the ideals behind Blend 3 behaviours are absolutely what we need, and over time I will definitely look into it and try and leverage whatever I can. We need to abstract higher, and indeed after these event level abstraction I've shown here, I next wanted to attempt higher level abstractions - have a look at the demo app code where I did the hover behaviour. But now hopefully I'll be able to use the Blend 3 infrastructure, and if possible bring it down to SL2.

Mike Brown
Mike Brown United States
on 21-Mar-09 10:10 PM
You know I hadn't looked at the full thing you were doing totally cool! I mean wickedly, insanely cool! Do you have a codeplex project setup?

Rishi
Rishi
on 22-Mar-09 7:39 AM
@Mike, yeah like totally, with complete source code and a demo app.. check out:

http://nroute.codeplex.com/

and incase you haven't seen the demo app, make sure you have a look at:

http://www.orkpad.com/nRoute/FutureDesktop/

Cheers

Matt
Matt United States
on 31-May-09 10:34 AM
Great and inspirational stuff!
Thanks for sharing.

trackback
geff zhang
on 14-Jun-09 4:20 AM
Trackback from geff zhang

?? nRoute ??????? Silverlight ?????

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading