tags:

views:

300

answers:

4

I have a .net MDI application written in vb.net. I'm trying to program a form that will allow the user to select a special character such as °, µ, ², ³, ɑ and so on and have that character inserted into whatever control was focused prior to their launching the form via a hot-key or the main menu in the MDI parent.

The easy way to do this would be to figure out which control was focused on which MDI Child form when the character selection form is invoked, but I can't find any information on how to do this.

Any ideas?

+1  A: 

Add an IMessageFilter and monitor for windows messages such as WM_SETFOCUS or WM_ACTIVATE. You can use Control.FromHandle to convert IntPtrs to .NET Controls.

hjb417
It's added the the application message loop via Application.AddMessageFilter. See http://blogs.msdn.com/csharpfaq/archive/2004/10/20/245412.aspx for an example.
hjb417
tom.dietrich
I'll post an example in a few
hjb417
A: 

You could add a class like this to your project:

public class FocusWatcher
{
    private static System.Windows.Forms.Control _focusedControl;
    public static System.Windows.Forms.Control FocusedControl
    {
        get
        {
            return _focusedControl;
        }
    }

    public static void GotFocus(object sender, EventArgs e)
    {
        _focusedControl = (System.Windows.Forms.Control)sender;
    }
}

Then, for any control on any form that you want to be a candidate for "most recently-focused control", you would do this:

textBox1.GotFocus += FocusWatcher.GotFocus;

and then access FocusWatcher.FocusedControl to get the most recently-focused control. Monitoring messages will work, but you have to ignore messages that you don't want (like WM_ACTIVATE from the Mdi Form).

You could iterate through all the controls on every form and add this handler for the GotFocus event, but surely there are controls that you don't want this for (like Buttons, for example). You could instead iterate and only add the handler for TextBoxes.

MusiGenesis
+1  A: 

Looks like the WM_SETFOCUS messages aren't coming through.... My bad. I'm pretty sure they'd come through in the Control.WndProc method. As a work around, I had to p/invoke GetFocus evertime a message came and store the handle of the control that has focus.

The 1st code section is the filter class and the 2nd code section is the test code.



    public class LastFocusedControlFilter : IMessageFilter
    {
        [DllImport("user32")]
        private static extern IntPtr GetFocus();

        private IntPtr? _lastCtrl;
        private readonly Control _ctrlToIgnore;

        public LastFocusedControlFilter(Control controlToIgnore)
        {
            _ctrlToIgnore = controlToIgnore;
        }

        public bool PreFilterMessage(ref Message m)
        {
            if (!_ctrlToIgnore.IsHandleCreated || _ctrlToIgnore.Disposing || _ctrlToIgnore.IsDisposed)
            {
                return false;
            }
            IntPtr handle = GetFocus();
            if (handle != _ctrlToIgnore.Handle)
            {
                _lastCtrl = handle;
            }
            return false;
        }

        public Control GetLastFocusedControl()
        {
            return _lastCtrl.HasValue ? Control.FromHandle(_lastCtrl.Value) : null;
        }
    }
static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        using (Form form = new Form())
        {
            Action resetBackColor = null;
            for (int i = 0; i < 10; i++)
            {
                TextBox textBox = new TextBox();
                resetBackColor += delegate { textBox.BackColor = Color.White; };
                textBox.Text = ((char)('a' + i)).ToString();
                textBox.Location = new Point(0, textBox.Height * i);
                form.Controls.Add(textBox);
            }
            Button showTextButton = new Button();
            LastFocusedControlFilter msgFilter = new LastFocusedControlFilter(showTextButton);
            showTextButton.Dock = DockStyle.Bottom;
            showTextButton.Text = "Show text of last selected control";
            showTextButton.Click += delegate(object sender, EventArgs e)
             {
                 resetBackColor();
                 Control lastControl = msgFilter.GetLastFocusedControl();
                 if (lastControl == null)
                 {
                     MessageBox.Show("No control previous had focus.");
                 }
                 else
                 {
                     lastControl.BackColor = Color.Red;
                     MessageBox.Show(string.Format("Control of type {0} had focus with text '{1}'.", lastControl.GetType(), lastControl.Text));
                 }
             };
            form.Controls.Add(showTextButton);

            Application.AddMessageFilter(msgFilter);
            Application.Run(form);
        }
    }
}
hjb417
Fantastic answer! Thank you for your efforts!
tom.dietrich
+1  A: 

Found an easier way -

[Parent Form].ActiveMdiChild.ActiveControl
tom.dietrich
Well sure, if you think *one line of code* is "easier". :)
MusiGenesis
Just a bit easier, haha. Although that other approach is awesome for non-MDI apps so I've still up-voted it.
tom.dietrich