views:

166

answers:

2

Hi there, imagine that I have a Form with 9 controls (TabbedStuffControl) in a 3x3 tile, and these controls contain TabControls containing another control (StuffControl) with ListBoxes and TextBoxes.

I'd like to know a proper way to let TabbedStuffControl that its child has received a focus? e.g. user clicks into a textbox of StuffControl or drags something to listbox of StuffControl. Eventually the Form should know which TabbedStuffControl is active

Do I need to hook up GotFocus event of TextBoxes and ListBoxes and TabControls, then dispatch another event to finally let Form know who got focus? I think that there should be a simpler way - that somehow TabbedStuffControl knows that its child got focus, so there would be only one place in code that I'll hook up.

Thanks.

A: 

You'll have to use COM:

// Import GetFocus() from user32.dll
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr GetFocus();

protected Control FocusControl
{
    get
    {
        Control focusControl = null;
        IntPtr focusHandle = GetFocus();

        if (focusHandle != IntPtr.Zero)
            // returns null if handle is not to a .NET control
            focusControl = Control.FromHandle(focusHandle);

        return focusControl;
    }
}
Icono123
thanks, but this is a property that would tell me which control has focus. What I want to do is to know *when* somebody got focus, so I'd need an event.
Axarydax
This has nothing to do with COM it's called P/Invoke...
Simon Linder
Simon is right. ActiveControl is the .NET equivalent.
Hans Passant
The library is part of COM: http://msdn.microsoft.com/en-us/library/ms646294(VS.85).aspx. P/Invoke is how to use COM objects in .NET.
Icono123
Actually, P/Invoke is just about calling native code from .NET - see http://en.wikipedia.org/wiki/Platform_Invocation_Services
Axarydax
+2  A: 

Using the Enter event (better then GotFocus) is certainly a good approach. Subscribe a handler for all controls, then go find the parent of the control in the Enter event handler. This sample code demonstrates the approach:

  public partial class Form1 : Form {
    public Form1() {
      InitializeComponent();
      wireEnter(this.Controls);
    }
    private void wireEnter(Control.ControlCollection ctls) {
      // Hook Enter event for all controls
      foreach (Control ctl in ctls) {
        ctl.Enter += ctl_Enter;
        wireEnter(ctl.Controls);
      }
    }

    TabbedStuffControl mParent;

    private void ctl_Enter(object sender, EventArgs e) {
      // Find parent
      Control parent = (sender as Control).Parent;
      while (parent != null && !(parent is TabbedStuffControl)) parent = parent.Parent;
      if (parent != mParent) {
        // Parent changed, do something.  Watch out for null
        //....
        mParent = parent as TabbedStuffControl;
      }
    }
  }
Hans Passant
Do you think wiring up every control is a little too much? Just wondering what the performance would look like.
Icono123
@Icono: Focus events happen in "human time". Your user will *never* notice the ~20 nanoseconds it takes to fire the event. Only one will fire, not all of them.
Hans Passant
works like a charm!
Axarydax