Creating Dynamic Windows 7 Taskbar Overlay Icons, the MVVM Way

WPF, MVVM Comments

metrotwit

Since Windows 7 the icon of an application can get an overlay bitmap. You can use that to indicate some state of the application, or–like MetroTwit–to show the number of unread items.

Overlay Icon in WPF

In WPF, the API is pretty simple:

<Window.TaskbarItemInfo>
    <TaskbarItemInfo 
        Overlay="{StaticResource ResourceKey=MyOverlayImage}" />
</Window.TaskbarItemInfo>

However, in one of my projects I have to display dynamic text in the overlay, similar to MetroTwit, but above example only shows a static resource.

While searching in the internet I found Pete Brown’s article Creating Dynamic Windows 7 Taskbar Overlay Icons. He uses a WPF DataTemplate to define the content of the overlay, and in his code-behind he takes that template, renders it to a bitmap and assigns it to the TaskbarItemInfo’s Overlay property. See his article for the detailed steps.

Though I think Pete’s solution pretty clever, it lacks the separation of logic and presentation. In my application I don’t want to create images in the code-behind, code-aside, whatever. It follows the MVVM pattern, so the creation of the overlay image shouldn’t be the concern of my viewmodel.

Solution

Extending TaskbarItemInfo doesn’t work because it is sealed. Therefore I took the same route as in my previous post, attaching dependency properties:

public class TaskbarItemOverlay  {
    public static readonly DependencyProperty ContentProperty =
        DependencyProperty.RegisterAttached(
            "Content", 
            typeof(object), 
            typeof(TaskbarItemOverlay), 
            new PropertyMetadata(OnPropertyChanged));

    public static readonly DependencyProperty TemplateProperty =
        DependencyProperty.RegisterAttached(
        "Template", 
        typeof(DataTemplate), 
        typeof(TaskbarItemOverlay), 
        new PropertyMetadata(OnPropertyChanged));


    public static object GetContent(DependencyObject dependencyObject) {
        return dependencyObject.GetValue(ContentProperty);
    }

    public static void SetContent(DependencyObject dependencyObject, object content) {
        dependencyObject.SetValue(ContentProperty, content);
    }

    public static DataTemplate GetTemplate(DependencyObject dependencyObject) {
        return (DataTemplate)dependencyObject.GetValue(TemplateProperty);
    }

    public static void SetTemplate(DependencyObject dependencyObject, DataTemplate template) {
        dependencyObject.SetValue(TemplateProperty, template);
    }

    private static void OnPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
        var taskbarItemInfo = (TaskbarItemInfo) dependencyObject;
        var content = GetContent(taskbarItemInfo);
        var template = GetTemplate(taskbarItemInfo);

        if (template == null || content == null) {
            taskbarItemInfo.Overlay = null;
            return;
        }

        const int ICON_WIDTH = 16;
        const int ICON_HEIGHT = 16;

        var bmp =
            new RenderTargetBitmap(ICON_WIDTH, ICON_HEIGHT, 96, 96, PixelFormats.Default);
        var root = new ContentControl {
            ContentTemplate = template, 
            Content = content
        };
        root.Arrange(new Rect(0, 0, ICON_WIDTH, ICON_HEIGHT));
        bmp.Render(root);

        taskbarItemInfo.Overlay = bmp;
    }
}

The first lines a boilerplate code to define the attached properties. There are two of them, Content and Template. The former defines, well, the content we’re going to bind to our model. The latter defines the template used to render the content.

The actual work is done in the method OnPropertyChanged. It takes the template together with the content, renders it, and assigns the resulting bitmap to the Overlay property of the TaskbarItemInfo element.

Usage

I have created a small application to demonstrate the use of the attached properties. The XAML of the window is this:

<Window 
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:src="clr-namespace:WpfApplication1" 
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="OverlayIcon">
            <Grid Width="16" Height="16">
                <Ellipse 
                    Fill="Red" 
                    Stroke="White" 
                    StrokeThickness=".5" />
                <TextBlock 
                    Text="{Binding}" 
                    TextAlignment="Center" 
                    Foreground="White" 
                    FontWeight="Bold" 
                    Height="16" 
                    VerticalAlignment="Center" 
                    FontSize="10"/>
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <Window.TaskbarItemInfo>
        <TaskbarItemInfo 
            src:TaskbarItemOverlay.Content="{Binding Count}" 
            src:TaskbarItemOverlay.Template="{StaticResource OverlayIcon}" />
    </Window.TaskbarItemInfo>
    <Viewbox>
        <TextBlock Text="{Binding Count}" />
    </Viewbox>
</Window>

TaskbarItemOverlay

In the window’s resources we define the template for the overlay. Notice that the Text is bound! Later you can see the TaskbarItemInfo with the attached properties in action: Content binds to the Count property of my viewmodel, and Template references the DataTemplate defined in the resources.

The code-behind is straight forward. I won’t repeat it here, but you can see it at GitHub. Basically it increments the Count property of the viewmodel every seconds in a background thread. You can see the result in the image to the left.

The source code is attached, but also available at GitHub.

Binding WebBrowser content in WPF

WPF, MVVM Comments

When you’re using a WebBrowser control in your WPF application, you may have noticed that you can’t bind the control’s content. WebBrowser has no property to set its content but a method named NavigateToString. So when you’re following a strict MVVM approach you’re lost because you don’t want any code-behind for your views.

But then there are attached properties. As their name implies they allow you to attach new properties to existing dependency objects. In your XAML code you apply such a attached property to your element and can access it as any other property of the object.

Ok, first here’s the code of an attached property to set a WebBrowser’s content:

public class WebBrowserHelper {
    public static readonly DependencyProperty BodyProperty =
        DependencyProperty.RegisterAttached("Body", typeof (string), typeof(WebBrowserHelper), new PropertyMetadata(OnBodyChanged));

    public static string GetBody(DependencyObject dependencyObject) {
        return (string) dependencyObject.GetValue(BodyProperty);
    }

    public static void SetBody(DependencyObject dependencyObject, string body) {
        dependencyObject.SetValue(BodyProperty, body);
    }

    private static void OnBodyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
        var webBrowser = (WebBrowser) d;
        webBrowser.NavigateToString((string)e.NewValue);
    }
}

The static BodyProperty defines the type of the attached property: its name is Body, the type is string, and whenever it is changed the method OnBodyChanged should be called.

The accessors for a attached property must be conventionally named SetXxx and GetXxx. They are called whenever you set or get the property’s value.

Last but not least OnBodyChanged is called when the value of the property has changed. The first parameter is the object the property is attached to, so we can cast it to WebBrowser and call its NavigateToString method.

The actual usage of the new Body property is pretty simple:

<WebBrowser
    src:WebBrowserHelper.Body="{Binding MyHtml}"
    />

given that the ViewModel has a property named MyHtml providing the desired content for the control.

A complete sample application is available on GitHub.

Reactivating this site

Site news Comments

germany.hamburg

Yes, this blog is still alive, though the last post is about two and a half years old.

It has happened a lot since then, but it can be condensed in two points: a) I moved again, this time to Bernau am Chiemsee at the “other end” of Germany, and b) I got married. And the latter caused the former 😃

Anyway, after 30 month of silence I think I should reactivate this blog. In the past I hesitated because most of the time I thought my topics are too trivial to write about. But that’s because whenever I had a particular programming error, I thought about it and tried to solve it. But at the point of the solution I’m already so familiar with the problem that I think it’s not interesting enough anymore to blog about it.

But on the other hand, other might have had the same problem, so why not help them and publish a possible solution? At least Google will find it.

Additionally blogging will help me to sharpen my rusty English skills 😉

Anyway, in my profession and in my spare time I deal with WPF, IoC containers, ASP.NET MVC, NoSQL databases (RavenDB in particular) among others, so you know what to expect in the future.

Sharing Extension for Graffiti

Graffiti Comments

Two weeks ago Telligent published the second beta of their upcoming new product Graffiti. It is a simple lightweight content management system. And by simple I don't mean lame. Far from it! It is simple in the sense of easy deployment, management, and publishing.

Additionally it's easy to extend. Keyvan, who I got to know and appreciate while working on the Community Server Modules, already wrote several extensions for Graffiti. To wrap up all his addons and provide a simple installation experience he started the Graffiti Extras project on CodePlex. And he was kind enough to accept me as a contributor.

So here it is, my first extension for Graffiti. In fact, I was inspired by Danny Douglass' Social Bookmarks extension for BlogEngine.NET. It enables you to link your posts to some of the most popular social bookmarking sites. The image to the right depicts an exemplary post with the extension rendered below.

Implementation

The Sharing extension is implemented as a so called chalk. Think of chalks as of macros. How to write your own chalk is well-documented, so I won't describe how I implemented the Sharing extension. If you want to have a look at the sources, go to the GraffitiExtras project on CodePlex and either download them or browse them online.

Installation

To install the Sharing chalk (and all other extensions provided in Graffiti Extras) download the attached ZIP File. This archive contains two root folders: in the bin folder you can find the GraffitiExtras.dll which you must drop into the bin folder of your Graffiti installation. The second folder, sharing-images, contains two flavors of icons in different sizes (16x16, 24x24, 32x32, and 48x48) for several social bookmarking sites (original icons are provided by FastIcon). Either copy that folder entirely or only the desired flavor/sizes somewhere to the Graffiti web folder.

Usage

To add the sharing extension to your posts, you just have to add a single line to your theme file:

$sharing.Write($post, "<image folder>")

Replace image folder with the path to the desired images. E.g. if you have copied the entire sharing-images folder to the root of your web application, and you want to see the round images with a size of 16x16, you would add following line:

$sharing.Write($post, "/sharing-images/circle/16x16/")

By default the different images are separated by a non-breaking space (&nbsp;) but you can change that with the optional third parameter:

$sharing.Write($post, "<image folder>", " | ")

.NET-Forum.de launched

Community Server, Community Comments

.NET Forum

A couple of days ago Jan Welker launched a new German .NET related community site, .NET-Forum.de. I didn't expect the developsphere to require just another site, but there are already 51 users registered, even though the site wasn't advertised anywhere except the dotnet-snippets.de newsletter.

Jan set up Community Server 2007.1 to drive the site (Did you know that .NET related non-profit communities may receive a free license?) I try to support Jan whenever he experiences issues with CS or has a configuration question. So for me it's an appreciated opportunity to get to learn CS's forum capabilities (Till now I only used the blogging part.)

I wish Jan success, and maybe this post will lead some more people to his site.