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.
Related
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:
I'm making a simple calculator to save myself from having to add up a bunch of invoice totals on basic calculator at work (since we use paper invoices). I'm just getting the basic functionalities in place currently and one of the functions I have is adding extra textboxes and then later trying to add all of the values in the textboxes to a subtotal. The problem I am having (I think) is that the compiler needs to have the the textbox ID's before the program is compiled. I'm also going to apologize for the sloppy variable names, I tried everything I could think of in my basic mindset until 4AM and by the time I was just using any variable. I've tried all the iterations, (ended up with do while statement as you can see).
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
int partboxes = 3;
int lastY = 83;
public Form1()
{
InitializeComponent();
}
private void buttonFinal_Click(object sender, EventArgs e)
{
AddParts();
}
public double AddParts()
{
int i = 1;
double megavalue = 0;
do
{
double totalsum = double.Parse(("partBox" + i).Text);
megavalue = totalsum + megavalue;
i = i + 1;
} while (i < partboxes);
string supervalue = megavalue.ToString();
lblPartsTotal.Text = supervalue;
return megavalue;
}
private void button1_Click(object sender, EventArgs e)
{
TextBox partBox = new TextBox();
partBox.Name = "partBox" + partboxes++;
partBox.Location = new System.Drawing.Point(12, lastY + 26);
partBox.Size = new System.Drawing.Size(100, 20);
// Add the textbox control to the form's control collection
this.Controls.Add(partBox);
lastY = lastY + 26;
partboxes = partboxes++;
}
}
}
You can get a textbox by name from the Controls property with
var tb = (TextBox)Controls["partBox" + i];
where i is the textbox number.
The indexer returns a Control. But since every control has a Text property, you can use it without casting.
double totalsum = double.Parse(Controls["partBox" + i].Text);
See: Control.ControlCollection Class
so I created my own private void that can create buttons
private void addButtonsToForm()
{
for (int i = 0; i < 26; i++)
{
Button currentNewButton = new Button();
currentNewButton.Size = new Size(20, 30);
currentNewButton.Location = new Point(20 + 25 * i, 420);
currentNewButton.Text = ((char)(65 + i)).ToString();
currentNewButton.Click += LetterClicked;
letters[i] = currentNewButton;
this.Controls.Add(letters[i]);
}
}
The buttons are alphabets and will be accessed when the user wants to choose a letter ... but the problem is I'm trying to figure out how to go back when the user clicked or selected a button..
Originally I wanted to do was i could just hide all the buttons created and just make the previous button visible but for some reason it only hides the only button that is clicked
//this is under private void LetterClicked(object sender, EventArgs e)
Button selectedLetter = (Button)sender;
selectedLetter.Enabled = false;
i thought of stupid codes like
addbuttonstoform().visible = false; but of course that won't work.. but you might get an idea to where i want to go.... it's a bit confusing to explain... I'm new in c# and i'm creating a guess the word game so help could be great..
Here is a working solution for your problem. See below:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Alphabets
{
public partial class Form1 : Form
{
private Button[] _letters;
public Form1()
{
InitializeComponent();
AddButtonsToForm();
}
private void AddButtonsToForm()
{
_letters = new Button[26];
for (int i = 0; i < 26; i++)
{
Button currentNewButton = new Button
{
Name = "BtnLetter"+ ((char)(65 + i)),
Size = new Size(20, 30),
Location = new Point(20 + 25 * i, 420),
Text = ((char) (65 + i)).ToString()
};
currentNewButton.Click += LetterClicked;
_letters[i] = currentNewButton;
this.Controls.Add(_letters[i]);
}
}
private void LetterClicked(object sender, EventArgs e)
{
var selectedLetter = (Button) sender;
//hide all other buttons
foreach (var letter in _letters)
{
if (letter.Text != selectedLetter.Text)
{
var buttons = this.Controls.Find("BtnLetter" + letter.Text, true);
buttons[0].Enabled = false;
}
}
}
}
}
This question already has answers here:
How to add event handler for dynamically created controls at runtime?
(3 answers)
How to add an event to a UserControl in C#?
(6 answers)
Closed 3 years ago.
I am trying to write a C# program that plays checkers. So far I've completed all the "Shallow" things such as generating the board and all the labels etc.
My problem is that all the controls are dynamically added. So I can't just "double click" them and define what to do at Button_Click event.
This is my code for generating the form
public partial class formGameBoard : Form
{
private readonly int m_BoardSize;
private readonly string m_Player1Name;
private readonly string m_Player2Name;
private GameTile m_BlueTile;
private bool m_IsThereBlue;
public formGameBoard(int i_BoardSize, string i_Player1Name, string i_Player2Name)
{
m_BoardSize = i_BoardSize;
m_Player1Name = i_Player1Name;
m_Player2Name = i_Player2Name;
if (m_Player2Name == "(Computer)")
{
m_Player2Name = "Computer";
}
m_IsThereBlue = false;
InitializeComponent();
}
private void formGameBoard_Load(object sender, EventArgs e)
{
int SizeOfButton = 60;
int ButtonRowindex = 0;
int ButtonColindex = 0;
this.Size = new System.Drawing.Size(30 + m_BoardSize * SizeOfButton, 100 + m_BoardSize * SizeOfButton);
Button[,] PlayButtonArray = new Button[m_BoardSize, m_BoardSize];
for (ButtonRowindex = 0; ButtonRowindex < m_BoardSize; ButtonRowindex++)
{
for (ButtonColindex = 0; ButtonColindex < m_BoardSize; ButtonColindex++)
{
PlayButtonArray[ButtonRowindex, ButtonColindex] = new Button();
PlayButtonArray[ButtonRowindex, ButtonColindex].Size = new Size(SizeOfButton, SizeOfButton);
PlayButtonArray[ButtonRowindex, ButtonColindex].Left = 10 + ButtonRowindex * SizeOfButton;
PlayButtonArray[ButtonRowindex, ButtonColindex].Top = 50 + ButtonColindex * SizeOfButton;
if ((ButtonRowindex + ButtonColindex) % 2 == 0)
{
PlayButtonArray[ButtonRowindex, ButtonColindex].Enabled = false;
PlayButtonArray[ButtonRowindex, ButtonColindex].BackColor = Color.Gray;
}
this.Controls.Add(PlayButtonArray[ButtonRowindex, ButtonColindex]);
}
}
FillButtons(PlayButtonArray);
}
public void FillButtons(Button[,] ButtonMatrix)
{
int i, j;
for (i = 0; i < m_BoardSize; i++)
{
for (j = 0; j < m_BoardSize; j++)
{
if ((i + j) % 2 == 1)
{
if (j <= (m_BoardSize / 2) - 2)
{
ButtonMatrix[i, j].Text = "O";
}
if (j > (m_BoardSize / 2))
{
ButtonMatrix[i, j].Text = "X";
}
}
}
}
}
struct GameTile
{
int RowIndex;
int ColumnIndex;
}
}
}
I have no issues with it, it looks very nice in my opinion
My problem is that all those buttons are dynamically added. I didnt drag and drop them. I created them at form load. Now I want something to happen when I click a button. For example, I want the button I clicked to change its color to blue.
How can I do that?
Adding the click event for a button is very simple, like this:
buttonName.Click += (sender, e) => { buttonName.Foreground = Colors.Blue; };
Or if you want to make it a method that will handle many button clicks, you could do this:
buttonName.Click += YourButtonHandler;
private void YourButtonHandler(object sender, EventArgs e)
{
// do something, for example:
Button b = sender as Button;
b.Foreground = Colors.Blue;
}
You can add a handler to Click event of your buttons and use sender parameter.
Also probably the index of button in the array is important for you, so you can store array index of the button in Tag property and use it later.
In the for loop:
var button = PlayButtonArray[ButtonRowindex, ButtonColindex];
button.Tag= new Point(ButtonRowindex, ButtonColindex);
button.Click += Button_Click;
Code for Button_Click:
private void Button_Click(object sender, EventArgs e)
{
var button = sender as Button;
//You can manipulate button here
//Also to extract the button index in array:
var indexes = (Point)button.Tag;
MessageBox.Show(string.Format("This is the button at {0}, {1}", indexes.X, indexes.Y));
}
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";
}