Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

A WPF Behavior for Window Resize Events in .NET 3.5

Pete Brown - 09 January 2010

I got stuck on one bit of functionality in the ShoeBoxScan project Scott assigned me just before the holidays. I needed to keep the size of the selection adorners in sync with the size of the overall window when resized:

image image

(the screenshots are a WIP development build)

Now, if I had just used a ViewBox, everything would have resized on the adorner layer, and I only wanted the bounds changed, not the size of the grips or caption, or thickness of the lines.

WPF handles almost all resizing and layout tasks you could want. In fact, I’ve never really needed to manually resize elements in the past. I usually put things in grid cells to handle resizing and maybe a viewbox if I need automatic scaling. This time, though, I needed some resizing information that simply wasn’t being surfaced by WPF.

(This should work for WPF 4 as well, but the code I wrote was for WPF 3.5.)

Window.SizeChanged will tell you when the user has resized the window, but it’s a discrete final event as opposed to an in-progress event. Sometimes you need to know when the user is resizing the window so you can provide appropriate UI updates. In this app, I needed to continually update the adorner as it looked really bad otherwise (or, I could have hidden it during resize, but I didn’t like that option)

Window.LayoutUpdated is also helpful, but if you handle that and inside the event do something that would cause another layout cycle, it gets pretty messy.

So I created a behavior that would surface the sizing events, and figured “what the heck”, I’d include the full suite of resize-related events. I’m posting it here as an example of writing your own behavior as well as something which may be potentially useful for you in your own projects.

Project Setup

This was added to a “Behaviors” folder of an existing WPF 3.5 project.

image

Add a reference to System.Windows.Interactivity to get access to the behavior types.

image

Creating the Behavior

There are other tutorials about creating behaviors. Don’t limit yourself to ones on WPF, as the Silverlight ones can be useful as well. I won’t go into great detail on that here other than to point out a few specifics.

Overall Structure and Attach/Detach

The behavior is intended to be attached to a Window, so it inherits from Behavior<Window>. This could have been an attached property “behavior”, but I felt like playing with the real thing, so there it is :)

The two functions of interest are OnAttached and OnDetaching. Those wire up the windows proc handler (more on that in a bit) and then clean it back up. AssociatedObject is provided by the Behavior base class. In this case, it is a Window.

public class WindowResizeEvents : Behavior<Window>
{
    public event EventHandler Resized;
    public event EventHandler Resizing;
    public event EventHandler Maximized;
    public event EventHandler Minimized;
    public event EventHandler Restored;

    // called when the behavior is attached
    // hook the wndproc
    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.Loaded += (s, e) =>
            {
                WireUpWndProc();
            };
    }

    // call when the behavior is detached
    // clean up our winproc hook
    protected override void OnDetaching()
    {
        RemoveWndProc();

        base.OnDetaching();
    }

...
}

 

Wiring up the Windows Proc

The Window Procedure is the message handler for a window. In regular C++ programming, all Windows messages for a window are handled in a single location with a big old switch statement. In managed code, much of that is handled for us and transformed into a bunch of events.

But what happens if the windowing toolkit you’re using (windows forms, WPF, VB6, whatever) doesn’t do anything useful with some messages you’re interested in? Well, you wire up your own windows proc, of course!

System.Windows.Interop provides us with what we need to wire up our own proc. It takes care of getting us an hWnd for a visual (the Window itself in this case) as well as providing a function to let us wire up the windows proc.

private HwndSourceHook _hook;

private void WireUpWndProc()
{
    HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;

    if (source != null)
    {
        _hook = new HwndSourceHook(WndProc);
        source.AddHook(_hook);
    }
}

private void RemoveWndProc()
{
    HwndSource source = HwndSource.FromVisual(AssociatedObject) as HwndSource;

    if (source != null)
    {
        source.RemoveHook(_hook);
    }
}

If your background doesn’t include any native code, or dealing with Windows API functions, you may not recognize the Hwnd. an hWnd is a windows native handle to a window. Under the covers, Windows deals with handles for most important resources including windows (hWnd) and device contexts (hDC - for things like drawing). There are lots of other handles as well, but you don’t run into them as often.

The Window Proc

And here’s the windows proc itself. You can find the constant definitions from the C++ header files, or by searching for define WM_WHATEVER on the net. I keep “define” in there so I can find the actual constant and its value, not just some use of it.

private const Int32 WM_EXITSIZEMOVE = 0x0232;
private const Int32 WM_SIZING = 0x0214;
private const Int32 WM_SIZE = 0x0005;

private const Int32 SIZE_RESTORED = 0x0000;
private const Int32 SIZE_MINIMIZED = 0x0001;
private const Int32 SIZE_MAXIMIZED = 0x0002;
private const Int32 SIZE_MAXSHOW = 0x0003;
private const Int32 SIZE_MAXHIDE = 0x0004;

private IntPtr WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, ref Boolean handled)
{
    IntPtr result = IntPtr.Zero;

    switch (msg)
    {
        case WM_SIZING:             // sizing gets interactive resize
            OnResizing();
            break;

        case WM_SIZE:               // size gets minimize/maximize as well as final size
            {
                int param = wParam.ToInt32();

                switch (param)
                {
                    case SIZE_RESTORED:
                        OnRestored();
                        break;
                    case SIZE_MINIMIZED:
                        OnMinimized();
                        break;
                    case SIZE_MAXIMIZED:
                        OnMaximized();
                        break;
                    case SIZE_MAXSHOW:
                        break;
                    case SIZE_MAXHIDE:
                        break;
                }
            }
            break;

        case WM_EXITSIZEMOVE:
            OnResized();
            break;                
    }

    return result;
}

See, one switch statement for the messages, and another to parse the parameters.

You can see I didn’t do anything with MAXSHOW and MAXHIDE. I stuck them in there, but they’re not that interesting to me. Feel free to wire them up if you’d like.

Event Raising

Finally, the event raising. This is pretty simple stuff. I broke them all out into separate functions in case it later makes sense to make these protected and allow overriding.

private void OnResizing()
{
    if (Resizing != null)
        Resizing(AssociatedObject, EventArgs.Empty);
}

private void OnResized()
{
    if (Resized != null)
        Resized(AssociatedObject, EventArgs.Empty);
}

private void OnRestored()
{
    if (Restored != null)
        Restored(AssociatedObject, EventArgs.Empty);
}

private void OnMinimized()
{
    if (Minimized != null)
        Minimized(AssociatedObject, EventArgs.Empty);
}

private void OnMaximized()
{
    if (Maximized != null)
        Maximized(AssociatedObject, EventArgs.Empty);
}

Use in Xaml

To use it in Xaml, you’ll need to do three things in addition to handling the events:

  1. Create a namespace reference for System.Windows.Interactivity (I used “i”)
  2. Create a namespace reference for your own behaviors (“behaviors”
  3. Wire up the behavior using the <i:Interaction.Behaviors>
<Window x:Class="ShoeBoxScan.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        ...
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:behaviors="clr-namespace:ShoeBoxScan.Behaviors"
        Title="Shoebox Scan"
        Height="650"
        Width="700"
        ...
        Icon="/ShoeBoxScan;component/Resources/app.png">
    
    <i:Interaction.Behaviors>
        <behaviors:WindowResizeEvents Resized="Window_Resized"
                                      Resizing="Window_Resizing"
                                      Maximized="Window_Maximized"
                                      Minimized="Window_Minimized"
                                      Restored="Window_Restored" />
    </i:Interaction.Behaviors>
...
</Window>

You could have all the events go to a single handler if you wished, but I decided to break them out.

Why not just Subclass the Window?

Over the years, I’ve grown to prefer composition over inheritance whenever I can – ie, when I don’t need access to protected members. I don’t want to force my own base class in there when it may interfere with someone else’s custom base class. A behavior is really easy to add to the window and doesn’t require editing generated templates to change from Window to MySuperEventWindow or otherwise getting in the way of existing functionality.

Why Events and Not Commands?

Commands are easy enough to add, and I would have no objection to anyone doing so. However, I felt this was a pure UI function and so events would be ok here.

Conclusion

Hope this helps you out in your own application. If you’ve had to solve this problem in your own WPF project, I’d love to hear about your approach. Let me know in the comments below.

All the source required is inline in this post.

       
posted by Pete Brown on Saturday, January 9, 2010
filed under:        

8 comments for “A WPF Behavior for Window Resize Events in .NET 3.5”

  1. Jeremiah Morrillsays:
    Super cool! I know other's have had similar difficulties with Window.SizeChanged. I know one that wanted to make a window that always kept a specific aspect ratio, even while resized. They had a fit when I told them to hook the message loop...now I can just point them to this link! :)

  2. Owensays:
    I'm looking at the online MSDN library documentation for both .NET 4.0 Beta 2 and Silverlight 4 and I don't see anything about a System.Windows.Interactivity namespace. Am I missing something obvious or is this something you have to be special to read about?
  3. Alexissays:
    Hi Pete,

    Thanks for this great piece of code. I was hoping you could help me out with something though...

    I'm creating a WPF user cointrol, which essentially needs to respond to its' parent window being resized.
    The user control will in-fact be hosted in a WinForms application, and as such, I cannot attach the behavior using XAML.

    How does one go about attaching the behavior in the code-behind (or XAML) of the user control itself, given I can find the parent Window?

    Alexis
  4. Jesussays:
    Thank you so much for sharing your solution, you made my day. Now I understand why Windows doesn't implement all those events that are available on Forms: to give coders like you the chance to show their value. Brilliant!

Comment on this Post

Remember me

3 trackbacks for “A WPF Behavior for Window Resize Events in .NET 3.5”

  1. Sanjeev Agarwalsays:
    Daily tech links for .net and related technologies - Jan 10-12, 2010 Web Development ASP.NET MVC Mobile
  2. Daily tech links for .net and related technologies &laquo; .Net Knowledge Basesays:
    PingBack from http://hirentechie.wordpress.com/2010/01/11/daily-tech-links-for-net-and-related-technologies/
  3. Sanjeev Agarwalsays:
    Daily tech links for .net and related technologies - Jan 10-12, 2010 Web Development ASP.NET MVC Mobile