Introduction

Window finder is a simple WPF application that shows all the visible top level windows of the current session. It can be very useful to get back access to windows that have gone off screen, e.g. after you have undocked your laptop. While being off screen is not a real problem for top level windows - you can still use the task bar context menu to move them - it can be for popup windows that don't appear in the task bar, especially if the application remembers the position and insists on drawing it outside of the visible area even after a restart. With this little tool, you can just drag any window back to the visible area.

The functionality of this tool is not a big thing, but it nicely shows some WPF techniques in a concise project and how easy it is with WPF to have a simple implementation for simple things, so I decided to publish it here.

What Can I Get from this Article?

The WPF techniques covered here are:

MVVM design

Using an ItemsControl with a Canvas as the items panel

Using a ViewBox to decouple View and ViewModel coordinates

Dragging items inside of a ViewBox

Using the Code

Like all my WPF apps, I built this little application using the MVVM design. Even for such simple apps, it pays off using it since it really helps to keep everything simple and clear.

To have all information required to display the items, only two view models are needed: one representing each window and one holding the collection of all windows. The coordinates used in the view models are the native windows coordinates; we don't need any view related translations in the view model.

The WindowItemViewModel represents each window and has just Handle, Text, Process and Rect properties. That's all we need to display a placeholder of a window. It implements INotifyPropertyChanged so the view of the window will follow the real windows position when we change it. You may notice that the properties like Text or Process are not buffered, but will get evaluated every time the property getter is called. This is OK for properties designed to be used in binding, since the binding will anyhow call the getter just once, so buffering would be an unnecessary overhead. And if evaluating the property turns out to be slow, you could just make the binding asynchronous to speed up the display, which would not work if you had evaluated it in the constructor to feed the property with the value.

The TopLevelWindowsViewModel provides a list of all windows to show, plus the bounding rectangle of all windows to set up proper scrolling. It derives from DependencyObject so we can just use dependency properties. Looking at the code dependency properties seem to introduce a lot of typing work, but if you use the "propdp" code snippet, it turns out to be easier to handle than a simple property with INotifyPropertyChanged - and much safer! The only "written" code here is the Refresh method, that simply enumerates all windows, creates the list of item view models and calculates the bounds.

Since we use the MVVM pattern, the control's data context is the TopLevelWindowsViewModel. We want to display a list of items, so we use an ItemsControl and bind the ItemsSource property to the TopLevelWindows property of the view model. We want to explicitly position the items, so we use a Canvas as the item control's ItemsPanel. To visualize each window, we simply draw a border and a top aligned text block, so that's how the ItemTemplate is done. The data context of each item will be a WindowItemViewModel, so we can bind to its properties. The borders width and height simply bind to the window rectangle's width and height, but if you look at the XAML you won't find the Canvas.Left and Canvas.Top binding at the borders level. Why this wouldn't work you can see if you look at the visual tree, e.g. using Snoop:

The Border, which is the root element of the item template, is not a direct child of the Canvas. The ItemsControl creates a list of ContentPresenters which then contain the item that we have designed. This seems to be a problem, since the ContentPresenter is not part of our XAML but generated by the ItemsControl when realizing the items, so it's not under our direct control. We could start writing code behind to bubble up the visual tree and find the ContentPresenter or write an ItemsContainerGenerator, but there is a much simpler solution - just using the ItemContainerStyle to access the ContentPresenter's properties as you can see in the XAML above.

Now we can display all our windows in the items presenter, but it would be a one to one mapping of the screen (we used native window coordinates in the view model), which is not the best for getting an overview, so we want to scale down everything. A ViewBox will do exactly what we need. We just have to take care that we don't mess up the scrolling, so we need to keep the aspect ratio of the ViewBox the same as the ItemsControl's. We could use a converter in the binding to scale the values down, but this would make a dynamic scaling hard, if not impossible. The easiest way to solve this in XAML without writing extra code is to simply bind the native width and height to the ViewBox and then just scale the whole view box using a layout transformation.

The final step is to add a simple drag handler to support dragging the lost windows back to the visible area. Just dragging items is a simple task; remember the initial mouse position in the mouse down event and measure the distance in each mouse move event. Since the dragging is applied against the view model and the view model is using native window coordinates, everything turns out to match perfect.