Dependency Property gotchas

Here’s a quick list of gotchas I’ve learnt the hard way or seen over the time while working with Dependency Properties in Silverlight and WPF (most of this applies to Attached Properties as well).
Here’s a typical dependency property defined on a class inheriting from DependencyObject:

public int SomeValue
{
    get { return (int)GetValue(SomeValueProperty); }
    set { SetValue(SomeValueProperty, value); }
}
public static readonly DependencyProperty SomeValueProperty =
    DependencyProperty.Register("SomeValue", typeof(int), typeof(ownerclass), null);
private static void OnSomeValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ownerclass obj = (ownerclass)d;
    int newValue = (int)e.NewValue;
    int oldValue = (int)e.OldValue;
} 

Naming

The naming is very important. Note ‘SomeValue’ is not only the name of the property, but the static DependencyProperty field is prefixed with the word ‘Property’, and the string in the first parameter also must match this name.

Type and default values.


Below we instead create a dependency property of type double and define a default value in PropertyMetadata to 0. See if you can spot the bug:

public static readonly DependencyProperty SomeValueProperty =
    DependencyProperty.Register("SomeValue", typeof(double), typeof(ownerclass), new PropertyMetadata(0));

The problem is very subtle, and the error you will get at runtime is not always obvious. The reason is that PropertyMetadata takes an object as parameter, and there’s no way for the debugger to know that you meant to put a double there, and it will wrongly assume Int32. At runtime you will get an exception because of this. Instead make sure you explicitly declare the default value as a double:

public static readonly DependencyProperty SomeValueProperty =
    DependencyProperty.Register("SomeValue", typeof(double), typeof(ownerclass), new PropertyMetadata(0d));

Default values are static – Beware of object references!

Don’t ever ever EVER declare a default value in a DependencyProperty that is not a value type. Since this field is static, the default value will be shared among every instance that’s using the default value.

If I were to access the property and modify its sub properties, anything using this instance would be affected.
Example: Brush property on a control. I have multiple instances of the control, but changing a subproperty on the property will affect all the controls:

public static readonly DependencyProperty MyBrushProperty =
    DependencyProperty.Register("MyBrush", typeof(SolidColorBrush), 
    typeof(ownerclass), new PropertyMetadata (new SolidColorBrush(Colors.Red))); 

. . . 

myControl.MyBrush.Color = Colors.Blue;

Instead set the default value to null. Then either set the property in the default style template (if it’s declared on a control), or set it in the constructor of your class.

Code in property setter

Often you are used to execute code in the setter of your property when a value changes like so:

public int SomeValue
{
    get { return (int)GetValue(SomeValueProperty); }
    set 
    { 
        if(value != SomeValue)
        {
            SetValue(SomeValueProperty, value); 
            UpdateValues(value);
        }
    }
}

However, if you’ve done this, you might have seen that sometimes your setter is not called, and the UpdateValues() method is never executed. The reason is that if you are binding to this property instead of just calling the setter directly, this setter will never be hit! Instead Silverlight/WPF will call SetValue(…) directly and “circumvent” your setter (this is also why the naming mentioned above is important so it can find the property).
Instead the correct way is to declare a method to call when the value changes. This is done in the PropertyMetadata instance: Also note that you do not need to check whether the value actually has changed. The changed handler will only be called if the value is actually different.

public static readonly DependencyProperty SomeValueProperty =
    DependencyProperty.Register("SomeValue", typeof(int), typeof(ownerclass), new PropertyMetadata(0, OnSomeValuePropertyChanged));
private static void OnSomeValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ownerclass obj = (ownerclass)d;
    int newValue = (int)e.NewValue;
    obj.UpdateValues(newValue);
}

Code Syntax Highlighting in Silverlight

A little while back, Jeff Wilcox wrote an awesome syntax highlighter (blog post here). It enables you to display code in Silverlight with similar syntax highlighting that we know and love from Visual Studio. This is really useful if you want to display some code samples inside your Silverlight application.

However it had one major feature missing: You couldn’t select and copy the code. And what good are code samples if you can’t do that (unless you’re the patient type who likes typing a lot). Vladimir Horbovanu had commented that he had modified the sample to be built on MVVM and added the option of selecting and even editing the text. However, this only supported C# and not VB, JS, Xaml, C++ and Xml as the original sample did, and there were a few issues with getting the text to properly align. However the “trick” he applied was simple but clever: Put a TextBox on top of the existing control, and make it transparent.

So I basically took Jeff’ awesome library, modified the template a bit, bound a TextBox with transparent text to the source code and put it on top of the existing template parts, tweaked the TextBox template to properly align and voila: The SyntaxHighlightingTextBlock is now a SyntaxHighlightingTextBox. I also added a couple of properties to the control to quickly get the selected text, set readonly state etc.

Jeff deserves most of the credit for this one, and Vladimir the rest for the neat TextBox trick. All I did was put it together, clean it up a bit and fix a couple of bugs in the highlighter. Expect to see this used in the next release of our SDK.

You can get the source here, and view the sample app here.

The library still only requires Silverlight 3, however the demo app uses Silverlight 4’ right-click feature for the context menu.

image

Update: Vlad took this sample further and improved rendering performance when editing larger amounts of code. See his updated blogpost here: http://www.vladhorby.com/blog/post/2010/11/01/code-editor-control-with-syntax-highlighting-for-silverlight-available.aspx
Isn't it great how the .NET community works together on building some useful stuff? Thanks Vlad!

Add MouseWheel scrolling to Silverlight's DataGrid

In my previous post, I wrote about how you could add MouseWheel scrolling to the ScrollViewer using a simple behavior. I wanted to have the same MouseWheel scroll on the DataGrid, and thought that it could be done the exact same way.

However, it turns out that the DataGrid is not using a ScrollViewer, but instead used a Virtualized panel that is re-rendered every time a ScrollBar is scrolled. Unfortunately the DataGrid only reacts to the Scroll event of the ScrollBar, and not it’s Value property (not sure why they did it like this), so it’s not just a matter of finding the ScrollBar and increase the value. If I did that, the ScrollBar would move, but the DataGrid would not update. In fact the only way to raise the Scroll event was to click the ScrollBar, but I wanted to do this in code so I can’t really “click” something.

Fortunately Silverlight has a UIAutomation framework, that allows you to simulate user interaction. This is great for running automated tests against your controls, where you want to simulate a user clicking and typing inside your application. This got me thinking that the way to raise the Scroll event was simply to use the UIAutomation framework to virtually ‘click’ the ScrollBar. So in other words, each time you scroll down, the code ‘clicks’ the down button in the ScrollBar, and vice versa. While this simulated click approach works well, there’s an even simpler way, since the DataGrid’s Automation allows for calling scroll, which is what I’ll be using here.

The first step is to get a reference to the AutomationPeer of the DataGrid:

AutomationPeer automationPeer = FrameworkElementAutomationPeer.FromElement(this.AssociatedObject);

Since the automation peer for the DataGrid implements the IScrollProvider pattern, we can simply request that pattern and call Scroll() to scroll the DataGrid:

IScrollProvider scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
ScrollProvider.Scroll(ScrollAmount.NoAmount, ScrollAmount.SmallIncrement);

The rest is trivial for the DataGrid MouseWheel behavior. On the MouseWheel event of the DataGrid we call the Scroll method with either a SmallIncrement or SmallDecrement, and the DataGrid will scroll one row. It’s not as smooth as the ScrollViewer implementation in the previous blogpost, but at least it allows us to use the MouseWheel to scroll the rows. Since most scrollable controls in Silverlight already has UIAutomation and IScrollProvider support, this behavior will also work on other scrollable controls, and not only the DataGrid.

So here’s what it looks like in XAML when you included a reference to the behavior assembly:

<data:DataGrid x:Name="datagrid" Height="100" >
	<i:Interaction.Behaviors>
		<behaviors:MouseScrollBehavior/>
	</i:Interaction.Behaviors>
</data:DataGrid>

I’ve updated the source code for my Behavior library, and also updated the sample application to include the DataGrid.

Silverlight Behaviors, Triggers and Actions

My recent new love when doing Silverlight Development is Expression Blend’s Interactivity API that gives you some really neat support for attaching behaviors, triggers and actions to your elements, which in turn lets you structure your code better and allows you to reuse it more often (It boggles my mind why these are not part of core Silverlight).

You can download the Expression Blend SDK for free here, but you also get it with Expression Blend 3.0.

How often haven’t you done something like this:

<Button OnClick=”ShowToolsWindow” Content=”Show Tools” />

and in the code behind:

private void ShowToolsWindow(object sender, RoutedEventArgs e)
{
            ToolsWindow.Visibility = Visibility.Visible;
}

While this probably isn’t much code, imagine a whole toolbar or menu each with code in two places, and especially the code-behind more or less the exact same, but with a different target. And if I remove a button, I also have to remember to remove the eventhandler. Enter triggers and actions…

With the Expression SDK we can create a trigger that does this for us.

public class ToggleVisibilityAction : TargetedTriggerAction<UIElement>
{
	protected override void Invoke(object parameter)
	{
		this.Target.Visibility = 
			this.Target.Visibility == Visibility.Visible ?
				Visibility.Collapsed : Visibility.Visible;
	}
}

What this allows us to do is the following in xaml and without using any code-behind:

<Button Content="Show Tools" Margin="20" >
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click" >
			<behaviors:ToggleVisibilityAction TargetName="ToolsWindow" />
		</i:EventTrigger>
	</i:Interaction.Triggers>
</Button>

While this might be a little more XAML than before, it cleanly separates the button into one little local piece, and also allows me to reuse the same behavior for multiple tools.

In the above case, I’m using a TargetedTriggerAction, meaning that it is triggered by one control, but acts on another. There are also non-targeted actions. For instance this one for toggling full screen:

public class ToggleFullScreenAction : TriggerAction<UIElement>
{
	protected override void Invoke(object parameter)
	{
		Application.Current.Host.Content.IsFullScreen = 
			!Application.Current.Host.Content.IsFullScreen;
	}
}
<Button Content="Toggle Fullscreen" >
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click" >
			<behaviors:ToggleFullScreenAction />
		</i:EventTrigger>
	</i:Interaction.Triggers>
</Button>

Another more powerful item, is the behaviors. Behaviors allows us to add any logic to the control (or the application) it is attached to, when it gets attached. Basically there are two methods to override when creating a behavior: OnAttached and OnDetached. Usually this is where you will want to attach and detach eventhandlers to the element.

Here is one example that will hide an element if the application gets installed:

public class HideOnInstall : Behavior<UIElement>
{
	protected override void OnAttached()
	{
		base.OnAttached();
		Application.Current.InstallStateChanged += Current_InstallStateChanged;
	}

	protected override void OnDetaching()
	{
		base.OnDetaching();
		Application.Current.InstallStateChanged -= Current_InstallStateChanged;
	}

	private void Current_InstallStateChanged(object sender, EventArgs e)
	{
		this.AssociatedObject.Visibility = (Application.Current.InstallState == InstallState.Installed)
			? Visibility.Collapsed : Visibility.Visible;
	}
}

Now we can use that on our install button, so when the app has been installed, the button will automatically hide itself:

<Button Content="Install">
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click" >
			<behaviors:InstallAction />
		</i:EventTrigger>
	</i:Interaction.Triggers>
	<i:Interaction.Behaviors> 
		<behaviors:HideOnInstall />
	</i:Interaction.Behaviors>
</Button>

A thing that has always bothered me is that the ScrollViewer control by default doesn’t listen to MouseWheel events. Delay recently blogged about adding smooth scrolling to the scroll viewer. Based on that, I changed this to be a behavior that listens to the MouseWheel event.

To do this we first create a behavior that inherits from Behavior<ScrollViewer>. This ensures that the behavior can only be attached to a ScrollViewer. In OnAttached we start listening for the MouseWheel event:

protected override void OnAttached()
{
       AssociatedObject.MouseWheel += AssociatedObject_MouseWheel;
       base.OnAttached();
}

In the event handler we just call AssociatedObject.ScrollToVerticalOffset(offset) to apply the scroll (The sample you can download here goes a little further and uses a DoubleAnimation with easing to make the scrolling more smooth). Now to add MouseWheel scrolling support to all scroll viewers, all you need to do is add the behavior to the scroll viewer:

<ScrollViewer>
	<i:Interaction.Behaviors>
		<behaviors:MouseScrollViewer />
	</i:Interaction.Behaviors>
</ScrollViewer>

The neat thing about these behaviors is that you get drag’n’drop behavior inside Expression Blend. This means that you can simply drag these from the Assets window right on to your elements without having to touch any code or XAML. Here’s what my sample application looks like in the objects and assets windows:

image

When I migrated this application from Silverlight 2 to 3, I managed to remove almost 70% of the code-behind in page.xaml.cs mostly caused by reusing simple actions and behaviors, and separating the logic into reusable classes instead. Later I found myself often going back to grab these classes and re-use them for other projects.

You can try out the sample application here and download the code here.

ESRI ArcGIS Silverlight/WPF API released

Last week we finally released v1.0 RTW of our Silverlight/WPF API. Today at our User Conference in San Diego, Jack Dangermond announced, that it even will be free for non-commercial use. The API will run on both v2 and v3 of Silverlight.

Furthermore today we released the entire source code for the toolkit assembly on CodePlex: http://esrisilverlight.codeplex.com

Also today at the Windows Partner Conference, we announced MapIt, a mapping solution running on a combination of SQL Server 2008, IIS and Silverlight/WPF and also with integration into Sharepoint. You can download a free 60-day evaluation of MapIt today. I encourage you to go see some of the demonstration videos – especially if you are new to GIS, this video could be an eye-opener to what information is hidden in your existing business intelligence.

If you are at the ESRI User Conference this week, come by the Silverlight Island in the showcase area and talk some Silverlight. I’ll be there most of the time Tuesday through Thursday. I’ll also be joining Rex Hansen and Art Haddad at the .NET SIG Tuesday evening, and the Silverlight intro sessions Wednesday and Thursday. Also watch Rich’s Demo Theater session which will demonstrate the ESRI Map Web Part for SharePoint and MapIt (the title on this session is incorrect).

Silverlight 3 Released

Silverlight 3 RTW is finally out !

Find the downloads here:

Expression Blend 3 with Sketchflow

Silverlight 3 Software Development Kit (SDK)

Silverlight 3 Tools for Visual Studio 2008 SP 1

or just go here to install the plugin: http://www.microsoft.com/silverlight/resources/install.aspx

While most of the new SL3 features has already been announced with the beta release, below are some of the new stuff that wasn’t available in the beta.

Mouse Wheel support !

We finally get native mouse wheel support with Silverlight 3. This means support for wheel in full screen, out of browser, when the DOM bridge is disabled, and most importantly no more ugly JavaScript hacks to get this working. The API is exactly the same as in WPF.

image 

 

Multi Touch Support.

Scott Guthrie announced this feature a few months ago, but it wasn’t included in the beta. Below is what this new API looks like:

image

Compared to the Surface API it looks like a fairly low-level version. The touch points you get will tell you if the user started touching, let go or is moving the finger, and also has methods for getting the element that is directly under the touch point. It’s up to the developer to then convert these gestures to something meaningful. The new behaviors API might come in handy for streamlining this.

Our prototype lab has already created some cool demos using Microsoft’s Surface table and our WPF API, and hopefully we can easily extend that to our Silverlight API as well.

 

Support for Alpha in 8bit PNG.

To me this is huge! We can finally start optimizing the download size by using PNG8 images and still have an alpha channel. Unfortunately Microsoft forgot 1, 2 and 4 bit, which a lot of optimized PNGs end up being. Hopefully that’ll come in a later version (judging from the PNG spec, there really isn’t much difference between 1, 2, 4 and 8 bits).

Out of browser changes

Out of browser configuration has been changed, and can now be configured using the UI tool in Visual Studio project properties:

image

Clear-type support

All browsers now get clear-type rendering of fonts.

Adding right click to Silverlight

In an earlier post, I created a small extension library that added several mouse gestures to Silverlight. However, the right-click only worked in Internet Explorer, and the solution was a bit of a hack. So to continue down the road of hacks, I found that there is a way to prevent the Silverlight Plugin from getting the right click event. By inserting a <div> on top of the plugin when the right mousebutton down event fires, you can prevent the mozilla browsers from getting the rightclick event. The mouseup event is then fired on this overlay div, and we remove the overlay again when the button is released or the mouse moves. Ugly but it works... The approach still requires the plugin to run in windowless mode.

Of course whether you even should be preventing the user from getting to the silverlight context menu in the first place is a whole different discussion.

I've updated the demo page, source and binary:

Try a demo!

Download binary (7 kb)

Download source (25 kb)

Using surrogate binders in Silverlight

In WPF you can bind values to any property on a DependencyObject, however in Silverlight, you can only bind to FrameworkElements. Most often when I have hit this roadblock is when I want to bind the rotation or scale of an element to a value. For instance a compass direction bounded to the current heading.

In WPF this would look like this:

<Image>
<Image.RenderTransform >
<RotateTransform Angle="{Binding Path=Heading}" />
</Image.RenderTransform>
</Image>

Unfortunately, since RotateTransform isn’t a FrameworkElement, this won’t work in Silverlight.

Enter: Attached Properties.

Attached properties are really neat when you start getting to know them, and you can do some pretty cool stuff, and still stick to all the MVC glory that the above case prevents you from. Using an custom attached property that manages rotation, I could for instance write:

<Image local:SurrogateBinder.Angle="{Binding Path=Heading}">
<Image.RenderTransform >
<RotateTransform  />
</Image.RenderTransform>
</Image>

So what does this binder look like? The first step is to declare the actual attached property, as well as a get and set method. Note that the naming of the property and the two get and set methods are important for this to work.
 
public static class SurrogateBinder
{
public static readonly DependencyProperty AngleProperty =
DependencyProperty.RegisterAttached("Angle", typeof(double),
typeof(SurrogateBinder),
new PropertyMetadata(OnAngleChanged));
public static double GetAngle(DependencyObject d)
{
return (double)d.GetValue(AngleProperty);
}
public static void SetAngle(DependencyObject d, double value)
{
d.SetValue(AngleProperty, value);
}
}

The next step is to react to when this value is being set/changed. The property declaration above references an OnAngleChanged method to call when the property changes. The idea is that when the value changes, we grab the rotate transform and set the value.
private static void OnAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is UIElement)
    {
        UIElement b = d as UIElement;
        if (e.NewValue is double)
        {
            double c = (double)e.NewValue;
            if (!double.IsNaN(c))
            {
                if (b.RenderTransform is RotateTransform)
                    (b.RenderTransform as RotateTransform).Angle = c;
                else 
                    b.RenderTransform = new RotateTransform() { Angle = c };
            }
        }
    }
}

In this case, the property binder is only made to work with a UIElement that wants a rotation applied. But by using a little reflection magic, we can create a completely generic binder that can set any property.
Instead of using one attached property, we use two. One for the value to bind, and another for the property to bind to. This is very similar to when you are creating animations and you both set the target you are animating and the property you are animating on. Apart from the Reflection magic, the code is pretty much the same:
 
public static class SurrogateBind
{
    public static readonly DependencyProperty TargetProperty =
        DependencyProperty.RegisterAttached("Target", typeof(string), typeof(SurrogateBind), null);
 
    public static string GetTarget(DependencyObject d)
    {
        return (string)d.GetValue(TargetProperty);
    }
 
    public static void SetTarget(DependencyObject d, string value)
    {
        d.SetValue(TargetProperty, value);
    }
 
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.RegisterAttached("Value", typeof(object), typeof(SurrogateBind),
        new PropertyMetadata(OnValueChanged));
 
    public static object GetValue(DependencyObject d)
    {
        return (object)d.GetValue(ValueProperty);
    }
 
    public static void SetValue(DependencyObject d, object value)
    {
        d.SetValue(ValueProperty, value);
    }
 
    private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        string path = GetTarget(d);
        if (String.IsNullOrEmpty(path)) return;
        string[] pathElements = path.Split(new char[] { '.' });
        PropertyInfo propertyInfo = null;
        object o = d;
        for (int i = 0; i < pathElements.Length; i++)
        {
            if (o == null) break;
            string s = pathElements[i];
            int begin = s.LastIndexOf('[');
            bool isIndexed = s.EndsWith("]") && begin >= 0;
            propertyInfo = o.GetType().GetProperty(isIndexed ? s.Substring(0, s.LastIndexOf('[')) : s);
            if (propertyInfo == null) break;
            if (i < pathElements.Length - 1)
            {
                object[] index = null;
                if (isIndexed)
                {
                    index = new object[] { int.Parse(s.Substring(begin + 1, s.LastIndexOf(']') - begin - 1)) };
                }
                o = propertyInfo.GetValue(o, index);
            }
        }
        if (propertyInfo != null && propertyInfo.PropertyType == e.NewValue.GetType())
        {
            propertyInfo.SetValue(o, e.NewValue, null);
        }
    }
}

This allows us to to the same thing in XAML:
<TextBox Text="Hello World"
binders:SurrogateBind.Value="{Binding Path=Heading}" 
binders:SurrogateBind.Target="RenderTransform.Angle" >
<TextBox.RenderTransform>
<RotateTransform />
</TextBox.RenderTransform>
</TextBox>

Or if we have nested controls:
<TextBox RenderTransformOrigin="0.5,0.5"
Text="Hello Universe!"
binders:SurrogateBind.Value="{Binding Path=MoreValues.Heading}" 
binders:SurrogateBind.Target="RenderTransform.Children.Item[1].Angle" >
<TextBox.RenderTransform>
<TransformGroup>
<ScaleTransform />
<RotateTransform />
</TransformGroup>
</TextBox.RenderTransform>
</TextBox>

This approach can actually be extended to invoke methods on your control. For instance when a value changes, you can trigger a storyboard to start playing etc. This is partly something we will get with Triggers in Silverlight 3.0, but if you can’t wait, this is one way to do it. Maybe I’ll cover this in a later blogpost. but for now you can download the source-code and example here:
 

Extending Silverlight’s mouse events

If you just want the extension method library or source, jump to the bottom. If not, read on…

Silverlight is pretty limited when it comes to mouse events. Out of the box, you only have five events you can subscribe to on a given element:

Notice that there is no such thing as click, double-click, drag, right-click and mouse-wheel.

Click and double click you can create from the up/down events. If the mouse doesn’t move between up and down, its a click, and if it happens two times within a timespan, it’s a double-click. Similarly, a drag is a click with mouse movement in-between down and up. Using extension methods, we can easily extent the UIElement class with two new methods for each event: Attach[eventname] and Detach[eventname], that takes care of this tracking. I earlier described this approach for defining a double click extension. These are all types of gestures we often need, so creating a reusable library with these extensions is a no-brainer.

When it comes to mouse wheel, it gets slightly trickier. We can’t do this using existing Silverlight events. Instead we can use the JavaScript bridge to detect wheel events in the browser, and bubble them into the plugin. First we listen to the JavaScript event on the entire plugin, and then inside Silverlight we use the Enter/Leave events to track whether the mouse is over the element you are listening to events on. However there are cases where this wouldn’t work:

  • The application is running in full screen.
  • The Silverlight plugin is running with HTML access explicitly disabled.
  • The .xap file is hosted on a different domain than the page hosting it.
  • You are running the application in Silverlight 3’s Out-of-browser mode.

All of them are cases where the JavaScript bridge is disabled.

Lastly right-click. This is a whole different story. When you right-click a Silverlight page, it will should you a small context menu with a link to the Silverlight settings. This can’t be overridden, but using the same approach as for Wheel, you can intercept the event using JavaScript and prevent the bubbling to the plugin. However this only works reliably in Internet Explorer, and most secondly only if you run the application in windowless mode.

UPDATE: Right-click now also work in Mozilla browsers. See here

I took all these events, and wrapped the, into a little set of extension method, so you don’t have to write the code over and over again.

To use it, download the library (link at the bottom) and add a reference to the DLL. Then at the top of the class where you want to use the event extensions, add the following to your using statements:

using SharpGIS.MouseExtensions;

When you have done this, you will instantly get new intellisense on any object that inherits from UIElement:

image

You will see seven new Attach* and Detach* methods:

Click Mouse/Down without moving the mouse
DoubleClick Two quick clicks
Drag Mouse down, move, mouse up. Event will fire for each mouse movement.
Hover If the mouse stops over an element and stays in the same place for a short time.
KeyClick Click while the user is holding down a key.
RightClick Windowless only!
Wheel Mouse wheel

You can see a live sample of it in action here.

Assembly Library that you can reference in your project: Download binary (7.14 kb)

Sourcecode and sample application is available here:Download source (24.79 kb)

REALLY small unzip utility for Silverlight

UPDATE: See this updated blogpost.

There are quite a few libraries out there that adds zip decompression/compression to Silverlight. However, common to them all is that they add significantly to the size of the resulting .xap.

It turns out that Silverlight 2.0 already has zip decompression built-in. It uses this to uncompress the .xap files which really just are zip files with a different file extension.

There are several blog posts out there that will tell you how to dynamically load a XAP file and load it. It turns out that if you use the same approach with almost any other zip file, you can actually do the same thing, even though this is not a Silverlight XAP. I don’t think this was the original intent. but its still really neat! Here’s how to accomplish that, based on a zip file stream:

public static Stream GetFileStream(string filename, Stream stream)
{
    Uri fileUri = new Uri(filename, UriKind.Relative);
    StreamResourceInfo info = new StreamResourceInfo(stream, null);
    StreamResourceInfo streamInfo = System.Windows.Application.GetResourceStream(info, fileUri);
    if (streamInfo != null)
        return streamInfo.Stream;
    return null; //Filename not found or invalid ZIP stream
}

However, the problem is that this requires you to know before-hand what the names of the files are inside the zip file, and Silverlight doesn’t give you any way of getting that information (Silverlight uses the manifest file for reading the .xap).

Luckily getting filenames from the zip is the easy part of the ZIP specification to understand. This enabled us to create a generic ZIP file extractor in very few lines of code. Below is a small class utility class I created that wraps this all nicely for you.

public class UnZipper
{
    private Stream stream;
    public UnZipper(Stream zipFileStream)
    {
        this.stream = zipFileStream;
    }
    public Stream GetFileStream(string filename)
    {
        Uri fileUri = new Uri(filename, UriKind.Relative);
        StreamResourceInfo info = new StreamResourceInfo(this.stream, null);
        StreamResourceInfo stream = System.Windows.Application.GetResourceStream(info, fileUri);
        if(stream!=null)
            return stream.Stream;
        return null;
    }
    public IEnumerable<string> GetFileNamesInZip()
    {
        BinaryReader reader = new BinaryReader(stream);
        stream.Seek(0, SeekOrigin.Begin);
        string name = null;
        while (ParseFileHeader(reader, out name))
        {
            yield return name;
        }
    }
    private static bool ParseFileHeader(BinaryReader reader, out string filename)
    {
        filename = null;
        if (reader.BaseStream.Position < reader.BaseStream.Length)
        {
            int headerSignature = reader.ReadInt32();
            if (headerSignature == 67324752) //PKZIP
            {
                reader.BaseStream.Seek(14, SeekOrigin.Current); //ignore unneeded values
                int compressedSize = reader.ReadInt32();
                int unCompressedSize = reader.ReadInt32();
                short fileNameLenght = reader.ReadInt16();
                short extraFieldLenght = reader.ReadInt16();
                filename = new string(reader.ReadChars(fileNameLenght));
                if (string.IsNullOrEmpty(filename))
                    return false;
                //Seek to the next file header
                reader.BaseStream.Seek(extraFieldLenght + compressedSize, SeekOrigin.Current);
                if (unCompressedSize == 0) //Directory or not supported. Skip it
                    return ParseFileHeader(reader, out filename);
                else
                    return true;
            }
        }
        return false;
    }
}

Basically you create a new instance of the UnZipper parsing in the stream to the zip file. The method “GetFileNamesInZip” will provide you with a list of the file names available inside the file, that you can use to reference the file using “GetFileStream”.

Below is a simple example of using this. The contents of each file will be shown in a message box:

private void LoadZipfile()
{
    WebClient c = new WebClient();
    c.OpenReadCompleted += new OpenReadCompletedEventHandler(openReadCompleted);
    c.OpenReadAsync(new Uri("http://www.mydomain.com/myZipFile.zip"));
}
 
private void openReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    UnZipper unzip = new UnZipper(e.Result);
    foreach (string filename in unzip.GetFileNamesInZip())
    {
        Stream stream = unzip.GetFileStream(filename);
        StreamReader reader = new StreamReader(stream);
        string contents = reader.ReadToEnd();
        MessageBox.Show(contents);
    }
}

Note that some ZIP files which doesn't report file size before the file content is not supported by Silverlight, and is therefore also ignored by this class. This is sometimes the case when the zip file is created through a stream where the resulting file size is written after the compressed data. If you are dealing with those kind of zip files (seems fairly rare to me), you will need to use a 3rd party zip library that supports this.
UPDATE: See this updated blogpost.

This class will hardly add much to your resulting .xap, and I think it will cover 95% of the use cases when working with zip files.

Download class file: UnZipper.zip (1.25 kb)