Apply one action to multiple objects C# - c#

I have different objects: 6 buttons, Listbox, and 5 labels, those names are like lblHum, lblCo2...
I need to show or hide them together with one button click.
Is it possible (without putting objects in a panel or group boxes) to apply, for example, Hide action, within 1 method for all objects without specifying hiding for each?
Like private void(List<objects>,action)

You can write something like this:
public void HideAll(params Control[] controls)
{
foreach(var control in controls) control.Hide();
}
Call it like this:
HideAll(lblHum, lblCo2, etc);
Or for something more flexible:
public void GroupAction(Action<Control> action, params Control[] controls)
{
foreach(var control in controls) action(control);
}
And then call it like this:
GroupAction(c => c.Hide(), lblHum, lblCo2, etc);
In both cases, though, the collection is still there. You just hid it with a method. And since the method is fairly simple, I'm not sure you get much value above putting the collection directly in your code. That is, you can make a list as a member of the form class and add the controls when the form is initialized. Then you can loop over the list whenever you want.

Since all controls inherit from the same base class (i.e. Control), you can do this with a generic method and passing in an Action. For example:
public void ApplyToAll<T>(IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
}
}
And call the method like this:
var items = new List<Control> { lblHum, lblCo2... etc };
ApplyToAll(items, x => x.Visible = false);

Related

Get all Controls of a Type, including nested containers [duplicate]

This question already has answers here:
How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?
(28 answers)
Closed 2 years ago.
I have a winforms project with a multi-level container setup in place. As in, I have a set of Panels on the top layer, each of those Panels has another set of Panels nested within them, and each of those sub-panels have a set of Controls (TextBoxes, CheckBoxes, etc.) in each one.
What I want is to be able to retrieve Controls of a certain type, given only the top level Panel. For example, one set of this using the names in my project would be MainPanelTruck which has a PanelTruckCheck Panel under it, which itself has 8 CheckBox nested in it. I want to be able to retrieve those 8 CheckBox given just MainPanelTruck.
I have been trying to get OfType<>() to work but as far as I can tell, it only searches among the next level down, so when I search for CheckBox using it, I get nothing as a result, because it only looks at the sub-panels.
This would be a lot easier if the Find method in ControlCollection allowed for a predicate or condition instead of a specific string as key, because it has the searchAllChildren bool which would be very handy here.
I can think of many ways I can get the items I want here, but none of them feel particularly elegant, which is what I'm seeking here. Thanks :)
You can write a simple (extension) method that returns all nested controls of given control:
public static class ControlExtensions
{
public static IEnumerable<Control> GetAllNestedControls(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
do
{
var control = stack.Pop();
foreach (Control child in control.Controls)
{
yield return child;
stack.Push(child);
}
}
while (stack.Count > 0);
}
}
With this in hand getting all checkboxes is easy:
panel1.GetAllNestedControls().OfType<CheckBox>()
You can also pass an optional boolean flag to search all children if you want. Check the flag before pushing child controls to stack. I would rename the method in this case to the name suggested by 00110001:
IEnumerable<Control> EnumerateControls(this Control root, bool searchAllChildren)
Usage would be
panel1.EnumerateControls(searchAllChildren: true).OfType<CheckBox>()
If I understand what you are asking, you want all controls of a certain type in a given parent container and include all sub containers
Given
public IEnumerable<T> EnumerateControls<T>(Control control) where T : Control
{
var queue = new Queue<Control>(new []{ control });
while (queue.Any())
{
var controls = queue.Dequeue().Controls;
foreach (var item in controls.OfType<T>())
yield return item;
foreach (Control subControl in controls)
queue.Enqueue(subControl);
}
}
Usage
var allCheckBoxes = EnumerateControls<CheckBox>(somePanel);

Refresh, Redraw, or Reload UserControl (UWP)

I wonder how can I reload the UserControl to it's original template from inside,
<UserControl x:Name="_UserControl">
<!-- other input controls and combos -->
<Button OnClick="Reload_UserControl"/>
</UserControl>
is there any this.reCreate() similar method?
My Answer is based on a answer posted for Windows Forms 8+ years back.
I have used the same logic from that answer and created extension method on the Root Grid to loop through and clear values of each element( Mimicking it to bring the UserControl back to its usual state)
Below is the Extension Method.
public static class Extensions
{
private static Dictionary<Type, Action<UIElement>> controldefaults = new Dictionary<Type, Action<UIElement>>()
{
{typeof(TextBox), c => ((TextBox)c).Text = String.Empty},
{typeof(CheckBox), c => ((CheckBox)c).IsChecked = false},
{typeof(ComboBox), c => ((ComboBox)c).SelectedIndex = 0},
{typeof(ListBox), c => ((ListBox)c).Items.Clear()},
{typeof(RadioButton), c => ((RadioButton)c).IsChecked = false},
};
private static void FindAndInvoke(Type type, UIElement control)
{
if (controldefaults.ContainsKey(type))
{
controldefaults[type].Invoke(control);
}
}
public static void ClearControls(this UIElementCollection controls)
{
foreach (UIElement control in controls)
{
FindAndInvoke(control.GetType(), control);
}
}
public static void ClearControls<T>(this UIElementCollection controls) where T : class
{
if (!controldefaults.ContainsKey(typeof(T))) return;
foreach (UIElement control in controls)
{
if (control.GetType().Equals(typeof(T)))
{
FindAndInvoke(typeof(T), control);
}
}
}
}
For usage, Give the RootGrid Some name. Say rootGrid. So all you need to call is
rootGrid.Children.ClearControls();
Also As mentioned in other answer, if you want to clear only specific controls, you can use
rootGrid.Children.ClearControls<TextBox>();
You can find the Extensions class on my Gist
Good Luck.
If what you mean is clearing the values entered by a user in various fields, then using MVVM makes it quite simple - you can either replace the view model with a new one and the bindings would update everything. You could also have a Clear method in the view model.
If what you mean is fixing the topology of the visual tree that might have been altered from the original - your best bet would be to replace your control with a new one.
Also worth noting - "template" is usually not a word associated with user controls. User controls define their visual tree in XAML, which is loaded every time the control is created. Templated controls use a template that is typically loaded once and applied to each control when it loads. See When to use a templated control over a UserControl? for some more potentially useful info.

How would I go about creating a list of Textbox items to use in other methods?

I have a series of 5 text boxes on a winforms form which are used to collect data as strings. I intend to use these boxes in multiple ways from a method to clear the contents of each, to a method which takes the data from each to check before exporting to a text file.
In order to do this I planned to create a List<Textbox> which would essentially be a list of all 5 boxes so I could later use code such as
foreach(Texbox box in *texboxList*)
{
box.Clear()
}
etc.
My only idea so far is to create a seperate method which adds all of the boxes to a list and then somehow pass the result of the method as a parameter to the relevant methods such as those that clear the boxes. The code I currently have is displayed below.
public List<TextBox> Clear_entered_data()
{
List<TextBox> textBoxes = new List<TextBox>();
textBoxes.Add(tbox1);
textBoxes.Add(tbox2);
textBoxes.Add(tbox3);
textBoxes.Add(tbox4);
textBoxes.Add(tbox5);
return textBoxes;
}
This is the code I'm using to generate the list of textboxes to use. I think the problem I'm having is understanding how this can be passed to other methods through parameters. The method I'd like to make use of the list is shown below here as I have it so far.
private void Clear_button_Click(object sender, EventArgs e, List<TextBox> textBoxes)
{
DialogResult Clear_validation = MessageBox.Show("Are you sure you would like to clear all data from the form?","Clear data?", MessageBoxButtons.YesNo);
if(Clear_validation == DialogResult.Yes)
{
foreach (TextBox box in textBoxes)
{
box.Clear();
}
}
}
With the code above I get the error upon running :
'Error 1 No overload for 'Clear_button_Click' matches delegate 'System.EventHandler''
But I've had no luck, please try and explain whats going wrong and help me find more appropriate solution!
Thanks
create a private class member that of list of textboxes List<TextBox> _textBoxes;
then after you call InitializeComponents(because before that your textboxex doesn't exists if you used the IDE to create them) place your code to add the textboxes into the list
_textBoxes = new List<TextBox>();
_textBoxes.Add(tbox1);
_textBoxes.Add(tbox2);
_textBoxes.Add(tbox3);
_textBoxes.Add(tbox4);
_textBoxes.Add(tbox5);
now you can use in your methods the _textBoxes that contains all your created TextBox
your public List Clear_entered_data() became
public List<TextBox> ClearTextBoxes() {
foreach(var textBox in _textBoxes){
textBox.Clear();
}
}
If you used a naming convention, you could just iterate over the form's control collection like this - in my example, the names all start with "SpecialTextBox":
foreach(Control c in someFormReference.Controls)
{
if (c.GetType() == typeof(TextBox) && c.Name.StartsWith("SpecialTextBox"))
{
// do your thing here
}
}

Looping through controls on a page using a masterpage

I'm trying to perform an operation on every control on a page that is inherited from a masterpage. I don't know how to access the child pages controls. I have tried recursively getting to my controls like this:
private void checkControls(ControlCollection controlcollection)
{
foreach (Control control in controlcollection)
{
if (control.Controls.Count > 0)
{
Debug.WriteLine(control.GetType().ToString());
checkControls(control.Controls);
}
else
{
Debug.WriteLine(control.GetType().ToString());
}
}
The method is called like this:
protected void resettodefault()
{
checkControls(this.Page.Controls);
}
However, the only controls that are printed from this execution are:
ASP.site_master
System.Web.UI.LiteralControl
I would prefer to access my controls directly (without recursion). Otherwise, how can I modify my recursion to get to the desired page's controls?
I would suggest using a base page instead of a master page, this way your logic for iterating over controls (and whatever you will do with that afterwards) is not tied to which master page a page is using.
As far as getting all the controls on the page, because the controls are hierarchical, as is the HTML they represent, so iterating over them recursively makes sense. However if you are dead set on not recursively getting controls something like this should work:
IEnumerable<Control> GetAllControls()
{
var allControls = new List<Control>();
var currentControls = new Queue<Control>();
currentControls.Enqueue(this.Page);
while (currentControls.Count >0)
{
var c = currentControls.Dequeue();
if (!allControls.Contains(c))
{
allControls.Add(c);
if (c.Controls != null && c.Controls.Count > 0)
{
foreach (Control e in c.Controls)
{
currentControls.Enqueue(e);
}
}
}
}
return allControls;
}
The last thing to consider is the lifecycle of the page, and when you iterate over the controls. If you try to walk to control tree too early not all controls may exist.
EDIT: Updated code.
Update
For validation purposes I would highly recommend using the built in validation controls of asp.net. You can read more about them here. This has the added benefit of providing validation on the client, providing faster UI responses and easing the load off the servers.
For resetting all the textboxes. I would recommend creating a new class for this purpose, then calling upon that class when needed instead of messing with the master page:
public class UIControlsHelper
{
public static void ClearTextboxes(Page page)
{
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType())
.ToList()
.ForEach(x => (TextBox)x.Text = string.Empty);
}
IEnumerable<Control> GetAllControls(Page page)
// Same as above, but with the page parameter replaced.
}
}
And in any of your pages:
UIControlsHelper.ClearTextboxes(this);
To access the controls in your child page do the following steps:
1-declare a variable of the type you want to access. For example if you want to access a Label in your child page use:
Label lbl_child=this.ContentPlaceHolder1.findcontrol("your label id in child page") as Label;
Now you have your label and you are free to make changes on it. Every change on this control will be reflected on the child control.
ContentPlaceHolder1 is your contentplace holder id so change it with your content id.
public void ClearTextboxes(Page page) {
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType()))
.ToList()
.ForEach(x => ((TextBox)x).Enabled=false);
}

Recursively notify child controls via C#

I have a form MainForm which is a Windows Forms form that contains many child controls. I want to call one function on MainForm that notifies all of its children. Does the Windows Forms form provide a means to do this? I played with update, refresh and invalidate with no success.
foreach (Control ctrl in this.Controls)
{
// call whatever you want on ctrl
}
If you want access to all controls on the form, and also all the controls on each control on the form (and so on, recursively), use a function like this:
public void DoSomething(Control.ControlCollection controls)
{
foreach (Control ctrl in controls)
{
// do something to ctrl
MessageBox.Show(ctrl.Name);
// recurse through all child controls
DoSomething(ctrl.Controls);
}
}
... which you call by initially passing in the form's Controls collection, like this:
DoSomething(this.Controls);
The answer from MusiGenesis is elegant, (typical in a good way), nice and clean.
But just to offer an alternative using lambda expressions and an 'Action' for a different type of recursion:
Action<Control> traverse = null;
//in a function:
traverse = (ctrl) =>
{
ctrl.Enabled = false; //or whatever action you're performing
traverse = (ctrl2) => ctrl.Controls.GetEnumerator();
};
//kick off the recursion:
traverse(rootControl);
No, there isn't. You must roll out your own.
On a side note - WPF has "routed events" which is exactly this and more.
You are going to need a recursive method to do this (as below), because controls can have children.
void NotifyChildren( control parent )
{
if ( parent == null ) return;
parent.notify();
foreach( control child in parent.children )
{
NotifyChildren( child );
}
}

Categories

Resources