it-swarm.com.de

Was ist der bevorzugte Weg, um eine fokussierte Steuerung in der WinForms-App zu finden?

Was ist der bevorzugte/einfachste Weg, um das Steuerelement zu finden, das aktuell Benutzereingaben (Tastatur) in WinForms empfängt?

Bisher habe ich mir Folgendes ausgedacht:

public static Control FindFocusedControl(Control control)
{
    var container = control as ContainerControl;
    return (null != container
        ? FindFocusedControl(container.ActiveControl)
        : control);
}

In einem Formular kann dies einfach folgendermaßen aufgerufen werden (in .NET 3.5+ kann dies sogar als Erweiterungsmethode für das Formular definiert werden):

var focused = FindFocusedControl(this);

Ist das angebracht

Gibt es eine integrierte Methode, die ich stattdessen verwenden sollte?

Beachten Sie, dass ein einzelner Aufruf von ActiveControl nicht ausreicht, wenn Hierarchien verwendet werden. Vorstellen:

Form
    TableLayoutPanel
        FlowLayoutPanel
            TextBox (focused)

(formInstance) .ActiveControl gibt einen Verweis auf TableLayoutPanel zurück, nicht auf das Textfeld (da ActiveControl anscheinend nur ein unmittelbares aktives untergeordnetes Element in der Steuerelementstruktur zurückgibt, während ich nach dem Blattsteuerelement suche).

63
Milan Gardian

Wenn Sie bereits andere Aufrufe an die Windows-API haben, kann die Verwendung der Peters-Lösung nicht schaden. Aber ich verstehe Ihre Bedenken und würde zu einer ähnlichen Lösung tendieren wie Sie, wenn Sie nur die Framework-Funktionen verwenden. Schließlich sollte der Leistungsunterschied (falls vorhanden) nicht signifikant sein.

Ich würde einen nicht rekursiven Ansatz verfolgen:

public static Control FindFocusedControl(Control control)
{
    var container = control as IContainerControl;
    while (container != null)
    {
        control = container.ActiveControl;
        container = control as IContainerControl;
    }
    return control;
}
65
Hinek

Nach dem Durchsuchen des Internets fand ich Folgendes in Häufig gestellte Fragen zu Windows Forms von George Shepherd

Die .NET Framework-Bibliotheken bieten keine API zum Abfragen des fokussierten Steuerelements. Sie müssen dazu eine Windows-API aufrufen:

[C #]

public class MyForm : Form
{
          [DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]
          internal static extern IntPtr GetFocus();

          private Control GetFocusedControl()
          {
               Control focusedControl = null;
               // To get hold of the focused control:
               IntPtr focusedHandle = GetFocus();
               if(focusedHandle != IntPtr.Zero)
                    // Note that if the focused Control is not a .Net control, then this will return null.
                    focusedControl = Control.FromHandle(focusedHandle);
               return focusedControl;
          }
} 
26
Xn0vv3r

ActiveControl in einem Formular oder Container wird gibt das aktive Steuerelement dieser Entität zurück, unabhängig davon, wie tief es in anderen Containern verschachtelt ist.

In Ihrem Beispiel, wenn die TextBox Focus hat: dann: für Form, TableLayoutPanel und FlowLayoutPanel: die Eigenschaft 'ActiveControl von allen wird die TextBox sein!

Einige, aber nicht alle "echten" ContainerControl-Typen ... wie Form und UserControl ... machen Schlüsselereignisse verfügbar (im Fall von Form: Nur wenn Form.KeyPreview == true ist, können sie verwendet werden).

Andere Steuerelemente, die standardmäßig andere Steuerelemente wie TableLayOutPanel, GroupBox, Panel, FlowLayoutPanel usw. enthalten, sind vom Typ ContainerControl nicht und machen KeyEvents nicht verfügbar.

Jeder Versuch, Instanzen von Objekten wie TextBox, FlowLayoutPanel, TableLayoutPanel direkt in ContainerControl umzuwandeln, wird nicht kompiliert: Es handelt sich nicht um ContainerControl.

Der Code in der akzeptierten Antwort und in der nächsten Antwort, der die Rechtschreibfehler der ersten Antwort korrigiert, kompiliert/akzeptiert Instanzen der oben genannten Parameter, da Sie sie "downcasten", um "Control" einzugeben, indem Sie den Parametertyp "Control" festlegen

In jedem Fall gibt die Umwandlung in ControlContainer jedoch null zurück, und die übergebene Instanz wird zurückgegeben (downcasted): im Wesentlichen ein No-Op.

Ja, der geänderte Antwortcode funktioniert, wenn Sie ihm einen "echten" ControlContainer übergeben, z. B. eine Formularinstanz, die sich im übergeordneten Vererbungspfad von ActiveControl befindet. Sie verschwenden jedoch nur Zeit, um die Funktion von ActiveControl zu duplizieren.

Also, was sind "echte" ContainerControls: Check sie aus: MS-Dokumente für ContainerControl

Nur die Antwort von Peter beantwortet die explizite Frage wirklich, aber diese Antwort ist mit dem Preis für die Verwendung von Interop verbunden, und ActiveControl gibt Ihnen das, was Sie benötigen.

Beachten Sie auch, dass jedes Steuerelement (Container oder Nicht-Container) eine Controls-Auflistung hat, die niemals null ist, und dass viele (ich habe noch nie alle ausprobiert: Warum sollte ich?) Das grundlegende WinForms-Steuerelement Sie verrückt machen lassen Sachen "wie das Hinzufügen von Controls zur ControlCollection von 'einfachen' Controls wie Button ohne Fehler.

Wenn nun die echte Absicht Ihrer Frage lautete, wie Sie das äußerste ContainerControl finden ... das steht nicht auf der Bilden Sie sich selbst ... aus einem regulären Nicht-Container Control verschachtelt einige beliebige Ebenen tief ... Sie können einige der Ideen verwenden in der antwort: aber der code kann stark vereinfacht werden.

Normale Steuerelemente, ContainerControls, UserControls usw. (aber nicht Form!) Haben alle eine Container-Eigenschaft, auf die Sie zugreifen können, um ihren unmittelbaren Container abzurufen. Um sicherzustellen, dass Sie den endgültigen Container in ihrem Inhertanzpfad haben, der kein Formular ist, ist Code erforderlich den Vererbungsbaum "hochgehen", was hier demonstriert wird.

Möglicherweise möchten Sie auch die Eigenschaft "HasChildren" des Steuerelements überprüfen, die normalerweise bei Problemen mit Focus, ActiveControl und Select in WinForms hilfreich ist. Die Überprüfung des Unterschieds zwischen Select und Focus kann hier wertvoll sein, und SO hat einige gute Ressourcen dafür.

Hoffe das hilft.

20
BillW

Hineks Lösung funktioniert gut für mich, außer es ist ContainerControl, nicht ControlContainer. (Nur für den Fall, dass Sie sich über die rote Schnur kratzten.)

    public static Control FindFocusedControl(Control control)
    {
        ContainerControl container = control as ContainerControl;
        while (container != null)
        {
            control = container.ActiveControl;
            container = control as ContainerControl;
        }
        return control;
    }
8
Nate Cook

Wenn Sie ActiveControl rekursiv ausführen, gelangen Sie nicht zum fokussierten Blattsteuerelement.

2

ActiveControl funktioniert nicht immer wie bei SplitContainer, ActiveControl.Focused ist falsch.

Also für eine idiotensichere Methode könnte man so etwas machen:

private IEnumerable<Control> _get_all_controls(Control c)
{
    return c.Controls.Cast<Control>().SelectMany(item =>
        _get_all_controls(item)).Concat(c.Controls.Cast<Control>()).Where(control =>
        control.Name != string.Empty);
}

var _controls = _get_all_controls(this);
foreach (Control control in _controls) 
    if (control.Focused)
    {
        Console.WriteLine(control.Name);
        break;
    }
0
colin lamarre