WinForms 앱에서 집중된 컨트롤을 찾는 데 선호되는 방법은 무엇입니까?
WinForms에서 현재 사용자 (키보드) 입력을 받고있는 컨트롤을 찾는 가장 좋은 / 가장 쉬운 방법은 무엇입니까?
지금까지 다음을 생각해 냈습니다.
public static Control FindFocusedControl(Control control)
{
var container = control as ContainerControl;
return (null != container
? FindFocusedControl(container.ActiveControl)
: control);
}
양식에서 간단히 다음과 같이 호출 할 수 있습니다 (.NET 3.5 이상에서는 양식의 확장 메서드로 정의 할 수도 있음)-
var focused = FindFocusedControl(this);
이것이 적절합니까?
대신 사용해야하는 기본 제공 방법이 있습니까?
계층 구조가 사용되는 경우 ActiveControl에 대한 단일 호출만으로는 충분하지 않습니다. 상상해보십시오.
Form
TableLayoutPanel
FlowLayoutPanel
TextBox (focused)
(formInstance) .ActiveControl은 TextBox가 아닌 TableLayoutPanel에 대한 참조를 반환합니다 (ActiveControl은 리프 컨트롤을 찾고있는 동안 컨트롤 트리에서 즉시 활성 자식 만 반환하는 것처럼 보이므로).
이미 Windows API에 대한 다른 호출이있는 경우 Peters 솔루션을 사용해도 아무런 문제가 없습니다. 그러나 나는 그것에 대한 당신의 걱정을 이해하고 프레임 워크 기능 만 사용하여 당신과 유사한 솔루션을 사용하는 경향이 있습니다. 결국 성능 차이 (있을 경우)는 크지 않아야합니다.
비재 귀적 접근 방식을 사용합니다.
public static Control FindFocusedControl(Control control)
{
var container = control as IContainerControl;
while (container != null)
{
control = container.ActiveControl;
container = control as IContainerControl;
}
return control;
}
인터넷을 검색 한 후 George Shepherd의 Windows Forms FAQ 에서 다음을 발견했습니다.
.Net 프레임 워크 라이브러리는 집중된 컨트롤을 쿼리하는 API를 제공하지 않습니다. 이렇게하려면 Windows API를 호출해야합니다.
[씨#]
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는 것 이 다른 컨테이너 안에 중첩 될 수 있습니다 얼마나 깊이에 관계없이 그 법인의 활성 컨트롤을 반환하지 않습니다.
In your example if the TextBox has Focus : then : for Form, TableLayoutPanel, and FlowLayoutPanel : the 'ActiveControl property of all of them will be the TextBox !
Some, but not all, "genuine" ContainerControl types ... like Form and UserControl ... expose Key Events (in the case of Form : only if Form.KeyPreview == true can they be used) .
Other controls which, by design, contain other controls like TableLayOutPanel, GroupBox, Panel, FlowLayoutPanel, etc. are not type ContainerControl, and they do not expose KeyEvents.
Any attempt to cast instances of objects like TextBox, FlowLayoutPanel, TableLayoutPanel directly to ContainerControl will not compile : they are not type ContainerControl.
The code in the accepted answer, and in the next answer that corrects the first answer's spelling errors, will compile/accept instances of the above as parameters because you are "downcasting" them to type 'Control by making the parameter type 'Control
But in each case the cast to ControlContainer will return null, and the passed in instance will be returned (downcasted) : essentially a no-op.
And, yes, the modified answer code will work if you pass it a "genuine" ControlContainer, like a Form instance, which is in the parent inheritance path of the ActiveControl, but you are still just wasting time duplicating the function of 'ActiveControl.
So what are "genuine" ContainerControls : check them out : MS docs for ContainerControl
Only the answer by Peter really answers the explicit question, but that answer carries the price of using interop, and 'ActiveControl will give you what you need.
Also note that every Control (container or non-container) has a Controls Collection that is never null, and that a lot of (I've never tried all of them : why would I ?) the basic WinForms control let you do "crazy stuff" like adding Controls to the ControlCollection of 'simple' controls like Button without an error.
Now if the real intent of your question was to ask how you find the outermost ContainerControl ... that is not on the Form itself ... of a regular non-container Control nested some arbitrary levels deep ... you can use some of the ideas in the answer : but the code can be greatly simplified.
Regular Controls, ContainerControls, UserControls, etc. (but not Form !) all have a 'Container property you can access to get their immediate container, but making sure you have the 'final Container in their inhertance path that's not a Form requires some code to "walk-up" the inheritance tree, which is demonstrated here.
You may also wish to check out the 'HasChildren property of 'Control which is usually useful in dealing with issues of Focus, ActiveControl, and Select in WinForms. Reviewing the difference between Select and Focus can be valuable here, and SO has some good resources on that.
Hope this helps.
Hinek's solution works well for me, except it is ContainerControl, not ControlContainer. (Just in case you were scratching your head about that red squiggly line.)
public static Control FindFocusedControl(Control control)
{
ContainerControl container = control as ContainerControl;
while (container != null)
{
control = container.ActiveControl;
container = control as ContainerControl;
}
return control;
}
If you follow ActiveControl out recursively it doesn't take you to the leaf control that has focus?
ActiveControl doesn't always work out, like with SplitContainer, ActiveControl.Focused is false.
So for a more fool proof method could do something like this:
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;
}
'developer tip' 카테고리의 다른 글
Xcode에서 이미지 크기는 @ 1x, @ 2x 및 @ 3x 여야합니까? (0) | 2020.11.24 |
---|---|
64 비트 시스템에서 Java 32 비트 또는 64 비트의 int 크기가 있습니까? (0) | 2020.11.24 |
Oracle SQL Developer에서 Execute Explain Plan의 결과 이해 (0) | 2020.11.24 |
지속적 통합으로 실행되는 동안 WiX 3.0에서 오류 217이 발생합니다. (0) | 2020.11.24 |
Java 클라이언트에서 HTTP 서버로 파일 업로드 (0) | 2020.11.24 |