How to create MaterialSingleLineTextField Controls dynamically - c#

I would like to add MaterialSingleLineTextField dynamically to a Form.
I have used MaterialSkin NuGet package:
I am trying to create multiple MaterialSkin TextBoxes dynamically on Form.Load. But no Controls are displaying in the hosing Panel.
private void Form1_Load(object sender, EventArgs e)
{
int n = 5;
int pointX = 30;
int pointY = 40;
//panel1.Controls.Clear();
for (int i = 0; i < n - 1; i++)
{
MaterialSingleLineTextField a = new MaterialSingleLineTextField();
a.Text = (i + 1).ToString();
a.Visible = true;
a.Location = new Point(pointX, pointY);
panel1.Controls.Add(a);
panel1.Show();
pointY += 20;
}
}
This code block works perfectly fine for normal TextBoxes.
Is there any way to add MaterialSingleLineTextField dynamically?

A sample Form initialization with default Theme values.
The MaterialSkinManager is initialized in the Form Constructor, setting the Theme to MaterialSkinManager.Themes.LIGHT and default color scheme. The Form base Type is set to MaterialForm.
A specified number of MaterialSingleLineTextField controls is added to a parent container (a Panel), starting from a defined Location downwards.
These Controls are anchored to the parent and the Height is set to Parent.Font.Height + 4.
It's important that you specify the Size of these Controls, otherwise you'll get a minimal size that prevents the Controls from showing their content.
As you can see in the AddTextFields() method, it's important that you dispose of the previous Controls added to the Parent container, if you want to replace the existing with new ones. Even more important using this Library.
Calling the Clear() method of a Control.Controls collection (as in the line you have commented out) doesn't dispose of anything, those controls are still alive.
public partial class Form1 : MaterialForm
{
private readonly MaterialSkinManager msManager = null;
public Form1()
{
InitializeComponent();
msManager = MaterialSkinManager.Instance;
msManager.AddFormToManage(this);
msManager.Theme = MaterialSkinManager.Themes.LIGHT;
}
private void Form1_Load(object sender, EventArgs e)
{
AddTextFields(panel1, 5, new Point(30, 10), false);
}
private void AddTextFields(Control parent, int controlsCount, Point startPosition, bool ClearExisting)
{
if (clearExisting && parent.Controls.Count > 0) {
for (int i = parent.Controls.Count - 1; i >= 0; i--) {
parent.Controls[i].Dispose();
}
}
int controlHeight = parent.Font.Height + 4;
int yIncrement = 0;
for (int i = 0; i < controlsCount; i++) {
var textField = new MaterialSingleLineTextField() {
Text = (i + 1).ToString(),
Size = new Size(parent.ClientSize.Width - startPosition.X - 4, controlHeight),
Location = new Point(startPosition.X, startPosition.Y + yIncrement),
Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right
};
parent.Controls.Add(textField);
yIncrement += (controlHeight + 10);
}
}
private void matBtnChangeTheme_Click(object sender, EventArgs e)
{
msManager.Theme = MaterialSkinManager.Themes.DARK;
msManager.ColorScheme = new ColorScheme(Primary.Blue600, Primary.Blue900, Primary.Blue500, Accent.LightBlue200, TextShade.WHITE);
}
private void matBtnAddControls_Click(object sender, EventArgs e)
{
AddTextFields(panel1, 7, new Point(30, 10), true);
}
}
Sample functionality:

Related

Position buttons in control array

I'm trying to recall the buttons I created and position them on arrays that hold its location but FindControl gives Error.
'Form1' does not contain a definition for 'FindControl' and no extension method
'FindControl' accepting a first argument of type 'Form1' could be found (are you missing
a using directive or an assembly reference?)
My code is under a timer1.
private void timer1_Tick(object sender, EventArgs e)
{
...
if ...
{
...
Button btn = new Button();
{
btn.Name = "Btn-" + tail.ToString();
btn.Height = 10;
btn.Width = 10;
btn.Tag = tail+1;
btn.Location = new Point((stailX[1]-1)*10, (stailY[1] - 1) * 10);
}
this.Controls.Add(btn);
}
...
for (int i = 1; i <= tail; i++)
{
((Button)this.FindControl("Btn-" + tail.ToString())).Location = new Point((stailX[i] - 1) * 10, (stailY[i] - 1) * 10);
}
}
I've omitted parts of code not important. Please help.
My approach would include putting the controls in an array or a list for easy access later. Here is an example:
public partial class Form1 : Form
{
List<Button> buttonList = new List<Button>();
int i = 0;
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
i++;
Button btn = new Button();
btn.Location = new Point(20 , i* 30);
buttonList.Add(btn); // add instance to list
this.Controls.Add(btn); // add the same instance to the form
for (int j = 0; j < buttonList.Count; ++j)
{
// do whatever you want here
buttonList[j].Text = j.ToString();
}
}
}
This way you don't need to find the control you want on every iteration.
I think you can achieve the same, by iterating through each controls in the form using this.Controls, and can be more specifically by filtering them using .OfType<Button>(). To make sure that those controls are dynamically created with specific Ids you can add the conditions as well. The code for this will looks like the following:
int i = 1;
foreach (Button childButton in this.Controls.OfType<Button>())
{
if (childButton.Name.StartsWith("Btn-"))
{
childButton.Location = new Point((stailX[i] - 1) * 10, (stailY[i] - 1) * 10);
}
i++;
}
Hope that it will help you
How about Controls.Find("controlname", true)

Which Design pattern for dynamical creating buttons

I have created a custom user control that adds combo boxes to itself.
My custom user control has 1 button which Adds Two Combo Boxes.
Clicking on it adds 2 combo boxes under the last two.
When the selected index is changed on any of the combo boxes it will run a calculation using the selected values in each box.
This is my code so far...
public partial class PlateSectionAdder : UserControl
{
private List<Dictionary<string, CueComboBox>> listOfTwoCombos = new List<Dictionary<string, CueComboBox>>();
public List<string> ColList { get; }
public List<string> PlateList { get; }
private int count = 1;
private int gap = 3;
private int edge = 5;
private Button btnAdd = new Button();
public PlateSectionAdder(List<string> collist, List<string> platelist)
{
InitializeComponent();
this.ColList = collist;
this.PlateList = platelist;
btnAdd.FlatStyle = FlatStyle.Flat;
btnAdd.Top = edge / 2;
btnAdd.Left = edge;
btnAdd.Width = this.Width - (edge * 2);
btnAdd.Click += BtnAdd_Click;
this.Controls.Add(btnAdd);
}
private void BtnAdd_Click(object sender, EventArgs e)
{
CueComboBox cmbCols = new CueComboBox();
cmbCols.DataSource = this.ColList;
cmbCols.Top = (btnAdd.Bottom + edge) * count;
cmbCols.Left = edge;
cmbCols.Width = btnAdd.Width / 2 - gap;
cmbCols.SelectedIndexChanged += CalculatePlateSections;
CueComboBox cmbPlates = new CueComboBox();
cmbPlates.DataSource = this.PlateList;
cmbPlates.Top = (btnAdd.Bottom + edge) * count;
cmbPlates.Left = cmbCols.Right + gap;
cmbPlates.Width = btnAdd.Width / 2 - gap;
cmbCols.DataSource = ColList;
this.Controls.Add(cmbCols);
this.Controls.Add(cmbPlates);
this.Height = btnAdd.Height + (cmbCols.Height * count) + edge + edge + 1;
cmbCols.DataSource = ColList;
cmbPlates.DataSource = PlateList;
cmbPlates.SelectedIndexChanged += CalculatePlateSections;
count++;
}
private void CalculatePlateSections(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
The combo boxes seem to be copies of the same instance, rather than independent, I thought by using the new keyword they would be each there own instance.
I am sure there a design pattern out there that would be right for this but not sure which one.
Has a look at the builder pattern but it does not seem exactly what I am looking for.
if there is anything unclear about this let me know and I will clarify.

Reference a button outside of a loop?

This function dynamically creates nine buttons for use in a game I am making. You can see what attributes I give to the button.
private void createbuttons()
{
int tot = 0;
int x = 100;
int y = 100;
while(tot < 9)
{
string buttonsname = (tot + "button").ToString();
Button creating = new Button();
creating.Name = buttonsname;
creating.Size = new Size(100, 100);
creating.Click += delegate
{
MessageBox.Show("You clicked me!");
};
creating.Text = buttonsname;
if(x > 300)
{
y += 100;
x = 100;
}
creating.Location = new Point(x, y);
Controls.Add(creating);
tot += 1;
x += 100;
}
}
What I want to know is how to reference these buttons in different parts of the same form. Specifically when 'Start Game' is clicked I want to change the text for each button to something different.
private void button10_Click(object sender, EventArgs e)
{
//What would I write here to change the text?
}
You can access the buttons by enumerating the controls, or you could create a list of buttons for future reference, and use that list later.
Here is how you do it with a list:
private IList<Button> addedButtons = new List<Button>();
private void createbuttons() {
int tot = 0;
int x = 100;
int y = 100;
while(tot < 9) {
string buttonsname = (tot + "button").ToString();
Button creating = new Button();
creating.Name = buttonsname;
creating.Size = new Size(100, 100);
creating.Click += delegate {
MessageBox.Show("You clicked me!");
};
creating.Text = buttonsname;
if(x > 300) {
y += 100;
x = 100;
}
creating.Location = new Point(x, y);
addedButtons.Add(creating); // Save the button for future reference
Controls.Add(creating);
tot += 1;
x += 100;
}
}
Now you can do this:
foreach (var btn : addedButtons) {
btn.Text = "Changed "+btn.Text;
}
The form has a property Controls that holds all child controls. To find a child control by its Name property use method Find, which returns the array, because there may be several control with the same Name, but if you make sure that names exist, are unique, and you know their type (Button) you can just take the first item from the array and cast it:
private void button10_Click(object sender, EventArgs e)
{
Button buttonNamedFred = (Button)this.Controls.Find("Fred", false)[0];
buttonNamedFred.Text = "I'm Fred";
}

Array of buttons: change property

I have an array of buttons, like this:
int x = 0, y = 0;
butt2 = new Button[100];
for (int i = 0; i < 100; i++)
{
butt2[i] = new Button();
int names = i;
butt2[i].Name = "b2" + names.ToString();
butt2[i].Location = new Point(525 + (x * 31), 70 + (y * 21));
butt2[i].Visible = true;
butt2[i].Size = new Size(30, 20);
butt2[i].Click += new EventHandler(butt2_2_Click); //problem lies here (1)
this.Controls.Add(butt2[i]);
}
private void butt2_2_Click(object sender, EventArgs e)
{
// want code here
}
I want to change the back color of the button when clicked. I was thinking of passing i to be able to do this:
butt2[i].BackColor = Color.Green;
This should do the trick:
private void butt2_2_Click(object sender, EventArgs e)
{
Button pushedBtn = sender as Button;
if(pushedBtn != null)
{
pushedBtn.BackColor = Color.Green;
}
}
And this holds for most UI events, the 'object sender' parameter refers to the control that 'sent'/'fired' the event.
To learn more about C# event handling, I would start here.
Also, here is a SO question about GUI event handling, answered nicely by Juliet (accepted answer).
Hope this helps.

Referencing a newly created control

If i create a control on the fly, as below
private void button10_Click(object sender, EventArgs e)
{
CheckedListBox CheckedListBox1 = new CheckedListBox();
CheckedListBox1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(255)), ((System.Byte)(224)), ((System.Byte)(192)));
CheckedListBox1.ItemHeight = 16;
CheckedListBox1.Location = new System.Drawing.Point(12, 313);
CheckedListBox1.Name = "CheckedListBox1";
CheckedListBox1.Size = new System.Drawing.Size(168, 244);
CheckedListBox1.TabIndex = 0;
Controls.Add(CheckedListBox1);
Button button12 = new Button();
button12.Location = new Point(900, 500);
button12.Size = new Size(75, 23);
button12.Click += new System.EventHandler(button12_Click);
button12.Name = "button12";
button12.Text = "Toggle All";
Controls.Add(button12);
}
what is the best way to reference that control from a function outside of the local scope? would it be best creating a static class to somehow hold a reference to the control that can be accessed outside the local scope or is there a findcontrol function for winforms ( i think findcontrol is just for web).
i want
private void button12_Click(object sender, EventArgs e)
{
for (int i = 0; i <= (CheckedListBox1.Items.Count - 1); i++)
{
if (CheckedListBox1.GetItemCheckState(i) == CheckState.Checked)
{
CheckedListBox1.SetItemCheckState(i, CheckState.Indeterminate);
}
else if (CheckedListBox1.GetItemCheckState(i) == CheckState.Indeterminate)
{
CheckedListBox1.SetItemCheckState(i, CheckState.Checked);
}
}
}
to be able to work but im going wrong because of scope? pls help a newbie
thanks
I'm assuming the two functions button12_Click and button10_Click are members of a From class. In this case, your should make your CheckListBox1 and button12 members of the From class. That way, the button12_Click will be able to reference the controls you will have created.
public partial class Form1 : Form
{
CheckedListBox CheckedListBox1 = null;
Button button12 = null;
private void button10_Click(object sender, EventArgs e)
{
CheckedListBox1 = new CheckedListBox();
CheckedListBox1.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(255)), ((System.Byte)(224)), ((System.Byte)(192)));
CheckedListBox1.ItemHeight = 16;
CheckedListBox1.Location = new System.Drawing.Point(12, 313);
CheckedListBox1.Name = "CheckedListBox1";
CheckedListBox1.Size = new System.Drawing.Size(168, 244);
CheckedListBox1.TabIndex = 0;
Controls.Add(CheckedListBox1);
button12 = new Button();
button12.Location = new Point(900, 500);
button12.Size = new Size(75, 23);
button12.Click += new System.EventHandler(button12_Click);
button12.Name = "button12";
button12.Text = "Toggle All";
Controls.Add(button12);
}
private void button12_Click(object sender, EventArgs e)
{
for (int i = 0; i <= (CheckedListBox1.Items.Count - 1); i++)
{
if (CheckedListBox1.GetItemCheckState(i) == CheckState.Checked)
{
CheckedListBox1.SetItemCheckState(i, CheckState.Indeterminate);
}
else if (CheckedListBox1.GetItemCheckState(i) == CheckState.Indeterminate)
{
CheckedListBox1.SetItemCheckState(i, CheckState.Checked);
}
}
}
}
When there is only one CheckedListBox make it a class variable. But when you have always only one CheckedListBox - why do you create it dynamically?
If you're adding the controls to the page's Controls collection, just go look there. If you know the index of the control you can reference it that way. if you're adding the control to some container's Control's collection (say, a panel), look for it there

Categories

Resources