¿Cuál es la forma preferida de encontrar control centrado en la aplicación WinForms?

¿Cuál es la forma preferida / más fácil de encontrar el control que recibe actualmente la entrada de usuario (teclado) en WinForms?

Hasta ahora, he encontrado lo siguiente:

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

Desde un formulario, esto se puede llamar simplemente como (en .NET 3.5+ esto incluso podría definirse como un método de extensión en el formulario) –

 var focused = FindFocusedControl(this); 

¿Es esto apropiado?

¿Hay un método incorporado que debería usar en su lugar?

Tenga en cuenta que una sola llamada a ActiveControl no es suficiente cuando se utilizan jerarquías. Imagina:

 Form TableLayoutPanel FlowLayoutPanel TextBox (focused) 

(formInstance) .ActiveControl devolverá la referencia a TableLayoutPanel, no a TextBox (porque ActiveControl parece estar devolviendo el elemento secundario activo inmediatamente en el árbol de control, mientras estoy buscando el control de hoja).

Si ya tiene otras llamadas a la API de Windows, no hay inconveniente en usar la solución de Peters. Pero entiendo sus preocupaciones y tendré una solución similar a la suya, usando solo las funcionalidades de Framework. Después de todo, la diferencia de rendimiento (si hay una) no debería ser significativa.

Yo tomaría un enfoque no recursivo:

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

Después de buscar en Internet, encontré lo siguiente en las preguntas frecuentes sobre formularios de Windows de George Shepherd.

Las bibliotecas de .Net Framework no le proporcionan una API para consultar el Control enfocado. Debe invocar una API de Windows para hacerlo:

[DO#]

 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; } } 

ActiveControl en un Formulario o Contenedor devolverá el control activo de esa entidad sin importar cuán profundamente pueda estar nested dentro de otros contenedores.

En su ejemplo, si TextBox tiene Focus: luego: para Form, TableLayoutPanel y FlowLayoutPanel: ¡la propiedad ‘ActiveControl’ de todos ellos será el TextBox!

Algunos, pero no todos, los tipos “ContainerControl” genuinos … como Formulario y UserControl … exponen eventos clave (en el caso de la forma: solo si se pueden usar Form.KeyPreview == true).

Otros controles que, por diseño, contienen otros controles como TableLayOutPanel, GroupBox, Panel, FlowLayoutPanel, etc. no son de tipo ContainerControl y no exponen KeyEvents.

Cualquier bash de enviar instancias de objetos como TextBox, FlowLayoutPanel, TableLayoutPanel directamente a ContainerControl no se comstackrá: no son de tipo ContainerControl.

El código en la respuesta aceptada, y en la siguiente respuesta que corrige los errores ortográficos de la primera respuesta, comstackrá / aceptará instancias de los anteriores como parámetros porque los está “bajando” para escribir ‘Control haciendo que el tipo de parámetro’ Control

Pero en cada caso, el lanzamiento a ControlContainer devolverá nulo, y el aprobado en la instancia será devuelto (descifrado): esencialmente no operativo.

Y, sí, el código de respuesta modificado funcionará si le pasa un ControlContainer “genuino”, como una instancia de Formulario, que está en la ruta de herencia principal de ActiveControl, pero todavía está perdiendo tiempo duplicando la función de ‘ActiveControl.

Entonces, ¿qué son ContainerControls “genuinos”? Compruébelos: MS docs for ContainerControl

Solo la respuesta de Peter realmente responde la pregunta explícita, pero esa respuesta conlleva el precio de usar interoperabilidad, y ‘ActiveControl le dará lo que necesita.

También tenga en cuenta que cada control (contenedor o no contenedor) tiene una colección de controles que nunca es nula, y que una gran cantidad (nunca he probado todos ellos: ¿por qué lo haría?) El control básico WinForms le permite hacer “loco” cosas “como agregar Controles a la ControlCollection de controles ‘simples’ como el Botón sin un error.

Ahora bien, si la verdadera intención de su pregunta fue preguntar cómo se encuentra el ContainerControl más externoque no está en el Formulario en sí … de un Control no contenedor normal anidaba algunos niveles arbitrarios en profundidad … puede usar algunos de las ideas en la respuesta: pero el código se puede simplificar enormemente.

Controles regulares, ContainerControls, UserControls, etc. (¡pero no Form!) Tienen una propiedad ‘Container’ a la que puede acceder para obtener su contenedor inmediato, pero asegurarse de tener el ‘contenedor final en su ruta de inhertance que no es un Formulario requiere algún código para “subir” el árbol de herencia, lo cual se demuestra aquí.

También puede consultar el control ‘HasChildren property of’ Control, que suele ser útil para tratar los problemas de Focus, ActiveControl y Select en WinForms. Revisar la diferencia entre Select y Focus puede ser valioso aquí, y SO tiene algunos buenos recursos sobre eso.

Espero que esto ayude.

La solución de Hinek funciona bien para mí, excepto que es ContainerControl , no ControlContainer. (En caso de que estuvieras rascándote la cabeza con esa línea roja ondulada).

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

Si sigues ActiveControl de forma recursiva, ¿no te lleva al control de hoja que tiene foco?