Qual é a maneira preferida de encontrar o controle focado no aplicativo WinForms?

Qual é a maneira preferida / mais fácil de encontrar o controle que está recebendo input do usuário (teclado) no WinForms?

Até agora eu tenho o seguinte:

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

De um formulário, isso pode ser chamado simplesmente como (no .NET 3.5 + isso pode ser definido como um método de extensão no formulário) –

 var focused = FindFocusedControl(this); 

Isso é apropriado?

Existe um método interno que eu deveria estar usando em vez disso?

Observe que uma única chamada para o ActiveControl não é suficiente quando as hierarquias são usadas. Imagine:

 Form TableLayoutPanel FlowLayoutPanel TextBox (focused) 

(formInstance) .ActiveControl retornará a referência para TableLayoutPanel, não o TextBox (porque o ActiveControl parece estar retornando apenas o filho ativo imediato na tree de controle, enquanto eu estou procurando pelo controle de folha).

Se você já tem outras chamadas para a API do Windows, não há problema em usar a solução da Peters. Mas eu entendo suas preocupações sobre isso e tenderia a uma solução semelhante à sua, usando apenas as funcionalidades do Framework. Afinal, a diferença de desempenho (se houver) não deve ser significativa.

Eu tomaria uma abordagem não recursiva:

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

Depois de pesquisar na Internet, encontrei o seguinte no FAQ do Windows Shepherd sobre o Windows Forms

As bibliotecas de estrutura .Net não fornecem uma API para consultar o controle focalizado. Você precisa invocar uma API do Windows para fazer isso:

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

O ActiveControl em um Formulário ou Contêiner retornará o controle ativo dessa entidade, não importando o quão profundamente ele possa estar nested dentro de outros contêineres.

No seu exemplo, se o TextBox tiver Focus: then: para Form, TableLayoutPanel e FlowLayoutPanel: a propriedade ActiveControl de todos eles será o TextBox!

Alguns, mas não todos, tipos “genuínos” ContainerControl … como Form e UserControl … expõem Key Events (no caso de Form: somente se Form.KeyPreview == true puderem ser usados).

Outros controles que, por design, contêm outros controles como TableLayOutPanel, GroupBox, Panel, FlowLayoutPanel, etc. não são do tipo ContainerControl e não expõem KeyEvents.

Qualquer tentativa de converter instâncias de objects como TextBox, FlowLayoutPanel, TableLayoutPanel diretamente para ContainerControl não será compilada: elas não são do tipo ContainerControl.

O código na resposta aceita, e na próxima resposta que corrige os erros de ortografia da primeira resposta, compilará / aceitará as instâncias acima como parâmetros porque você está “fazendo downcast” para digitar ‘Control fazendo o tipo de parâmetro’ Control

Mas em cada caso, o cast para ControlContainer retornará null, e a instância passada será retornada (downcasted): essencialmente, um no-op.

E, sim, o código de resposta modificado funcionará se você passar um ControlContainer “genuíno”, como uma instância Form, que está no caminho de inheritance pai do ActiveControl, mas você ainda está perdendo tempo duplicando a function de “ActiveControl”.

Então, o que são ContainerControls “genuínos”: confira: MS docs for ContainerControl

Apenas a resposta de Peter realmente responde à pergunta explícita, mas essa resposta traz o preço de usar a interoperabilidade, e o ActiveControl lhe dará o que você precisa.

Observe também que todo Controle (contêiner ou não-container) tem uma Coleção de Controles que nunca é nula, e que muitos (eu nunca tentei todos eles: por que eu faria?) O controle básico do WinForms permite que você faça “louco coisas “como adicionar controles ao ControlCollection de controles ‘simples’ como Button sem um erro.

Agora, se a verdadeira intenção da sua pergunta foi perguntar como você encontra o ContainerControl mais externoque não está no próprio Formulário … de um controle não contêiner comum nested em alguns níveis arbitrários … você pode usar alguns as idéias na resposta: mas o código pode ser muito simplificado.

Controles regulares, ContainerControls, UserControls, etc. (mas não Form!), Todos têm uma propriedade ‘Container’ que você pode acessar para obter seu container imediato, mas certificando-se de ter o ‘Container final em seu caminho de inheritance que não é um Form requer algum código para “walk-up” a tree de inheritance, que é demonstrada aqui.

Você também pode querer verificar a propriedade ‘HasChildren’ de ‘Control’, que geralmente é útil para lidar com problemas de Focus, ActiveControl e Select em WinForms. Revisar a diferença entre Selecionar e Foco pode ser valioso aqui, e o SO tem alguns bons resources sobre isso.

Espero que isto ajude.

A solução de Hinek funciona bem para mim, exceto que é ContainerControl , não ControlContainer. (Apenas no caso de você estar coçando a cabeça sobre aquela linha vermelha ondulada.)

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

Se você seguir o ActiveControl de forma recursiva, não o levará ao controle de folha que tem foco?