Collect controls in a form c# - c#

I have a form in which there are some buttons. I'd like put their references in an array.Is it possible with a foreach ?
I want to do this:
public Form1()
{
InitializeComponent();
Button[] all = new Button[5];
all[0] = button1;
all[1] = button2;
all[3] = button3;
all[4] = button4;
}
I've already tried
int i=0;
foreach (Button p in Form1)
{
all[i]= p;
i++;
}
But I can't use a foreach on a Form.
The same thing if the buttons are in a panel.
What can I do to collect all buttons quickly?
Thanks :)

You're looking for the Controls collection of your form or container, which contains every control directly in it.
Beware that this will also include non-Buttons; call .OfType<Button>() to filter.
So instead of the foreach you can initialize an array like this:
Button[] all = this.Controls.OfType<Button>().ToArray();

Every Control has a Controls property which is a ControlCollection. You can get all Buttons on a Control (as a Form or a Panel) like this:
foreach(var button in control.Controls.OfType<Button>())
{ ... }
But this will only give you the Buttons that are contained directly by this control. If you want to get all Buttons in your Form on all Panels, GroupBoxs etc, you need to recurse through the Controlslike in this example:
public class Form1 : Form
{
// ...
private static IEnumerable<Button> GetAllButtons(Control control)
{
return control.Controls.OfType<Button>().Concat(control.Controls.OfType<Control>().SelectMany(GetAllButtons));
}
private void DoSomethingWithAllButtons()
{
foreach(var button in GetAllButtons(this))
{ // do something with button }
}
}

Related

How to find a control from controls of controls collection recursively?

My control "MyTextBox1" add dynamically on form1 under container1 control. This form1 can be child of form2 and form2 can be child of form3 and so on how can I find my control from multi controls collection?
e.g. MyTextBox1 exists in
form3.form2.form1.Container1.MyTextBox1
how to find my control by name from multi control collections?
I do not want to use recursive foreach control collection. I am looking for an smart/short code like controls.Find().
If you don't want to put it recoursive, you can try BFS (Breadth First Search); let's implement it as an extension method:
public static class ControlExtensions {
public static IEnumerable<Control> RecoursiveControls(this Control parent) {
if (null == parent)
throw new ArgumentNullException(nameof(parent));
Queue<Control> agenda = new Queue<Control>(parent.Controls.OfType<Control>());
while (agenda.Any()) {
yield return agenda.Peek();
foreach (var item in agenda.Dequeue().Controls.OfType<Control>())
agenda.Enqueue(item);
}
}
}
Then you can use it as
// Let's find Button "MyButton1" somewhere on MyForm
// (not necessary directly, but may be on some container)
Button myBytton = MyForm
.RecoursiveControls()
.OfType<Button>()
.FirstOrDefault(btn => btn.Name == "MyButton1");

How to create shortcut of a button on a panel at runtime?

I have a panel that contains a lot of buttons(right panel). I want to add a shortcut of selected button to another panel(left panel) with the same properties and events dynamically at runtime.
Buttons have so many properties like image, text, backcolor, forecolore, ... etc.
Also the buttons will open new form inside main panel:
private void butntest_Click(object sender, EventArgs e)
{
this.main_panel.Controls.Clear();
Form1 myForm = new Form1();
myForm.TopLevel = false;
myForm.AutoScroll = true;
this.main_panel.Controls.Add(myForm);
myForm.Show();
}
How Can i create a shortcut on left panel?
You can create a clone method which accepts a button as input and creates another button based on the input button's properties, also handle click event of the cloned button and just call PerformClick method of the input button:
public Button Clone(Button input)
{
var output = new Button();
output.Text = input.Text;
// do the same for other properties that you need to clone
output.Click += (s,e)=>input.PerformClick();
return output;
}
Then you can use it this way:
var btn = Clone(button1);
panel1.Controls.Add(btn);
Also instead of a panel, it's better to use a FlowLayoutPanel or TableLayoutPanel, so you don't need to handle the location and the layout yourself.
Note: If it's a dynamic UI and users can reorder command buttons or create whatever you called shortcut, the probably for the next step you may need to store the status of the panel to be able to reload buttons at the next load of the application after the application closed. In this case it's better to consider a pattern like command pattern. Then you can have your commands as classes. The then you can say which button is responsible to run which command at run-time and you can simply store the relation between buttons and commands using their names.
Create class Button like so
Button leftpannelbutton = new Button();
leftpannelbutton = button1.Clone();
Now leftpannelbutton is eqaul to button1. Now just add that to your form.
Find Below ( Reflection)
public static class ControlExtensions
{
public static T Clone<T>(this T controlToClone)
where T : Control
{
PropertyInfo[] controlProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
T instance = Activator.CreateInstance<T>();
foreach (PropertyInfo propInfo in controlProperties)
{
if (propInfo.CanWrite)
{
if(propInfo.Name != "WindowTarget")
propInfo.SetValue(instance, propInfo.GetValue(controlToClone, null), null);
}
}
return instance;
}
}

C# Fill an array with all the buttons being used in Windows form

I am trying to fill an array with all the Buttons being used in Form1.
Button[] ButtonArray = new Button[5];
ButtonArray[0] = button1;
ButtonArray[1] = button2;
ButtonArray[2] = button3;
ButtonArray[3] = button4;
ButtonArray[4] = button5;
This code works fine.
But if I have for example 100 buttons it is a long procedure.
If all the Buttons are on the form you can try using Linq:
using System.Linq;
...
Button[] ButtonArray = Controls
.OfType<Button>()
.ToArray();
Edit: in case you have some buttons within groupboxes, panels (i.e. not directly on the form, but on some kind of container), you have to elaborate the code into something like this
private static IEnumerable<Button> GetAllButtons(Control control) {
IEnumerable<Control> controls = control.Controls.OfType<Control>();
return controls
.OfType<Button>()
.Concat<Button>(controls
.SelectMany(ctrl => GetAllButtons(ctrl)));
}
...
Button[] ButtonArray = GetAllButtons(this).ToArray();
See How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)? for details
you can try this one:
ButtonArray[] buttonArray = new ButtonArray[this.Controls.OfType<Button>().Count()]
int i =0; //index for button array
foreach(var button in this.Controls.OfType<Button>()) //Iterate through each button control
{
buttonArray [i++] = button;
}
Enumerable.OfType doesn't search controls down the control-hierarchy. So if you want to find controls recursively you could use this extension method:
public static IEnumerable<T> RecursiveControlsOfType<T>(this Control rootControl) where T : Control
{
foreach (Control child in rootControl.Controls)
{
if (child is T targetControl)
yield return targetControl;
foreach (T targetControlChild in child.RecursiveControlsOfType<T>())
yield return targetControlChild;
}
}
Usage:
Button[] nonRecursiveButtons = this.Controls.OfType<Button>().ToArray();
Button[] recursiveButtons = this.RecursiveControlsOfType<Button>().ToArray();
List<Button> Buttons = new List<Button>();
foreach (var item in this.Controls) // Looping through all controls in the form
{
if (item is Button) // if the current is a button we add it
{
Buttons.Add(item as Button);
}
}

How do I add form on tab control in c#

I have a form page with text boxes and data grid view and other forms that contain a tab control. I want to add the first form tab in the second form. I tried to write the code for the form to appear but it is larger than the tab container and doesn't fit. Only half of the form appears.
This is my code:
private void tcMainPage_SelectedIndexChanged(object sender, EventArgs e)
{
if (tcMainPage.SelectedIndex == 0)
{
GTOWN.PrintingPage BookInfo = new PrintingPage();
BookInfo.TopLevel = false;
BookInfo.FormBorderStyle = FormBorderStyle.None;
BookInfo.Dock = DockStyle.Fill;
tpSearch.Controls.Add(BookInfo);
BookInfo.Show();
}
}
this is the form
and that is what appears
Set your main FORM as a Container.
yourForm.IsMdiContainer = true;
Then add the child form to the tabPage:
private void tcMainPage_SelectedIndexChanged(object sender, EventArgs e)
{
if (tcMainPage.SelectedIndex == 0)
{
PrintingPage newFrm = new PrintingPage
{
MdiParent = this,
// This set the form parent as the tabClicked
Parent = tcMainPage.TabPages[0]
};
newFrm.Show();
}
}
my tab form work good in the same code
thank you all my code was correct but the problem was in tab property i deleted the tab and add another one and the code is working now
thank you
I face this issue and I create this if may help
public void addform(TabPage tp, Form f)
{
f.TopLevel = false;
//no border if needed
f.FormBorderStyle = FormBorderStyle.None;
f.AutoScaleMode = AutoScaleMode.Dpi;
if (!tp.Controls.Contains(f))
{
tp.Controls.Add(f);
f.Dock = DockStyle.Fill;
f.Show();
Refresh();
}
Refresh();
}
Forms are top-most objects and cannot be placed inside of other containers.
You may want to refactor your code so that the items on your Form are on a UserControl instead. At that point you can then add that UserControl to both a Form and a TabControl
public UserControl myControl(){ /* copy your current view code here */}
public Form myForm(){
Controls.Add(new myControl());
}
public Form myTabbedForm(){
var tabControl = new TabControl();
var page1 = new TabPage();
page1.Controls.Add(new myControl());
tabControl.TabPages.Add(page1);
this.Controls.Add(tabControl);
}

Getting controls in a winform to disable them

I'm trying to get all controls in a winform disabled at the Load event.
I have a form (MDI) which loads a Login Form. I want to disable the controls behind the Login Form to only let the user enter his username and password, and then if the user is valid re-enable the controls again.
Just show the login form as a modal dialog, i.e., frm.ShowDialog( ).
Or, if you really want to disable each control, use the Form's Controls collection:
void ChangeEnabled( bool enabled )
{
foreach ( Control c in this.Controls )
{
c.Enabled = enabled;
}
}
I suggest doing it this way instead of simply setting the Form's Enabled propery because if you disable the form itself you also disable the tool bar buttons. If that is ok with you then just set the form to disabled:
this.Enabled = false;
However, if you are going to do this you may as well just show the login prompt as a modal dialog :)
Simple Lambda Solution
form.Controls.Cast<Control>()
.ToList()
.ForEach(x=>x.Enabled = false);
Container like Panel control that contains other controls
then I used queue and recursive function get all controls.
for (Control control in GetAllControls(this.Controls))
{
control.Enabled = false;
}
public List<Control> GetAllControls(Control.ControlCollection containerControls, params Control[] excludeControlList)
{
List<Control> controlList = new List<Control>();
Queue<Control.ControlCollection> queue = new Queue<Control.ControlCollection>();
queue.Enqueue(containerControls);
while (queue.Count > 0)
{
Control.ControlCollection controls = queue.Dequeue();
if (controls == null || controls.Count == 0)
continue;
foreach (Control control in controls)
{
if (excludeControlList != null)
{
if (excludeControlList.SingleOrDefault(expControl => (control == expControl)) != null)
continue;
}
controlList.Add(control);
queue.Enqueue(control.Controls);
}
}
return controlList;
}
Just for some fun with linq, because you can.....
What you could do is create a "BatchExecute" extension method for IEnumerable and update all your controls in 1 hit.
public static class BatchExecuteExtension
{
public static void BatchExecute<T>(this IEnumerable<T> list, Action<T> action)
{
foreach (T obj in list)
{
action(obj);
}
}
}
Then in your code....
this.Controls.Cast<Control>().BatchExecute( c => c.enabled = false);
Cool.
I agree that ShowDialog is the way to go, but to answer the original question, you can do this if you want to disable all controls:
foreach (Control c in this.Controls)
{
c.Enabled = false;
}
As Ed said, showing the form as a modal dialog will do what you want. Be sure to check the dialog result returned from ShowDialog in case they cancel it instead of clicking login.
But if you really want to disable all the controls on the form then you should be able to just disable the form itself, or some other parent control like a panel that has all controls in it. That will disable all child controls. This will also allow the child controls to go back to their previous state when the parent control is enabled again.
Trying the ShowDialog show this exception:
Form that is not a top-level form cannot be displayed as a modal dialog box. Remove the form from any parent form before calling showDialog.
What im doing is this:
private void frmControlPanel_Load(object sender, EventArgs e)
{
WindowState = FormWindowState.Maximized;
ShowLogin();
//User = "GutierrezDev"; // Get user name.
//tssObject02.Text = User;
}
private void ShowLogin()
{
Login = new frmLogin
{
MdiParent = this,
Text = "Login",
MaximizeBox = false,
MinimizeBox = false,
FormBorderStyle = FormBorderStyle.FixedDialog,
StartPosition = FormStartPosition.CenterScreen
};
Login.ShowDialog();
}

Categories