WPF Tips and Tricks: Window.Show() Without Activating The Window

- tagged

So you want to spawn a new WPF window but you don't want it to be activated?  This is doable in Win32, but with WPF, it presents some problems ... until now.  This issue recently came up and some folks (read: not me) on the WPF team discovered a a pretty clever workaround.  Basically, the workaround is to use an obscure windows hooks that exists for Computer Based Training (CBT), which allows or a training program to choose to ignore or allow messages to get through to the window.

So, the solution is to set the hook before calling the Show() method on the window. The call back receives the HCBT_ACTIVATE notification, returns 1 to ignore/prevent the operation, then unhooks the hook. The window is shown, but does not take focus. There is no flicker or visible degradation.

If you'd like to see this working in action, check out the sample application that I've deployed via ClickOnce.  You'll notice that the main window will spawn three windows yet you will continue to be able to type in the textbox of the main window. I've posted the source code and here's the crux of the code if you are interested:

 

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;


namespace ShowWindowWithoutActivating
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>

    public partial class Window1 : System.Windows.Window
    {
        delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
        private HookProc myCallbackDelegate;
        IntPtr hook;

        DispatcherTimer dt = new DispatcherTimer();

        int counter = 0;

        public Window1()
        {
            InitializeComponent();
            textBox1.Focus();
            dt.Interval = new System.TimeSpan(0, 0, 3);
            dt.Tick += new EventHandler(TimerTick);
            dt.Start();

            // initialize our delegate
            this.myCallbackDelegate = new HookProc(this.MyCallbackFunction);

        }
        public void TimerTick(object sender, EventArgs e)
        {
            if (counter == 2) dt.Stop();
            counter++;

            Window w = new Window();
            w.Title = "TestWindow";
            w.Width = 400;
            w.Height = 300;
            w.Top = 400;
            w.Left = 400;

            // setup a cbt hook
            hook = SetWindowsHookEx(5 /* wh_cbt */, this.myCallbackDelegate, IntPtr.Zero, AppDomain.GetCurrentThreadId());
            w.Show();
        }

        private int MyCallbackFunction(int code, IntPtr wParam, IntPtr lParam)
        {
            switch (code)
            {
                case 5: /* HCBT_ACTVIATE */
                    UnhookWindowsHookEx(hook);
                    return 1; /* prevent windows from handling activate */
            }
            //return the value returned by CallNextHookEx
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        [DllImport("user32.dll")]
        static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(int code, HookProc func, IntPtr hInstance, int threadID);

        [DllImport("user32.dll")]
        static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    }
}
posted on Aug 22nd, 2007 | Permalink | Comments (1)

1 Comment »

  1. Karsten,

    I'd love to hear more about Flotsam for our email newsletter InteractiveTV Today [itvt] and for our show, The TV of Tomorrow Show March 11-12, 2008 in San Francisco.

    Thanks very much,

    Tracy

    Comment by Tracy Swedlow - November 20, 2007 @ 12:56 PM

Leave a comment