Extensible Templates in Deep Zoom Composer

July 19th, 2009     |     View Comments

In the previous post, I started talking about some of the collection-related improvements we made in Deep Zoom Composer. The crux of that post was describing how you can defer to the Silverlight application the task of arranging and positioning your images.

Deep Zoom Composer ships with one basic template, based on my earlier Tag Browser, that handles simple arrangement of like-sized images during runtime. Of course, there are many more things that you may want to do beyond just having items be arranged in a grid. You may want to arrange them circularly. You may even choose to go scattershot like one of the layouts you have in DeepZoomPix:

scattershot

Beyond just the layout of the images, you have access to several pieces of metadata in the scene.xml and metadata.xml files!

Essentially, there are a lot of things that you can do that our default templates simply do not expose in Deep Zoom Composer (DZC). To help solve that problem, we made the process of selecting a template from within DZC extensible:

dzc_dropdown

While we currently ship with three templates, you can easily add your own. This post will describe briefly the two types of templates you can add.

Template Without Source Files/Project
To simply provide your own template, all you need is an HTML file named test.html and the corresponding XAP that is used to display your Deep Zoom images. Your Deep Zoom Images will be in GeneratedImages/dzc_output.xml relative to your XAP.

Note: The test.html file needs to have the appropriate tags to actually display the XAP file.

Once you have your HTML file and XAP file, you will have to find the Deep Zoom Composer installation directory. By default, that is found under Program Files/Microsoft Expression/Deep Zoom Composer. Once there, find the folder named Export Templates. You will see three folders whose name matches the name of the templates you see in the above screenshot:

folders_export

In this location, add a new folder whose name will be what you want to have showing up inside the templates drop-down in DZC. I am going to name my folder Kirupa’s Template:

folders_new

Once I have created this folder, I simply paste my HTML file named test.html and corresponding XAP file into it.

xap_html

After doing all of this, if I restart Deep Zoom Composer, switch to the Custom tab under Export, and expand the templates drop-down menu, notice that Kirupa’s Template now appears:

extensible_template

When you export with your chosen template, your HTML file and XAP will be copied along with the GeneratedImages folder that contains the various XML files and Deep Zoom image tiles:

kirupaTemplateGeneratedImages

If you find yourself stuck, you can always look at how the Tag Browser and Default templates inside your Export Templates are laid out.

Template With Source Files/Project
In the previous section, I explained how to add your own custom template with just an HTML and XAP file. There will be cases where what you want to use involves source files. For example, you may create a generic template that does something really cool with the various metadata files we generate. The actual look of the application is something that you would want a more technically/design savvy end-user to modify instead.

For those cases, an HTML file and XAP file are grossly inadequate. What you need to be able to provide is both the HTML file and XAP along with the source files associated with it. To add your custom template that includes source files, the steps are a little similar to the general XAP + HTML case. What is different is what DZC expects in terms of folder names and project locations.

Note: For reference, look at how the project under Deep Zoom Classic + Source are laid out.

Inside your Export Templates folder, create a folder whose name is what you want your template to be known as.

Next, create a new Silverlight project using Expression Blend 3 named DeepZoomProject, this solution and folder structure is created for you. When you build your application, you will basically see the following structure:

folder_images

If you compare this directory with what you see in your Deep Zoom Classic + Source folder, you will see that almost everything is identical:

export_project

The only thing that is missing in what you created in Blend and what DZC expects is a GeneratedImages folder in your ClientBin directory living alongside your XAP. You can add this folder easily using Blend 3 by right-clicking on the ClientBin folder and selecting Add New Folder:

addNewFolder

All that is left after you add your new folder is to rename it to GeneratedImages to make DZC happy:

generatedImages_2

Once you have your project done, just copy and paste your DeepZoomProject Solution file, the DeepZoomProject folder, and the DeepZoomProjectSite folder into the folder you created under DZC’s Export Templates directory. The folder you paste your source files into must contain the word Source as part of its name. For example, my folder is called Kirupa’s Template + Source:

source_folder_name

Conclusion
This post wraps up my coverage of the new Export-related features we now have inside Deep Zoom Composer. Hopefully this post and the previous post help you create the really nice Deep Zoom collections and visuals that you had always wanted to do easily.

Cheers!
Kirupa :)

A couple of days ago, we released another version of Deep Zoom Composer. One of the major changes we made revolved around dealing with large collections of images, and this was something that Deep Zoom Composer never handled particularly well in the past.

Some Background
As you may know, Deep Zoom Composer allows you to import your images, arrange them in whatever fashion you like, and even add some cool interactions and navigation. My fellow Expression Blend colleague, office mate, and co-conspirator on Deep Zoom Composer, Janete Perez, wrote extensively about those features in her series of posts here, here, and also here.

Manually composing your layout is great for a moderate number of images, but once you cross into hundreds if not thousands of images, the workflow does not scale. Manually arranging that many images will not be fun, and it probably isn’t even what you want to really do.

When you are dealing with large quantities of images, more than likely, what you want to do is have your Silverlight application take care of arranging and positioning your content at runtime instead such as what this example highlights. This on-the-fly positioning and arranging is what most of the popular Deep Zoom examples such as Hard Rock Cafe do as well.

Deep Zoom Composer, with our recent release, now plays two roles. The first role is the one it always played. It still allows you to import your images, arrange them on the artboard, and export them:

 image

The second role is something that is new. You still import your images. That has not changed. What is new is that you no longer have to compose your images. You can just import your images, skip the compose step altogether, and go straight to exporting:

image

This means that you can safely import hundreds (if not more!) images and seamlessly export them.

Using the New Features
Once you have imported your images, go straight to your Export view:

image

From the Export view, click on the Custom tab to go to see our more advanced Export options. Make sure you are exporting a Silverlight Deep Zoom output, give your project a name, and ensure the Export as a collection option is selected by default if it hasn’t already been selected for you.

The next step is crucial. You need to pick a project template that supports rearranging on the fly inside your browser. Currently, the only template that does that is the Tag Browser. Click on the drop-down that says Default and select the Tag Browser entry:

image

Your Custom tab’s various options and selections should look like the following:

image

Hit the Export tab to generate the Deep Zoom image tiles and associate them with the Tag Browser template that you selected. After a few moments your Export Completed dialog will appear:

image

Click on the Preview in Browser button to launch your browser and to see how everything looks. You will see that the images you imported automatically appear in a grid:

dzt_tb

Notice that the Tag browser actually allows you to filter by tags. Wouldn’t it be cool if Deep Zoom Composer allowed you to do all of this as well…?

Tagging your Images
Programmatically laying your images out is cool, but being able to filter what images you see based on the tag is even cooler. To be able to do this, you will have to venture into your Compose view. In the Compose View, you will see all of your currently imported images laid out horizontally at the bottom of your screen along with a list of the properties on each image you can view or set:

 dzctb2

You can select one or multiples images from this list and add the Tag that you want in the Properties panel you see on the bottom-right:

taggedAnimals

When working with lots of images, though, the tiny panel you are viewing all of your images may not be very helpful. You can maximize this panel by clicking on the up-arrow found on the top-right corner of the panel itself:

arrow_click

Once you click on that button, notice that your entire Compose view is nothing more than your list of imported images and the properties you can view or set on them:

maximize_collection_exporting

This maximized view optimizes Deep Zoom Composer for doing nothing but pure Collection exporting with the option of tagging your images.

If you export again, using the same selections that you had before, notice that the Tag Browser now displays any tags you may have displayed with the option of clicking on a tag and viewing only the images associated with that tag:

filtering_images

Hopefully you find this feature as much fun to use as it was for us to implement it. You can always use Deep Zoom Composer to manually arrange and position your images, but if want to not deal with that and have your images arranged and positioned on the fly, you can now do that as well.

As you can tell, this post got pretty long, and there are a few more features that I will describe in a future posting. To give you a heads-up, the templates you see when exporting as a Collection, such as Tag Browser, are extensible. You can add your own custom templates and have it be something you can select directly from within Deep Zoom Composer.

Stay tuned to learn more about that.

Cheers!
Kirupa :P

Making it Easier to Write Behaviors

July 9th, 2009     |     View Comments

Behaviors make it easier for designers to be able to do cool things that would otherwise require writing code. Of course, a behavior doesn’t just magically appear out of thin air.* It requires someone actually writing it.

While one our goals was to make it easy for someone to write a Behavior, there is a certain amount of boilerplate code that you need to be aware of before you get to writing code that is more familiar to you. To help make that process easier, we are going to be providing Item Templates that can be accessed from both Expression Blend 3 as well as Visual Studio 2008.

Here are the Action, Behavior, and Trigger item templates in Blend 3:

baction

…and here they are in Visual Studio:

vs_item_templates

These item templates contain the boilerplate code I referred to earlier, so with a few clicks, you are up and running with something that Blend recognizes as a Behavior, Action, or Trigger

For example, here is what a Trigger looks like when created via our Item Template:

public class Trigger1 : TriggerBase<DependencyObject>
{
protected override void OnAttached()
{
base.OnAttached();
 
// Insert code that you want to run when the Trigger is attached to an object.
}
 
protected override void OnDetaching()
{
base.OnDetaching();
 
// Insert code that you would want run when the Trigger is removed from an object.
}
 
//
// To invoke any associated Actions when this Trigger gets called, use
// this.InvokeActions(o) where o is an object that you can pass in as a parameter
//
}
 

We create all of the infrastructure code that makes your Trigger a trigger, and we also provide you with some helpful comments on what to do. Because our behaviors functionality requires referencing a DLL that we provide, that referencing step is taken care of automatically for you as well. Wouldn’t it be great if everything in life were as simple as this? Ok, I’ll stop now! :P

How this is Possible
Everything I’ve written above uses the Item Templates format documented on MSDN. What is new is how these templates get installed. We will be releasing in the near future a standalone Expression Blend SDK that makes it possible for you to create and redistribute the various new components we introduced as a part of Blend 3. You can read more about this on a blog post I wrote for the Expression Blend & Design team blog.

Cheers!
Kirupa :)

*That feature is currently being prototyped in an underground bunker in an undisclosed location.

Targeting Other Elements via the Blend 3 UI

June 6th, 2009     |     View Comments

In an earlier post, I described how you can change what your EventTrigger and Action (based on TargetedTriggerAction) can target. You can get into various interesting scenarios where what your Action is attached to is not the element your Trigger is listening for events on, nor is the element your Action is attached to the one that gets affected by whatever the Action does.

What I have not done is described how you would actually be able to do this using the Blend UI. That seems kinda important! Let’s say you have an Action (which derives from TargetedTriggerAction) applied to a Button. If you select the Action and look over in the Properties pane, you would see something similar to the following image:

pi_tta

The two properties that allow you to target other elements are SourceName in the Trigger category and TargetName in the Common Properties category:

two_targetableProperties

By default, both of these properties are set to target the Parent. Parent is the element your Action is applied to, but you can change what your element targets in two ways.

The Select Element Dialog
In the MIX release of our Blend 3 Preview, one approach was by hitting the little … button and launching the Select Element dialog:

visualElementPicker

The Select Element dialog provides you with a copy of your object tree with all visual elements displayed. The emphasis on “visual” is important because your object tree today displays non-visual content such as behaviors, and you will not be able to select them via the Select Element dialog.

This dialog is great for being able to pick from a large list of elements or to pick from an element whose name you know but cannot actually see on the artboard. For example, think of a circle whose opacity is set to be fully transparent. Unfortunately, these are edge cases. We didn’t want the only way to be able to target other elements to be one that was optimized for edge cases! To deal with this, shortly after MIX, we decided to add another way for you to be able to pick elements to target, and that is the Artboard Element Picker.

Artboard Element Picker
Like I briefly mentioned in the last paragraph, the Select Element dialog is primarily useful for a few edge cases. The most common case is one where you want to select an element that you have visible on your design surface. To help with this, we introduced the Artboard Element picker, and it can be accessed via the the bulls-eye icons you see inside the SourceName and TargetName properties:

blue_bullseye

When you click on either of these icons, you enter a special element selection mode where your mouse cursor changes to that of a bulls-eye, and you can move your mouse cursor over any element on the artboard to select it:

selectingElement

Notice that, now, I simply point and click at the element I want to target, and that is pretty convenient when what I want to target is clearly visible. If you have a lot of elements that possibly overlap, such as the four overlapping squares I have in my example, you can always hit the Ctrl key and click to display a menu of elements under the mouse cursor that you can select:

menuBlueSquares

You also have the ability to use the Artboard element picker on the Object tree itself. You would think with the name of “Artboard Element Picker”  you would be constrained to just the artboard, but you are not:

artboardElementPicking

The cursor displays the cancel sign when you hover over areas outside of the Artboard or Object tree, so it isn’t possible yet to select other types of things you may want to target.

Conclusion
Hopefully, this post helped give you a glimpse of what we are doing to make it easier for you to use Behaviors by presenting you with two ways you can easily use the targeting functionality our API provides. There are other niceties that we’ve added for Behaviors that I will describe in some more detail shortly.

Thanks,
Kirupa :)

Behaviors and Commands

May 17th, 2009     |     View Comments

In my previous post, I explained the basics of how to write a simple behavior. I concluded by mentioning that a behavior can actually be made to work with triggers. This is possible despite a Behavior having no real mechanism for a trigger to bury itself into. How is that possible? This post will explain how.

I may have lied a little bit. My previous explanations of Behaviors being solo-artists is not entirely accurate. While Behaviors can work independently without any external influence, unlike an Action, they can also be made to do certain things when certain conditions are met:

behavior_summary

That is why the condition part of the original sentence is grayed out as opposed to completely .being removed.

Your Wish is My ICommand
If you want your behavior to be capable of being triggered, you can expose Commands (anything of type ICommand) to be the function that will get called. If you are confused about what I just wrote, don’t worry. Let’s write a behavior that demonstrates this. First, make sure your project has a reference to both the Microsoft.Expression.Interactions and Microsoft.Expression.Interactivity DLLs.

After you have done that, create a new Class file, and before you do anything else, add the following using statement:

using Microsoft.Expression.Interactivity.Input;

Once you have done this, add the code to turn your class into a behavior:

public class BehaviorWithCommand : Behavior<DependencyObject>
{
public BehaviorWithCommand()
{
this.MyCommand = new ActionCommand(this.MyFunction);
}
 
protected override void OnAttached()
{
base.OnAttached();
}
 
protected override void OnDetaching()
{
base.OnDetaching();
 
}
 
public ICommand MyCommand
{
get;
private set;
}
 
private void MyFunction()
{
MessageBox.Show("Hello World");
}
}

Note that I call my behavior BehaviorWithCommand, so be sure to rename your class name and constructor appropriately instead of using my name if you copy/paste. Save and build your project to make sure everything works and nothing is complaining.

Looking at the Blend 3 UI
Go ahead and apply this Behavior any element in your Silverlight or WPF application. Once you have added it to something, select this behavior and look at what is displayed in your Properties Inspector:

 mycommandUI

Notice that you now see a category called MyCommand, the name of your ICommand property specified in code.

From this category, on the top-right corner, you can use the plus button to add the triggers that will invoke your MyCommand command:

commandsWithEvents

This UI may be new to you if you’ve never played with Behaviors that expose Commands. With commands, notice that I am not stuck with just one Trigger. I am able to add multiple triggers that can each call the same MyCommand function, and just like with Actions, I have the ability to modify the properties of my trigger as well!

How this Works Behind the Scenes
What you see in the Blend UI is a major abstraction of what goes on behind the scenes. To help you understand what is happening better, the following diagram gives you an overview of how commands and Behaviors work together to give you Action-like abilities:

ica_commands

Notice that your behavior exposes a command, and there exists an Action called InvokeCommandAction that does nothing but, as it name implies, invoke commands. Since this is an Action, you have the abilities to set triggers on it. All you ever see in the Blend UI is the Command and the Triggers that you want to set on it. Behind the scenes, we map your trigger to the InvokeCommandAction that is associated with the Command.

This means the trigger has no knowledge of the behavior. They are completely decoupled and fit with the self-contained, solo model that I have been describing about Behaviors for the past few posts. Because they interact with Actions that, in turn, interact with Commands exposed by the Behavior, you have the ability to have our behavior do things with some external input just like you can with Actions.

Conclusion
All right. This wraps up my current series of posts describing Behaviors themselves. You may find yourself wondering why you would ever want to use a Behavior when Actions just seem much easier to use and conceptually understand. The answer is, for the most part, rarely.

Unless you want the ability that Behaviors have where you can internally manage state independent of being tied to a trigger, you will find a Trigger and Action pair to be your best friends as you navigate through this sea of interactivity. For a class of interactions, especially those involving state, having a Behavior handy does not hurt either though.

Cheers!
Kirupa :)

Looking at Behaviors, the Class

May 16th, 2009     |     View Comments

In my earlier article a few weeks ago, I described the basics of Triggers and Actions. Triggers and Actions satisfy your standard, run-of-the-mill, cause and effect condition where something happens when you do something:

image

In this post, I am going to talk about the third leg in our three-legged Behaviors table, and it is a behavior itself. A behavior is a bit different than a trigger an action. Where triggers and actions work very closely with each other to have something happen, a behavior is the lone ranger that does not depend on anybody else.

Similar to your trigger and action, a behavior has the ability to do something when it is asked to do something. Unlike a trigger and action, though, a behavior also has the ability to do something whenever it wants to without any external notification from a trigger or trigger-like item.

If I had to redraw the “When ___ happens, do ___” sentence for a behavior, it would be as follows:

behavior_summary

The entire half where a condition needs to be met before something happens is optional. Let’s take a look at the following example where I have a behavior that randomizes the positions of elements inside a Canvas automatically:

public class RandomizeLayoutBehavior : Behavior<Canvas>
{
private Random positionRandom;
 
public RandomizeLayoutBehavior()
{
positionRandom = new Random();
}
 
private void RandomizeContents()
{
Canvas parentCanvas = this.AssociatedObject;
parentCanvas.Loaded += new RoutedEventHandler(parentCanvas_Loaded);
}
 
protected override void OnAttached()
{
base.OnAttached();
 
RandomizeContents();
}
 
protected override void OnDetaching()
{
base.OnDetaching();
}
 
private void parentCanvas_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
Canvas parentCanvas = sender as Canvas;
 
if (parentCanvas != null)
{
// Iterating through each child element inside the canvas and giving it a random position
foreach (UIElement element in parentCanvas.Children)
{
int xPosition = positionRandom.Next((int) parentCanvas.Width);
int yPosition = positionRandom.Next((int) parentCanvas.Height);
 
Canvas.SetLeft(element, xPosition);
Canvas.SetTop(element, yPosition);
}
}
}
}

To make this example work, make sure your behavior is placed on a Canvas that a.) has child elements, and b.) has a set Width and Height. In other words, if your Canvas’s width and height are just Auto, this will not work.

In case you are curious, here is how the Object Tree in Blend looks like with my RandomizeLayoutBehavior applied:

objectTree_behavior

The behavior is pretty straightforward. When your app runs, since it is attached to your Canvas, it looks at each of the Canvas’s children and randomly positions them in a different location.

I am not going to be describing the specifics of this behavior. Instead, the more interesting thing to look at is what is needed to basically create your behavior. The basics of what you need for a behavior are as follows:

public class Behavior1 : Behavior<DependencyObject>
{
public Behavior1()
{
// Insert code required on object creation below this point.
}
 
protected override void OnAttached()
{
base.OnAttached();
 
// Insert code that you would want run when the Behavior is attached to an object.
}
 
protected override void OnDetaching()
{
base.OnDetaching();
 
// Insert code that you would want run when the Behavior is removed from an object.
}
}

You need to extend your class by the Behavior type that we provide as a part of the Microsoft.Expression.Interactivity.dll, and once you have done that, just override the OnAttached and OnDetaching methods.

The code you place in OnAttached is what causes your behavior to run. By default, that is when your behavior is initialized. By not being tied to running only when a trigger tells you to, you have the ability to create interactions that are bit more involved or store state.

Conclusion
Hopefully this post gives you a good overview of how to write your own Behavior. The behavior I showed you in this post runs by default when it gets attached to the parent object. Not all behaviors work that way, and they can actually be made to do something at specific times using a trigger!

In a future post, I will describe how to create a behavior that exposes functionality to be associated with triggers.

Cheers!
Kirupa :)

Looking at Triggers and Actions

April 27th, 2009     |     View Comments

Like I’ve mentioned several times earlier, behaviors in Expression Blend 3 are made up of extensible Triggers, Actions, and Behaviors. What I really have not done is explain in greater conceptual detail what each of those pieces do and how far you can push our default implementation. In this post, let’s fix that by giving Triggers and Actions their long-awaited detailed overview.

In a Nutshell
If I had to sum up what triggers and actions do, the following sentence is it:

trigger_action

Pretty simple, right? Let me take a few steps back and look at a few examples. When I flip on a light switch switch, the light needs to turn on. When I click on a button, a sound needs to play. When I hit a certain time, the application should close. What I have just described are simple cause-and-effect relationships, and in a nutshell, Triggers and Actions are nothing more than digital manifestations of them. The cause (When) is controlled by the Trigger, and the effect (Do) is controlled by the Action:

image

All of the examples involving Actions that I have shown you over the past few blog posts follow a similar pattern:

behaviorsTabAssetLibrary

In the Blend UI, we suppress a lot of the details for you. You only drag and drop an Action onto an element, and the trigger is either automatically set as specified by the Action, or you can set it yourself. That seems simple…sort of!

Sneaky Like a Fox
What makes Triggers and Actions more interesting is how sneaky they actually can be. When you drag an Action onto an element in Blend, your Action is parented under this button:

applyingABehavior

What is worth looking into is what the Trigger and Action are looking at, and let’s assume the Trigger we are looking at is an EventTrigger. Dropping an Action onto the above button will set the Trigger’s source to that of the button itself, and it will also set your Action’s target to be that of the button as well:

 image

What you have the ability to do is set the Source to some other element besides your button.  Let’s say you have a checkbox you want to set as your EventTrigger’s Source instead.

With our current Behaviors implementation, you can simply change the Source of your Trigger to that of your checkbox very easily by setting the Trigger’s SourceName property to that of your checkbox:

image

This means that your Action will do something when the event you are listening for on your CheckBox fires.

You’ve just seen how we can set the source of a trigger, our EventTrigger, to that of another element besides the one your Action is parented under. To make things a bit more interesting, you also have the ability to target your Action to affect an element other than the one  you are parented under as well!

If your Action inherits from TargetedTriggerAction instead of just TriggerAction, you gain access to the TargetName property that allows you to specify which element you want your Action to affect. So you can have something such as the following:

image

Your Action is parented under the Button, but your EventTrigger is actually listening for events from a CheckBox. When the trigger fires, your Action does its thing on a blue rectangle that it is targeting.The actual Button is merely the Action’s host and it derives no benefit from it because neither the Action nor the Trigger care about it. Sad, isn’t it?

Rationale Behind This
Right now, you are probably wondering why we allow you to do something like this. It isn’t as if you have to manually modify XAML for this. We provide UI today that allows you to easily do this. The reason has to to do with different approaches individuals have on how the relationship between a trigger and Action should work.

I tend to like the following approach where my Action is on the element that will be the Source of my trigger:

 image

This is known as the Tell approach because I am telling my Action to affect something else. The other approach is known as Listen, and it is where you place your Action on the element that is being affected as opposed to the Source of the trigger:

image

There is no right or wrong way to approach how you wire up your Triggers and Actions. The thing to note is that our Behaviors API is flexible enough to support both, and our UI makes it easy for you to pick either Listen or Tell without having to do anything crazy.

Cheers!
Kirupa :)

:

Behaviors : Writing Your Own Triggers

April 9th, 2009     |     View Comments

In my previous posts, I briefly discussed what behaviors are and how to use them. Let’s now talk about writing your own behaviors. Behaviors is a catch-all word that describes behaviors, actions, and extensible triggers. For an overview of what all three mean, check out Jeff Kelly’s blog post as well as Christian Schormann’s two overview blog posts on this topic.

Because there is already some great material out there on the basics of behaviors, I am going to shift gears and explain how to write behaviors. First up, I am going to go into some detail in this blog post by showing you how to write your own Trigger!

What are Triggers?
Like I mentioned earlier, behaviors (much like a famed group of musketeers!) are made up of three components – the Behavior, the Action, and the Trigger. The Trigger is really the catalyst for causing an Action and indirectly a Behavior to actually do something.

Examples of triggers range from something common such as an EventTrigger that fires an Action when a mouse or keyboard event is raised to something fun like a CollisionTrigger that fires an Action when two objects collide with each other. The thing to note is that, much like everything in our implementation of behaviors, Triggers are extensible. You can easily create your own kind of crazy triggers…such as what I am going to show you next.

The DoubleClick Trigger
One common gesture that I miss in Silverlight is the ability to double click. You can always click once, but if I want an Action to do something with a double click, I will have to provide this functionality myself. The below code shows you what a sample double click implementation looks like:

namespace TriggerTest
{
public class DoubleClickTrigger : TriggerBase<UIElement>
{
private int count = 0;
private DispatcherTimer _timer;
 
public int DoubleClickSpeed
{
get { return (int)GetValue(DoubleClickSpeedProperty); }
set { SetValue(DoubleClickSpeedProperty, value); }
}
 
public static readonly DependencyProperty DoubleClickSpeedProperty = DependencyProperty.Register("DoubleClickSpeed", typeof(int), typeof(DoubleClickTrigger), new PropertyMetadata(500, DoubleClickSpeedChanged));
 
private static void DoubleClickSpeedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Code for dealing with your property changes
}
 
protected override void OnAttached()
{
base.OnAttached();
 
this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
}
 
void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (count == 0)
{
_timer = new DispatcherTimer();
_timer.Interval = new TimeSpan(0, 0, 0, 0, DoubleClickSpeed);
_timer.Tick += new EventHandler(ResetCount);
_timer.Start();
}
 
count++;
 
if (count >= 2)
{
this.InvokeActions(null);
}
}
 
void ResetCount(object sender, EventArgs e)
{
count = 0;
_timer.Stop();
}
 
protected override void OnDetaching()
{
base.OnDetaching();
 
this.AssociatedObject.MouseLeftButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
}
}

When this trigger is used on an Action, double clicking on the element your Action is listening to will cause it to fire. I have a property called DoubleClickSpeed that allows you to adjust how quick or slow the double click needs to be before your two clicks are registered as a double click.

Looking Briefly at the Code
While it seems like a lot of code, what I am doing is actually pretty straightforward. Instead, I am going to remove all of the double click specific functionality and just show you the bare minimum of what you need to create a trigger:

public class Trigger1 : TriggerBase<DependencyObject>
{
protected override void OnAttached()
{
base.OnAttached();
}
 
protected override void OnDetaching()
{
base.OnDetaching();
}
 
//
// To invoke any associated Actions when this Trigger gets called, use
// this.InvokeActions(o)
//
}

Notice that I am extending my class from TriggerBase – a type that is provided for you inside Microsoft.Expression.Interactivity.dll. Once I extend from the TriggerBase class, all I really have left to do is just override the OnAttached and OnDetaching methods like I show above.

Think of your OnAttached and OnDetaching methods like the constructor and destructor of your class. Anything you want initialized when the Trigger gets associated with an Action, be sure to specify it in the OnAttached method. Anything you want removed or cleaned up when the Trigger is no longer being associated with anything, be sure to put that in your OnDetaching method.

Finally, to actually have your trigger fire, though, you need to make a call to the InvokeActions method. Only when InvokeActions gets called will any Actions associated with this Trigger be notified to do whatever it is they do. My DoubleClickTrigger is simply an extension of the few lines of code that make up a barebones trigger starting point.

Using Triggers in Blend
Once you have written your trigger, using them in your own project is pretty straightforward. Draw a simple element, such as a rectangle, and drag and drop an Action on to it. If you don’t have any Actions you can use, feel free to borrow some from your peers on the Expression Gallery’s Behaviors category.

When you apply an Action to an element and select it, your Properties Inspector will display the current trigger applied as well as any properties your Action may expose. You can click the New button inside the Triggers category to display a dialog that allows you to pick your DoubleClickTrigger:

trigger_editor

Once you have selected your DoubleClickTrigger, click OK to accept it as the Trigger for your Action. After you do that, your Trigger category will display the things relevant to you customizing your DoubleClickTrigger:

Trigger_DoubleClick

Well, that is all there is to it. Hopefully this post gave you a quick overview of how to write your own Triggers for this extensible family we call Behaviors.

Update: Thanks to a comment from Bart, I made a simple change to the code.

Cheers!
Kirupa :)

Dependency Property Generator Article

March 29th, 2009     |     View Comments

A few weeks ago, I wrote a dependency property generator for Silverlight. Overall the feedback in my inbox has been great, but one thing many of you wanted was to learn more about what to do once the code gets generated. At first glance, it may not be obvious where exactly to paste the code or what the meaning of the “Owning Class” really means.

To help address some of those issues, I decided to promote my generator from the blog and put them on the main site itself: http://www.kirupa.com/blend_silverlight/dependency_property_generator.htm

Not only do you still have the dependency property generator, you also have a fair amount of textual information that can help answer some of those open questions a bare code snippet may not provide hints for.

Cheers!
Kirupa :)

Using Behaviors : A Quick Walkthrough

March 19th, 2009     |     View Comments

In my previous post, I provided a brief introduction to Behaviors and how they can be used to simplify tasks that were once complicated. My post was deliberately vague on details, so this post will start a series of posts on using behaviors as well as writing your own behaviors. Let’s first look at using behaviors from the point of view of a casual user.

1. Download and Install Expression Blend 3 Preview
First, if you haven’t done so already, download and install Expression Blend 3 Preview. The download takes a bit of time, but the installation should be a breeze.

2. Create a new Silverlight 3 Project
Once you have Expression Blend 3 installed, launch it and create a new Silverlight 3 Application + Web Site via the New Project dialog:

newprojectidialog

Give your project a descriptive name if you want, but it is certainly not mandatory!

3. Draw out a Button
With your project created, draw out a small button and give it the text “Click Me:.

myButton

You can change your button’s text by selecting it and modifying the Content property in the Properties Inspector.

4. Downloading Some Behaviors
You now have a simple app with a button that says Click Me. Too bad clicking on the button doesn’t do anything right now. Let’s change that. What you are going to do is download some sample behaviors I have already created.

Go to the following link and download the behaviors archive to your hard drive: http://gallery.expression.microsoft.com/SampleSLBehaviors

Once you have downloaded the archive, extract the files to a location you can easily access. There will be a SilverlightPreviewBehaviors folder with a References and Project Files folder. Inside your References folder, there will be two files – Microsoft.Expression.Interactivity.dll and SLPreviewBehaviors.dll:

twoAssemblies

5. Adding a Reference to Behaviors
Now that you have downloaded and extracted some behaviors, it’s time to get Blend to recognize it. Go back to your Blend project, find the Project pane, right-click on your References folder, and select the Add Reference menu item:

addingReference

The Add Reference dialog will appear. Browse to the location of your earlier extracted Assembly folder and select the Microsoft.Expression.Interactivity.dll and SLPreviewBehaviors.dll. Once you have selected these DLLs, click Open to add references to these DLLs to your project.

You’ll know you have succeeded when your References folder in the Project contains the names of the two DLLs you added:

image

Note:
We have an easier way of doing this step, but I (or another Blender) will describe that at a later date.

6. Applying a Behavior
With the references added, Blend now is aware of what is contained inside the two DLLs. Launch your Asset Library and navigate to the Behaviors tab. You will see a list of behaviors that you can pick and choose from:

behaviorsTabAssetLibrary

The behavior I want you to use is called HyperlinkAction. Drag and drop your HyperlinkAction from the Asset Library and drop it onto the button on your artboard:

applyingABehavior

Once you have dropped your behavior onto the element, you will notice that your behavior now has focus with properties of the behavior displayed in the Properties Inspector.

7. Modifying Behavior Properties
Right now, your properties inspector will be displaying all of the properties your HyperlinkAction exposes:

hyperlinkProperties

Because HyerlinkAction is the Action variant of a Behavior, the Properties Inspector displays the Trigger properties first followed by any custom properties this behavior exposes. I will explain what the “Action variant of a Behavior” actually means in the future.

The first category you will see is Trigger. A trigger determines what exactly will cause your behavior to fire. In our case, the trigger we are using is EventTrigger, and it will fire when the Loaded event is fired. We probably want this behavior to fire when someone actually Clicks on the button, so click on the EventName drop-down and select Click from the list:

changingToClickEvent

Your behavior will now only trigger when the button is clicked because you changed your EventTrigger’s event to be Click.

The final thing you can change is the URL that we launch when you click. You can change the TargetURL property to something else other than the default value of http://www.live.com.

8. Running your App
The final (and probably the easiest!) step is actually running your app. Simply hit F5. Once your Silverlight application loads in your browser, click on the button you created and attached a behavior to. When you click on the button, the URL you specified earlier will launch. That’s it!

Conclusion
One of our goals with behaviors is to make it very easy for someone to find a behavior and use them in their project. The second goal is to be able to modify the properties of a behavior easily by reusing one’s existing knowledge of the properties inspector. Hopefully, as you can see, finding a behavior from our gallery and using them in a project should be pretty straightforward. At the very least, I hope it is easier than you wiring up the event handler and writing some code yourself :)

Cheers!
Kirupa :)