What is the preferred/easiest way to find the control that is currently receiving user (keyboard) input in WinForms?
So far I have come up with the following:
public static Control FindFocusedControl(Control control)
{
var container = control as ContainerControl;
return (null != container
? FindFocusedControl(container.ActiveControl)
: control);
}
From a form, this can be called simply as (in .NET 3.5+ this could even be defined as an extension method on the form) -
var focused = FindFocusedControl(this);
Is this appropriate?
Is there a built-in method that I should be using instead?
Note that a single call to ActiveControl is not enough when hierarchies are used. Imagine:
Form
TableLayoutPanel
FlowLayoutPanel
TextBox (focused)
(formInstance).ActiveControl will return reference to TableLayoutPanel, not the TextBox (because ActiveControl seems to only be returning immediate active child in the control tree, while I'm looking for the leaf control).
If you have other calls to the Windows API already, there's no harm in using Peters solution. But I understand your worries about it and would tend to a similar solution as yours, using only the Framework functionalities. After all, the performance difference (if there is one) shouldn't be significant.
I would take a non recursive approach:
public static Control FindFocusedControl(Control control)
{
var container = control as IContainerControl;
while (container != null)
{
control = container.ActiveControl;
container = control as IContainerControl;
}
return control;
}
After searching the Internet, I found the following on George Shepherd's Windows Forms FAQ
The .Net framework libraries does not provide you an API to query for
the focused Control. You have to
invoke a windows API to do so:
[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;
}
}
ActiveControl on a Form or Container will return that entity's active control no matter how deeply it might be nested inside other containers.
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;
}
Related
I have code that dynamically loads (and destroys) user controls of different types onto a form as they are needed. The user control uses properties from its parent to load certain values into controls such as textboxes and check boxes. The issue is that in the line:
activeControl = new userControl1(params...);
my constructor is obviously going to try loading fields that don't exist yet (they belong to the parent form) since it hasn't been added to the parent form until after userControl1 is created.
I tried solving this using the ParentChanged event of the user control so it waits until its parent is set before loading fields. The problem here is when I destroy userControl1 and create a userControl2, the parent changes and it tries to load the fields from the null ParentForm (the exact problem I had originally.
Is there a preferred way to tackle this? Clearly, I could just add something like
if(this.ParentForm == null) { return; }
to the ParentChanged event or something along those lines. But this feels neither elegant nor safe. Am I overlooking some event or preferred way to accomplish this?
While it is not necessarily bad to check for null references and divert the code accordingly (e.g. not try to reference a missing object), the better approach here is to decouple your UserControl classes from their parents.
That is, these UserControl classes should not have any awareness of their parent form types at all. To do otherwise is to negate one of the major benefits of OOP: code reuse. With your current design, even if you get it to work without crashing, you can't ever use these UserControl objects except in the context of this specific parent form type.
You may think today that that's okay, that you'll never want to use them in any other context. But the future is hard to predict. You may decide, for example, that you want a variation on the parent form that should still use these same UserControl objects.
What you should be doing is exposing the UserControl state via public properties, and then allow the parent form to initialize those as necessary.
Here's a very simple example (I'm omitting the Designer-created codeā¦I assume that you can infer from the field names what I mean):
partial class UserControl1 : UserControl
{
/* ... */
public bool IsMyFlagChecked
{
get { return checkBox1.Checked; }
set { checkBox1.Checked = value; }
}
/* ... */
}
partial class ParentForm : Form
{
/* ... */
private void SomeMethodThatAddsUserControl1()
{
UserControl1 uc1 = new UserControl1;
uc1.IsMyFlagChecked =
this.SomeParentFormPropertyUserControl1UsedToReferenceDirectly;
// other initialization for uc1...
Controls.Add(uc1);
}
/* ... */
}
(Note that without a good, minimal, complete code example that illustrates your question, I can't provide a more detailed code example than the above. If this doesn't seem to address your question, please edit it so that it provides more detail, including a better code example).
I need to manipulate all controls on a form. I'm fine with accessing the Controls collection to do this. The problem comes with trying to include any controls contained within container controls such of GroupBox or Panel. I could recursively iterate the each Control's own Controls collection but this then accesses all constituent controls for non-design time containers.
Since my non-container controls all manage their constituent controls' state based on the their own properties I don't to start messing with constituent controls.
How can I determine if a control is a design time container so that I can avoid process those that are not?
I've tried checking for the Designer attribute but this returns null for both the ComboBox and the GroupBox:
foreach(Attribute attr in typeof(ctl).GetCustomAttributes(typeof(Attribute), false))
{
if(typeof(DesignerAttribute).IsAssignableFrom(attr.GetType()))
{
DesignerAttribute da = (DesignerAttribute)attr;
}
}
ctl is of type Control and in my testing is either Combox or GroupBox.
In both cases the GetCustomAttributes returns an array of 1 attribute which is the toolbox icon.
I've also tried checking assignability from to the ContainerControl class but they both are because, I assume, they will both contain controls at run time.
How do I detect a design time container?
In case Hans doesn't come back, and anyone is interested, this is my solution to the problem based on Hans Passant's suggestion:
public static bool IsContainerControl(this Control ctl)
{
if (ctl == null)
return false;
MethodInfo GetStyle = ctl.GetType().GetMethod("GetStyle", BindingFlags.NonPublic | BindingFlags.Instance);
if (GetStyle == null)
return false;
return (bool)GetStyle.Invoke(ctl, new object[] { ControlStyles.ContainerControl });
}
I am using c#.
Why does the CausesValidation property does not propagate from containers to child controls?
I have a TabControl and i need to perform validation of user input on all tabs.
But each tab is independent. I found out that if i have a container like a TableLayoutPanel and i set CausesValidation to false, the child componentes still perform validation.
So this code would not work:
Foreach(Control c in Container.Controls)
{
c.CausesValidation = False;
}
If you do some DEBUG output you will see that the only found control is the TableLayoutPanel or any other container like a GroupBox that will be found and set to False. But containers are not propagating that value to the child level.
A lot of people asked how we could solve the problem. I found a lot of methods, but i have created a static class that allows me to select wich tab on the TabControl that i want to perform validation, and it will set CausesValidation to False on ALL controls of that TabControl, including child controls with a deepness of 10 layers. If you want that library just ask for it!
My real question is, should not a container propagate that property to its child controls, and that child controls to any child controls?!
It would save us a lot of work from creating a very crazy code that is very specific for something that should work from scratch? Why is this not implied?
This just isn't a constructive way to deal with your requirement. The feature just wasn't implemented the way you like it to work and that's never going to change. It isn't a problem, you can easily add it yourself with a wee bit of code:
public static void SetCausesValidation(Control.ControlCollection ctls, bool enable) {
foreach (Control ctl in ctls) {
ctl.CausesValidation = enable;
SetCausesValidation(ctl.Controls, enable);
}
}
And use it in your form constructor, something like:
public Form1() {
InitializeComponent();
SetCausesValidation(panel1.Controls, false);
}
Note the use of recursion in the method to set the property through the entire tree of controls inside the container.
I have a question about design-time things:
I've made a component with an property "Links".
Those links are Controls. Now I want to make a UI-Dialog (for editing this property in the property grid).
How can I get all controls of the current form? I think the component has an connection to it, but where? I can't find anything.
Thanks :)
To get all of the controls of the current form then use the following code to get a collection of all of the controls on that form:
MyForm.Controls
See this MSDN help
Edit:
Perhaps these will help?
Design-time editor support for controls collection
http://social.msdn.microsoft.com/Forums/en-US/winformsdesigner/thread/64df27e7-8502-42ac-8634-cf8a8937d922/
Adding design-time support for a nested container in a custom/usercontrol (Winforms)
This is quite untrivial to do, I don't know of any examples of .NET components that do this. You can get to the form at design time with the Site property but there are problems. What's hard to deal with is the user deleting controls, ones that you have already added to your controls collection. I don't know of any good trigger to keep your collection valid, beyond also having to use a custom designer for the form or user control.
There's a better mousetrap for this, you see it being used by the HelpProvider and ErrorProvider components for example. Note how they add properties to all other controls on the form. This is done by implementing the IExtenderProvider interface. There's an excellent example of this in the MSDN library article.
You can get IDesignerHost service at design-time. This service has a property called Container which has Components. Then for each component, get INestedContainer service and then get all components from that service.
This is how Document Outline window works. I've changed their method to use List<IComponent> as return value:
List<IComponent> GetSelectableComponents(IDesignerHost host)
{
var components = host.Container.Components;
var list = new List<IComponent>();
foreach (IComponent c in components)
list.Add(c);
for (var i = 0; i < list.Count; ++i)
{
var component1 = list[i];
if (component1.Site != null)
{
var service = (INestedContainer)component1.Site.GetService(
typeof(INestedContainer));
if (service != null && service.Components.Count > 0)
{
foreach (IComponent component2 in service.Components)
{
if (!list.Contains(component2))
list.Add(component2);
}
}
}
}
return list;
}
To filter the result to contain just controls, you can call result.TypeOf<Control>().
Not sure if this is what you want.
I "lost" a label control by accidentally removing it's text property.
After looking here at this discussion I finally realized that by accessing ANY control property at design time I could use the drop - down at the top of the properties window to locate the control name. Selecting the name revealed the location of the control on the form and exposed it's properties in the properties editor.
I have a Form and a UserControl. The UserControl has a menu, and the form has a tabstrip (General, Food, Vitamins etc).
In the UserControl, I have the following code: (Form name is frmForm, the tab names in the form are tabGeneral,tabFood, tabVitamins)
frmForm fm=new frmForm();
fm.tabMain.Selected=tabVitamins;
I call these line from the UserControl to capture the tab to get selected on the form, but it does not select the vitamins tab.
Where am I going wrong? I have access specifier as Protected Internal for tabs in the form.
Please advice.
Thanks,
Karthick
When you write new frmForm(), you're creating a completely new instance of frmForm, which is then discarded.
To get the frmForm instance that holds your control, call the FindForm() method and cast to frmForm.
For example:
frmForm myForm = FindForm() as frmForm;
if(myForm != null)
myForm.tabMain.SelectedTab = myForm.tabVitamins;
If the control is on some other form, this code won't do anything.
By the way, Hungarian notation is frowned upon in .Net.
Your form should probably be named something like MainForm.
SLaks has correctly pointed out your fundamental error, and given you a valid example of a way, via a call to the method 'FindForm, to get the Form the UserControl is sited on.
It may be valuable to you to keep in mind that a UserControl (and all Controls) also has a 'Parent property, but, of course, a UserControl could be placed inside another Control on a Form (like your UserControl could be inside a Panel on the Form) : in that case the UserControl's Parent would be the control it's inside on the Form (like, a Panel), not the Form itself, but 'FindForm will do the right thing to get you the Form it's on.
However you are calling a Method every time you use 'FindForm, and "best practice" suggests that what you want to do is to "inject" a reference to the Form into the UserControl at run-time so that it can always access its Form property easily, without calling a 'Method.
In your example, on a practical level, this (calling the Method) may make almost no difference in performance, but, imho, as you get to a place with WinForms and .NET where you might have a UserControl that will need access to its Parent Form very frequently, this will pay off, and it's a better way to structure your code in the long run, for maintenance.
Wes showed you one way you can "embed" (inject) the UserControl's hosting Form : using an overloaded constructor for the UserControl. But that requires you to modify the Designer.cs file in standard WinForms, and I strongly advise you against that, even though it will work. Particularly if you are just "getting your feet on the ground" in .NET, I strongly advise you against modifying it, or anything having to do with the Form's constructor and its internal call to : InitializeComponent();
Also, as you progress with WinForms you are going to meet many situations where you are going to want instances of "objects" (a Control, a Form, an instance of a Class) to contain references to other instances of "objects.
If you can understand and use one simple use of "injection" here, you are going to make progress to make yourself ready to handle more complex .Net programming in the future.
Another way is to put a Public Property in the UserControl that can be set in code from the MainForm. In the UserControl something like :
private frmForm ParentForm;
public frmForm UCParentForm
{
set { ParentForm = value; }
}
So then in your main form's code, perhaps in the Load event like this :
private void frmForm_Load(object sender, EventArgs e)
{
TheUserControl.UCParentForm = this;
}
or when you need to, you set the UserControl's 'ParentForm property once. So you have eliminated using the method 'FindForm().
In this case, if you only want access to a specific control on the UserControl's Parent Form, like a TabControl, you might consider that you want to make the Property you set of type TabControl, rather than Form : the same coding technique shown above can be used in the UserControl :
private TabControl mainFormTabControl;
public TabControl MainFormTabControl
{
set { mainFormTabControl = value; }
}
imho, it is when you are creating UserControls dynamically at run-time, using an overloaded constructor, as Wes suggests, is the best strategy. And using overloaded constructors has many, many others uses in .NET that you'll get into.
good luck !
You should not be creating a new frmForm() inside the user control. You could pass a reference to the frmForm to the user control.
In your user control constructor try something like this.
private frmForm fm;
public YourUserControl(frmForm fm)
{
this.fm = fm;
}
Then you could use.
fm.tabMain.Selected=tabVitamins;
Does that help?