I wrote this recursive function to find name of radio button in each tab page:
private static DevExpress.XtraEditors.RadioGroup FindRadioGroupInTabPage(System.Windows.Forms.Control parentControl)
{
if (!parentControl.HasChildren)
{
return null;
}
foreach (var ct in parentControl.Controls.OfType<System.Windows.Forms.Control>())
{
if (ct is DevExpress.XtraEditors.RadioGroup rdg)
{
return rdg;
}
else
{
FindRadioGroupInTabPage(ct);
return null;
}
}
return null;
}
But it always returns Null. What's wrong with my code?
Use the following and in RadioButtonList change from RadioButton to DevExpress.XtraEditors.RadioGroup.
public static class ControlExtensions
{
public static IEnumerable<T> Descendants<T>(this Control control) where T : class
{
foreach (Control child in control.Controls)
{
if (child is T thisControl)
{
yield return (T)thisControl;
}
if (child.HasChildren)
{
foreach (T descendant in Descendants<T>(child))
{
yield return descendant;
}
}
}
}
public static List<RadioButton> RadioButtonList(this Control control)
=> control.Descendants<RadioButton>().ToList();
}
Usage where this is the current form
List<RadioButton> radioButtons = this.RadioButtonList();
Can be used on say a panel
List<RadioButton> radioButtonInPanel = panel1.RadioButtonList();
I have an asp.net dashboard site that allows a user to load HTML templates from a dropdownlist. There are multiple types of DevExpress components on the page, including the ASPxDockPanel. If a user changes templates I get an error that the dockpanel already exists, I would like to include a recursive function like the one below that checks to see if any ASPxDockPanels are present on the page, and if they are present remove them. This works for only the first dock panel then bombs out. I think this is because an enumerable set of controls cannot be modified while looping through it. How can I loop though the controls and remove the dock panels at runtime?
protected void LoadTableTemplate(string selectedTemplate, int currentMode)
{
FindAllDockPanels(this);
}
public void FindAllDockPanels(Control ctrl)
{
if (ctrl != null)
{
foreach (Control control in ctrl.Controls)
{
if (control is ASPxDockPanel)
{
ctrl.Controls.Remove(control);
control.Dispose();
}
FindAllDockPanels(control);
}
}
}
Use a temporary collection, like so:
public void FindAllDockPanels(Control ctrl) {
if (ctrl != null) {
List<Control> remove = new List<Control>();
foreach (Control control in ctrl.Controls) {
if (control is ASPxDockPanel) {
remove.Add( control );
}
}
foreach(Control control in remove) {
control.Controls.Remove( control );
control.Dispose(); // do you really need to dispose of them?
}
FindAllDockPanels(control);
}
}
If you find yourself doing this often, it might be worth moving these "DelayedDelete" actions to an extension method, like so:
public static void DelayedRemove<T>(this IEnumerable<T item> collection, T itemToRemove) {
// add it to a private static dictionary bound to the `collection` instance.
}
public static void DelayedRemoveFinish(this IEnumerable<T item> collection) {
// empty the private static dictionary in here
}
then you'd use it like so:
public void FindAllDockPanels(Control ctrl) {
if (ctrl != null) {
foreach (Control control in ctrl.Controls) {
if (control is ASPxDockPanel) control.Controls.DelayedRemove( control );
}
control.Controls.DelayedRemoveFinish();
FindAllDockPanels(control);
}
}
Much cleaner, no? :)
There is a way to get who lost his focus in a c# form without using the LostFocus event each component?
[edit]
I need for a On Screen Keyboard.
I need to store last focussed control to fire keypress, but i need to do it to all in the window.
Also the main project is wpf, than i have some component nested as itemsTemplate and so on...
I finally used this:
foreach (Control uie in FindInLogicalTreeDown(this, typeof(TextBox))) AssignEvents(uie);
private static IEnumerable<DependencyObject> FindInLogicalTreeDown(DependencyObject obj, Type type)
{
if (obj != null)
{
if (obj.GetType() == type) { yield return obj; }
foreach (object child in LogicalTreeHelper.GetChildren(obj))
if (typeof(DependencyObject).IsAssignableFrom(child.GetType()))
foreach (var nobj in FindInLogicalTreeDown((DependencyObject)child, type)) yield return nobj;
}
yield break;
}
void AssignEvents(Control element)
{
element.GotMouseCapture += new MouseEventHandler(Component_GotFocus);
}
public Control LastFocus { get; set; }
public void Component_GotFocus(object sender, RoutedEventArgs e)
{
LastFocus = (Control)sender;
if (LastFocus.GetType() == typeof(TextBox)) { KeyboardVisible = true; }
}
i don't think there is any way until unless you subscribe events and keep track which lost focus event has fired last
Does anyone have a piece of code to make all Controls (or even all TextBoxes) in a Form which is read-only at once without having to set every Control to read-only individually?
Write an extension method which gathers controls and child controls of specified type:
public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
var children = control.Controls.OfType<T>();
return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}
Gather TextBoxes on the form (use TextBoxBase to affect RichTextBox, etc - #Timwi's solution):
IEnumerable<TextBoxBase> textBoxes = this.GetChildControls<TextBoxBase>();
Iterate thru collection and set read-only:
private void AreTextBoxesReadOnly(IEnumerable<TextBoxBase> textBoxes, bool value)
{
foreach (TextBoxBase tb in textBoxes) tb.ReadOnly = value;
}
If want - use caching - #igor's solution
In Form:
if (_cached == null)
{
_cached = new List<TextBox>();
foreach(var control in Controls)
{
TextBox textEdit = control as TextBox;
if (textEdit != null)
{
textEdit.ReadOnly = false;
_cached.Add(textEdit);
}
}
}
else
{
foreach(var control in _cached)
{
control .ReadOnly = false;
}
}
Add recursion also (Controls can be placed into other controls (panels)).
You should be able to write yourself a utility function to do this. You can iterate over the form’s controls and then each control’s child controls recursively. For example:
public static void SetEnableOnAllControls(Control parentControl, bool enable)
{
parentControl.Enabled = enable;
foreach (Control control in parentControl.Controls)
SetEnableOnAllControls(control, enable);
}
[...]
// inside your form:
SetEnableOnAllControls(this, false);
This doesn’t take care of ToolStrips, which aren’t controls. You could write a separate, similar method for those.
Notice that the above disables the form itself too. If you don’t want that, try this:
public static void SetEnableOnAllChildControls(Control parentControl, bool enable)
{
foreach (Control control in parentControl.Controls)
{
control.Enabled = enable;
SetEnableOnAllChildControls(control, enable);
}
}
If you really meant the ReadOnly property, which is only relevant for TextBoxes, try this:
public static void SetReadOnlyOnAllControls(Control parentControl, bool readOnly)
{
if (parentControl is TextBoxBase)
((TextBoxBase) parentControl).ReadOnly = readOnly;
foreach (Control control in parentControl.Controls)
SetReadOnlyOnAllControls(control, readOnly);
}
I would use reflection to check to see if the generic Control object has an Enabled property.
private static void DisableControl(Control control)
{
PropertyInfo enProp = control.GetType().GetProperty("Enabled");
if (enProp != null)
{
enProp.SetValue(control, false, null);
}
foreach (Control ctrl in control.Controls)
{
DisableControl(ctrl);
}
}
this.Enabled = false;
Depends on what you doing really, you might want to consider putting the control within a panel and disabling that.
I haven't tested this, but it should work:
foreach (var textBox in this.Controls.OfType<TextBox>())
textBox.ReadOnly = true;
Edit: This is not such a good solution it seems: see Timwi's comment.
I just developed a recursive solution that handles any kind of Web Control, using a simple static method and ASP.NET polymorphysm.
/// <summary>
/// Handle "Enabled" property of a set of Controls (and all of the included child controls through recursivity)
/// By default it disable all, making all read-only, but it can also be uset to re-enable everything, using the "enable" parameter
/// </summary>
/// <param name="controls">Set of controls to be handled. It could be the entire Page.Controls set or all of the childs of a given control (property "Controls" of any Control-derived class)</param>
/// <param name="enable">Desired value of the property "enable" of each control. </param>
public static void DisableControls(ControlCollection controls, bool enable = false)
{
foreach (Control control in controls)
{
var wCtrl = control as WebControl;
if (wCtrl != null)
{
wCtrl.Enabled = enable;
}
if (control.Controls.Count > 0)
DisableControls(control.Controls, enable);
}
}
/// <summary>
/// Enable a set of Controls (and all of the included child controls through recursivity).
/// Friendly name for DisableControls(controls, true), that achieve the same result.
/// </summary>
/// <param name="Controls">Set of controls to be handled. It could be the entire Page.Controls set or all of the childs of a given control (property "Controls" of any Control-derived class)</param>
public static void EnableControls(ControlCollection controls)
{
DisableControls(controls, true);
}
This is tested and looks fairly fast (less than a millisecond in a web form with 25+ controls to be disabled).
If you prefer an extension method, i think it should be enough to change the solution as follows:
public static void DisableControls(this Control control, bool enable = false)
{
foreach (Control ctrl in control.Controls)
{
var wCtrl = ctrl as WebControl;
if (wCtrl != null)
{
wCtrl.Enabled = enable;
}
if (ctrl.Controls.Count > 0)
ctrl.DisableControls(enable);
}
}
public static void EnableControls(this Control control)
{
control.DisableControls(true);
}
How do I get available controls from a Windows Forms form using C#?
Or, ProfK's solution in enumerable syntax:
public static IEnumerable<Control> GetControls(Control form) {
foreach (Control childControl in form.Controls) { // Recurse child controls.
foreach (Control grandChild in GetControls(childControl)) {
yield return grandChild;
}
yield return childControl;
}
}
Try this method in your form. It will recursively get all controls on your form, and their children:
public static List<Control> GetControls(Control form)
{
var controlList = new List<Control>();
foreach (Control childControl in form.Controls)
{
// Recurse child controls.
controlList.AddRange(GetControls(childControl));
controlList.Add(childControl);
}
return controlList;
}
Then call it with a:
List<Control> availControls = GetControls(this);
I think you mean all controls on the form.
So simply you can use Controls property inside your form object.
foreach(Control c in this.Controls)
{
//TODO:
}