csammisrun

A rare situation

WPF windows, in my Winforms application?

with one comment

In the last post about some of the odd things that shaim does, I talked about how WPF’s rich text input makes for laggy typing and how we lessened the impact. Today I’ll talk about how one should be grateful that any text input happens in shaim at all, due to the slightly strange way that WPF is used by the application.

Core ‘n’ plugins

The shaim architecture is plugin-based. There’s the core (shaim.exe, a Winforms Application), and several plugins that comprise the bulk of the application’s functionality. Many people don’t realize that the UI – the contact list, conversation windows, all of that noise – is a plugin itself, specifically a WPF Class Library.

When we were designing shaim back in the bad old days, we knew that we were going to use WPF for the UI, so why isn’t the core a WPF application? The answer is compatibility: WPF is only supported on Windows XP SP2 and up. We wanted to build shaim so that developers could make UI plugins with different windowing toolkits so it could run on other platforms. This has actually worked out in practice, as the core is fully .NET 2.0 compatible and there’s at least one community-driven UI plugin in the works (using GTK#).

Two event models

About the only thing that Winforms and WPF have in common is that they’re both Windows GUI toolkits. Winforms follows the general Win32 GUI event model: An Application has a message pump that raises events (mouse click, text typed, window moved, etc.), developers handle events as they are raised from the message pump, the world keeps on spinning. There are niceties to the process, but at its root that’s all there is to it. It’s not altogether difficult to see how Winforms is a shell over the Win32 API.

WPF has a different event model altogether, called routed events. Although the concept of direct events still exist, there are also now tunneling and bubbling events. The gist of it is this: The user types text into a text field, which generates a PreviewKeyDown event from the root element. This event “tunnels” down the visual tree until it gets to the text field, which then generates a KeyDown event, which “bubbles” back up the visual tree until it gets to the root element. At any point along the visual tree, either type of event can be handled.

Where all the keyboard events at?

When shaim’s UI and WPF itself were still very young – we’re talking like January / February 2006 – we noticed that we couldn’t type into any text input fields. Buttons clicked, windows moved, but no keyboard events were fired at all. Attaching event handlers to all the key press events in the visual tree, we found that all the tunneling Preview* events were being raised, but none of the corresponding bubbling events were happening, and no text would appear in the input field.

As much fun as having an IM client that you couldn’t use to send messages seemed, we decided to try hosting a Winforms text field control in one of the UI windows to see what would happen. Lo and behold, it worked just fine. Great, except at the time hosting was slow and rather bloated, not to mention it felt like a cheap hack. What the hell was wrong with WPF text input?

Two! Two Application objects! Ha ha ha!

After a lot of experimenting and talking to a Microsoft engineer (I forget who, sorry), we finally got it diagnosed. It was completely shaim’s fault for being architected as a Winforms application that launched WPF windows, instead of a straight WPF application or even a Winforms application that lauched Winforms windows that contained a Winforms->WPF host. There was no WPF message pump, so WPF-style routed events couldn’t happen correctly. Direct events, like clicking and moving windows, were still fine. The Winforms text field worked because the shaim core hosts a Winforms message pump in its Application object.

Okay, so give the UI plugin a message pump and it’ll be happy…but starting a message pump by calling Application.Run() is a blocking call. If the Winforms Application started a WPF Application, it couldn’t do much else. The stunning workaround: Start the WPF Application on its own sweet time.

void StartWpfApplication()
{
  application = new Application();
  application.Startup += application_Startup;

  AnonInvoker invoker = delegate
  {
    // Some slow operation that we didn't want to block core startup
    application.Run();
  };
  application.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
    invoker);
}

void application_Startup(object sender, StartupEventArgs e)
{
  // signal the core that the UI's ready to roll
}

And that is why shaim users should be damn grateful that they can type in shaim at all

Written by Chris

April 4th, 2008 at 2:53 pm

One Response to 'WPF windows, in my Winforms application?'

Subscribe to comments with RSS or TrackBack to 'WPF windows, in my Winforms application?'.

  1. [...] WPF windows, in my Winforms application? [...]

Leave a Reply