I may be going about this the wrong way, so I'll set out the full scenario...
I have a DataTable which holds a number of items - like stock items. The data in this table can change, but it's populated from a database so that it's a distinct list. I want users to be able to select a number of them and I want to do this by creating a new checkBox object for each item in my DataTable.
So far I have the following (which I know is wrong, but illustrates what I'm trying to get at!):
string cbName = "cbNewTest";
int cbPosition = 24;
int cbTab = 1;
foreach (DataRow row in tblAllTests.Rows)
{
string cbNewName = cbName + cbTab.ToString();
this.(cbNewName) = new System.Windows.Forms.CheckBox();
this.testInfoSplitContainer.Panel2.Controls.Add(this.(cbNewName));
this.(cbNewName).AutoSize = true;
this.(cbNewName).Location = new System.Drawing.Point(6, cbPosition);
this.(cbNewName).Name = cbNewName;
this.(cbNewName).Size = new System.Drawing.Size(15, 14);
this.(cbNewName).TabIndex = cbTab;
this.(cbNewName).Text = row["itemDesc"].ToString();
cbPosition = cbPosition + 22;
cbTab = cbTab + 1;
}
So of course the problem is the stuff in the brackets. Essentially, I want this to be whatever is in my string 'cbNewName' but I really don't know how to do this...I'm used to SQL as I'm a database gal, so this probably means I've coded this all wrong...
Any help would be very much appreciated...I'm very new to C# (or for that matter, any programming outside a database) so simple terms would be appreciated!
You can create a CheckBox as a variable, just like anything else. No need to assign it to one of the Form's properties, which are impossible to generate dynamically regardless:
CheckBox newCheckBox = new CheckBox();
// (Initialize your new CheckBox here, basically exactly as you're
// already doing except instead of this.(cbNewName) you use newCheckBox)
this.testInfoSplitContainer.Panel2.Controls.Add(newCheckBox);
If you need to access it later, since you're already setting the name, just do:
(CheckBox)this.testInfoSplitContainer.Panel2.Controls["theName"]
Just create a Dictionary of checkboxes:
var mycbs = new Dictionary<string,<System.Windows.Forms.CheckBox>>();
foreach (DataRow row in tblAllTests.Rows)
{
string cbNewName = cbName + cbTab.ToString();
mycbs[cbNewName] = new System.Windows.Forms.CheckBox();
this.testInfoSplitContainer.Panel2.Controls.Add(mycbs[cbNewName]);
mycbs[cbNewName].AutoSize = true;
mycbs[cbNewName].Location = new System.Drawing.Point(6, cbPosition);
mycbs[cbNewName].Name = cbNewName;
mycbs[cbNewName].Size = new System.Drawing.Size(15, 14);
mycbs[cbNewName].TabIndex = cbTab;
mycbs[cbNewName].Text = row["itemDesc"].ToString();
cbPosition = cbPosition + 22;
cbTab = cbTab + 1;
}
you can create a check box object and set the name of the check box like
CheckBox c = new CheckBox();
c.Name = "CheckBoxName";
and when you need to access this check box you can differentiate between then using the name like :
// Loop through all controls
foreach (Control tempCtrl in this.Controls)
{
// Determine whether the control is CheckBoxName,
// and if it is, do what ever you want
if (tempCtrl.Name == "CheckBoxName")
{
// ...
}
}
I'm not entirely sure I understand your question, but if your intent is to 1) add a new checkbox control to the panel and 2) keep a reference to that new control object, which you can find later based on a string value. If that's right, then:
Add a Dictionary to your class, something like:
using System.Collections.Generic;
using System.Windows.Forms;
...
IDictionary checkboxes = new Dictionary();
Create your new checkbox and assign it as an ordinary variable, e.g.:
CheckBox cb = new CheckBox();
this.testInfoSplitContainer.Panel2.Controls.Add(cb);
cb.AutoSize = true;
// etc...
Store a reference to the variable in the dictionary, like so:
lock (((System.Collections.ICollection)checkboxes).SyncRoot)
{
checkboxes[cbNewName] = cb;
}
Clear out the dictionary in your form's overriden Dispose method, e.g., checkboxes.Clear().
Related
I´d like to be able to acces a Checkbox (or Textbox or similar) by just adding the last digit or letter like this:
int number = 1;
CheckBox tempCheckbox = "myform.checkBoxTool" + number;
tempCheckbox.Checked = true;
I guess this is already covered but I cant seem to find the right search words.
You have several opportunities. The most obvious is to use the ControlsCollection of your Form:
var checkBox = myForm.Controls["myform.checkBoxTool" + number];
Alternativly go with ControlCollection.Find:
var checkBox = myForm.Controls.Find("myform.checkBoxTool" + number, true).FirstOrDefault();
However if you have multiple checkboxes that all differ only by a single index, it´s a better idea to store them as a list or array of CheckBox in the first place:
List<CheckBox> myCheckBoxes = new List<CheckBox>();
Now you can easily access them by index:
var checkBox = myChekBoxes[number];
Be aware that indices - as any number in .NET - are zero-based. So the very first element in the list has index zero.
Technically, if tempCheckbox is on the same form where the querying code is, you can try Linq;
using System.Linq;
...
public partial class MyForm : Form {
...
int number = 1;
CheckBox tempCheckbox = this
.Controls
.Find($"checkBoxTool{number}", true) // we don't want "myform." here
.OfType<CheckBox>()
.FirstOrDefault();
// If check box found, check it
if (tempCheckbox != null)
tempCheckbox.Checked = true;
A better approach is to organize these controls into a collection, e.g. Dictionary:
public partial class MyForm : Form {
private Dictionary<int, CheckBox> m_CheckBoxTools = new Dictionary<int, CheckBox>();
public MyForm() {
InitializeComponent();
m_CheckBoxTools.Add(1, checkBoxTool1);
m_CheckBoxTools.Add(3, checkBoxTool3);
m_CheckBoxTools.Add(25, checkBoxTool25);
}
Then you can query the dictionary
if (m_CheckBoxTools.TryGetValue(number, out CheckBox tempCheckbox)) {
tempCheckbox.Checked = true;
}
you can use something like below
CheckBox chkList1 =new CheckBox();
chkList1.Text = strCheckboxText;
chkList1.ID="Chk"+intControlIndex;
chkList1.Font.Name = "Verdana";
chkList1.Font.Size = 9;
chkList1.Checked = true;
this.Form.Controls.Add(chkList1);
then you can check existing check box and find your ID. Assuming checkbox is in Panel control.
string str1="";
foreach (Control c in panel1.Controls)
{
if((c is CheckBox) && ((CheckBox) c).Checked)
str1 += c.ID+ ", ";
}
str1=str1.Trim();
Hi when I create textboxes on Windows Application Form I cannot name it as box[0], box[1] and so on. The purpose why I want to do like this is because I want to use them in a loop.
Actually I found TextBox[] array = { firstTextBox, secondTextBox }; works too!
How about making a list of them after you create them? In your form initialization function, you can do something like:
List<TextBox> myTextboxList = new List<TextBox>();
myTextBoxList.Add(TextBox1);
myTextBoxList.Add(TextBox2);
mytextBoxList.Add(TextBox3);
Now you can itterate through with your "myTextboxList" with something like below:
Foreach (TextBox singleItem in myTextboxList) {
// Do something to your textboxes here, for example:
singleItem.Text = "Type in Entry Here";
}
You can create textboxes on runtime and just put them in an array...
If you want to do it in design time, you will have to do some control filtering logic on the whole this.Controls array in order to access only the wanted textboxes. Consider if (currControl is TextBox) if all textboxes in the form are ones you want in the array.
Another option for design time, is putting all wanted textboxes in a panel which will be their parent, and then iterating over the panel's children (controls) and cast them to TextBox.
A runtime solution would be something like:
var arr = new TextBox[10];
for (var i = 0; i < 10; i++)
{
var tbox = new TextBox();
// tbox.Text = i.ToString();
// Other properties sets for tbox
this.Controls.Add(tbox);
arr[i] = tbox;
}
I wouldn't use an array for this, personally. I would use some form of generic collection, like List.
List<TextBox> textBoxList = new List<TextBox>();
//Example insert method
public void InsertTextBox(TextBox tb)
{
textBoxList.Add(tb);
}
//Example contains method
public bool CheckIfTextBoxExists(TextBox tb)
{
if (textBoxList.Contains(tb))
return true;
else
return false;
}
You don't necessarily have to use the Contains method, you could also use Any(), or maybe even find another way- all depends on what you're doing. I just think using a generic collection gives you more flexibility than a simple array in this case.
for C# just use this to create an array of text boxes
public Text [] "YourName" = new Text ["how long you want the array"];
then add the text boxes to the array individually.
TextBox Array using C#
// Declaring array of TextBox
private System.Windows.Forms.TextBox[] txtArray;
private void AddControls(int cNumber)
{
// assign number of controls
txtArray = new System.Windows.Forms.TextBox[cNumber + 1];
for (int i = 0; i < cNumber + 1; i++)
{
// Initialize one variable
txtArray[i] = new System.Windows.Forms.TextBox();
}
}
TextBox[] t = new TextBox[10];
for(int i=0;i<required;i++)
{
t[i]=new TextBox();
this.Controls.Add(t[]);
}
I'm trying to add data to a datagrid (in fact, any control that presents data in a grid will do), but the columns (both names and numbers) are not known until runtime.
The columns I DO know how to create: E.g
DataGridTextColumn textColumn = new DataGridTextColumn();
textColumn.Header = column.DisplayName;
MyDataGrid.Columns.Add(textColumn);
But how do I add rows? I don't see how I can use binding because my data is not contained in an object with known properties. For example, data for each row might come in as a string[]. So one time I might have three columns, another time I might have five.
I was expecting do be able to do something like this:
// Example data to represent a single row.
string[] row1 = new[] { "value1", "value2", "value3" };
var row = new Row;
row.AddCell(row1[0]);
row.AddCell(row1[1]);
row.AddCell(row1[2]);
MyDataGrid.Rows.Add(row);
I'd have to start plugging away in VS to get my head round the exact code, but you can most likely just create your columns and use the column key as the binding expression seeing as indexed bindings work in WPF
I'll get some code up in a minute - but it would look something like your row creation code but with bindings on the columns that look something like (forgive the possibly incorrect method names)
textColumn.Bindings.Add(new Binding("this[" + columnIndex.ToString() + "]"));
Update:
Yeah not sure if this is what you are looking for but it works:
Created a single window with a datagrid on it (dataGrid1)
public MainWindow()
{
InitializeComponent();
var col = new DataGridTextColumn();
col.Header = "Column1";
col.Binding = new Binding("[0]");
dataGrid1.Columns.Add(col);
col = new DataGridTextColumn();
col.Header = "Column2";
col.Binding = new Binding("[1]");
dataGrid1.Columns.Add(col);
col = new DataGridTextColumn();
col.Header = "Column3";
col.Binding = new Binding("[2]");
dataGrid1.Columns.Add(col);
//dataGrid1.ad
List<object> rows = new List<object>();
string[] value;
value = new string[3];
value[0] = "hello";
value[1] = "world";
value[2] = "the end";
rows.Add(value);
dataGrid1.ItemsSource = rows;
}
Haven't played with datagrids a lot but you can try something like this
int currentRow = MyDataGrid.Rows.Add();
MyDataGrid.Rows[currentRow].Cells[0].Value = row1[0];
MyDataGrid.Rows[currentRow].Cells[1].Value = row1[1];
MyDataGrid.Rows[currentRow].Cells[2].Value = row1[2];
Not sure what is the best way to word this, but I am wondering if a dynamic variable name access can be done in C# (3.5).
Here is the code I am currently looking to "smarten up" or make more elegant with a loop.
private void frmFilter_Load(object sender, EventArgs e)
{
chkCategory1.Text = categories[0];
chkCategory2.Text = categories[1];
chkCategory3.Text = categories[2];
chkCategory4.Text = categories[3];
chkCategory5.Text = categories[4];
chkCategory6.Text = categories[5];
chkCategory7.Text = categories[6];
chkCategory8.Text = categories[7];
chkCategory9.Text = categories[8];
chkCategory10.Text = categories[9];
chkCategory11.Text = categories[10];
chkCategory12.Text = categories[11];
}
Is there a way to do something like ("chkCategory" + i.ToString()).Text?
Yes, you can use
Control c = this.Controls.Find("chkCategory" + i.ToString(), true).Single();
(c as textBox).Text = ...;
Add some errorchecking and wrap it in a nice (extension) method.
Edit: It returns Control[] so either a [0] or a .Single() are needed at the end. Added.
for(...)
{
CheckBox c = this.Controls["chkCategory" + i.ToString()] as CheckBox ;
c.Text = categories[i];
}
You can do that with reflection. But don't.
It's more proper to instantiate a list of contols, add them programmatically to your form, and index that.
Sometimes it can help to put your controls into an array or collection as such:
Checkbox[] chkCataegories = new Checkbox[] { chkCategory1, chkCategory2 ... };
for(int i = 0; i < chkCategories.Length; i++)
chkCategories[i].Text = categories[i];
As another approach, you can dynamically create your checkboxes at runtime instead of design time:
for(int i = 0; i < categories.Length; i++)
{
Checkbox chkCategory = new chkCategory { Text = categories[i] };
someContainer.Controls.Add(chkCategory);
}
At least with dynamically created controls, you don't need to modify your GUI or your form code whenever you add new categories.
You don't need dynamic for that. Put chkCategory1 - 12 in an array, and loop through it with a for loop. I would suggest you keep it around in a field and initialize it at form construction time, because chkCategory seems to be related. But if you want a simple example of how to do it in that simple method, then it would be something like this:
private void frmFilter_Load(object sender, EventArgs e)
{
var chkCategories = new [] { chkCategory1, chkCategory2, chkCategory3, .......... };
for(int i = 0 ; i < chkCategories.Length ; i++ )
chkCategoies[i].Text = categories[i];
}
You know more about the application, so you could perhaps avoid writing out all the control names - for instance, if they are placed on a common parent control, then you could find them by going through it's children.
No, but you could do something like this (untested, beware of syntax errors):
private readonly CheckBox[] allMyCheckboxes = new CheckBox[] { chkCategory1, chkCategory2, ... }
Then you just need to do
for (i = 0; i < 12; i++) allMyCheckboxes[i].Text = categories[i];
The "this.Controls["chkCategory" + i.ToString()]" and "this.Controls.Find("chkCategory" + i.ToString(), true)" both do not work... the former informs you that the contents of the [] are not an int and the latter that ControlCollection does not contain a definition for Find.
Use "Control myControl1 = FindControl("TextBox2");" instead.
I needed this form as I was looping through another array, extracting values and using them to populate form fields. Much easier to look for label1, label2, label3, etc.
I'm using a DataGridView binding its datasource to a List, and specifying the properties for each column.
An example would be:
DataGridViewTextBoxColumn colConcept = new DataGridViewTextBoxColumn();
DataGridViewCell cell4 = new DataGridViewTextBoxCell();
colConcept.CellTemplate = cell4;
colConcept.Name = "concept";
colConcept.HeaderText = "Concept";
colConcept.DataPropertyName = "Concept";
colConcept.Width = 200;
this.dataGridViewBills.Columns.Add(colConcept);
{... assign other colums...}
And finally
this.dataGridViewBills.DataSource=billslist; //billslist is List<Bill>
Obviously Class Bill has a Property called Concept, as well as one Property for each column.
Well, now my problem, is that Bill should have and Array/List/whateverdynamicsizecontainer of strings called Years.
Let's assume that every Bill will have the same Years.Count, but this only known at runtime.Thus, I can't specify properties like Bill.FirstYear to obtain Bill.Years[0], Bill.SecondYear to obtain Bills.Years[1]... etc... and bind it to each column.
The idea, is that now I want to have a grid with dynamic number of colums (known at runtime), and each column filled with a string from the Bill.Years List. I can make a loop to add columns to the grid at runtime depending of Bill.Years.Count, but is possible to bind them to each of the strings that the Bill.Years List contains???
I'm not sure if I'm clear enough.
The result ideally would be something like this, for 2 bills on the list, and 3 years for each bill:
--------------------------------------GRID HEADER-------------------------------
NAME CONCEPT YEAR1 YEAR2 YEAR3
--------------------------------------GRID VALUES-------------------------------
Bill1 Bill1.Concept Bill1.Years[0] Bill1.Years[1] Bill1.Years[2]
Bill2 Bill2.Concept Bill2.Years[0] Bill2.Years[1] Bill2.Years[2]
I can always forget the datasource, and write each cell manually, as the MSFlexGrid used to like, but if possible, I would like to use the binding capabilities of the DataGridView.
Any ideas? Thanks a lot.
I recently ran into this same problem. I ended up using DataGridView's virtual mode instead of binding to a data source. It doesn't have exactly the same features as binding, but it's still a lot more powerful than populating each cell manually.
In virtual mode, the DataGridView will fire an event whenever it needs to display a cell, which essentially means you can populate the cell however you please:
private void my_init_function() {
datagridview.VirtualMode = true;
datagridview.CellValueNeeded += new System.Windows.Forms.DataGridViewCellValueEventHandler(datagridview_CellValueNeeded);
}
private void datagridview_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
e.Value = get_my_data(e.RowIndex, e.ColumnIndex);
}
You could use reflection to set up and fill the DataGridView. I've done this with a single type, but I don't see why it couldn't be extended to your data structure.
To set up the DataGridView:
// Create the columns based on the data in the album info - get by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
dataTable.TableName = t.Name;
foreach (PropertyInfo p in t.GetProperties())
{
// IF TYPE IS AN ARRAY (OR LIST) THEN ADD A COLUMN FOR EACH ELEMENT
var columnSpec = new DataColumn();
// If nullable get the underlying type
Type propertyType = p.PropertyType;
if (IsNullableType(propertyType))
{
var nc = new NullableConverter(propertyType);
propertyType = nc.UnderlyingType;
}
columnSpec.DataType = propertyType;
columnSpec.ColumnName = p.Name;
dataTable.Columns.Add(columnSpec);
}
dataGridView.DataSource = dataTable;
Then to populate the DataGridView:
// Add album info to table - add by reflection
var ai = new AlbumInfo();
Type t = ai.GetType();
// WOULD NEED TO INCREASE BY LENGTH OF ARRAY
var row = new object[t.GetProperties().Length];
int index = 0;
foreach (PropertyInfo p in t.GetProperties())
{
// IF TYPE IS AN ARRAY (OR LIST) THEN ADD EACH ELEMENT
row[index++] = p.GetValue(info, null);
}
dataTable.Rows.Add(row);
This is just the code I used, so you'll have to modify the code to handle your year array/list.