I try to find out, how it is possible to access an object through a string which has the same name as the object name.
for example, I want to change the property of n times of Buttons using for loop
public static object GetObject(string ObjectName)
{
// this Method has to return an Object through his name
}
for (int i = 1; i < 4; i++)
{
GetObject(Convert.ToString("Button" +i) ).Text = Convert.ToString(i);
}
}
}
this code has the same function of this code
Button1.Text = "1";
Button2.Text = "2";
Button3.Text = "3";
You can develop different types of applications using C#. i.e. Web, WinForms, WPF. They have different types of Control and Type. I'm assuming that you are developing a WinForms application. In that case, you can use the Controls property of a WinForm to access all the Controls of a Form.
Please check the below code block for the implementation:
public object GetObject(string ObjectName)
{
// this Method has to return an Object through his name
Control myControl = Controls.Find(ObjectName, true).FirstOrDefault();
if (myControl != null)
{
// Do Stuff
return myControl;
}
else return null;
}
private void RenameButtons()
{
for (int i = 1; i < 4; i++)
{
//GetObject(Convert.ToString("Button" + i)).Text = Convert.ToString(i);
object btn = GetObject(Convert.ToString("Button" + i));
if (btn != null) ((Button)btn).Text = Convert.ToString(i);
}
}
You will find more details about the Controls property by following this link.
you can try this
foreach (Control control in Controls)
{
var btn = control as Button;
if ( btn != null && btn.Name.StartsWith("Button") )
{
var i= btn.Name.Substring(6, 1)
//if( i.Convert.ToInt32() <4 ) //optional
btn.Text = i;
}
}
Related
I've provided, in a seperated class, some methods to create an element dynamically. Creating that element dynamically is not the problem but routing the event.
My thoughts where that the "path" (like namespace.class.method) is submitted via a parameter in the creating method.
But up to now, I was not able to attach such an event listener. I did many researches using Google and StackOverflow, but without any success.
I post the source code of the method below.
The code in the if-section for "EventHandler" is not empty was my last attempt.
Is it possible that my thoughts will work or is it only possible to attach event handlers in the main class that is initializing the app.
static public WrapPanel comboBox(string Name, List<string> Items, bool MultiSelection = false, string EventHandler="",string Label="", int Width = 0)
{
WrapPanel lPanel = new WrapPanel();
lPanel.Name = "stPanel_" + Name;
if (Label != "")
{
Label lLabel = new Label();
lLabel.Content = Label;
lPanel.Children.Add(lLabel);
if (Width != 0)
{
lPanel.Width = Width;
}
}
dynamic lComboBox;
if (MultiSelection)
{
lComboBox = new ComboBoxAdv();
lComboBox.AllowMultiSelect = true;
}
else lComboBox = new ComboBox();
lComboBox.Name = Name;
foreach (string Item in Items) lComboBox.Items.Add(Item);
lPanel.Children.Add(lComboBox);
ComboBox dkaf = new ComboBox();
if (EventHandler != "")
{
string lClass = "";
string lMethod = EventHandler.Split('.').Last();
for (int lCounter = 0; lCounter < EventHandler.Split('.').Length - 1; lCounter++)
lClass += EventHandler.Split('.')[lCounter] + ".";
lClass = lClass.Substring(0, lClass.Length - 1);
object dynamicObject;
Type objectType = Type.GetType(lClass);
dynamicObject = Activator.CreateInstance(objectType);
System.Reflection.MethodInfo method = objectType.GetMethod(lMethod);
//method.Invoke(dynamicObject, new object[] { });
lComboBox.Click += new RoutedEventHandler(method);
}
return lPanel;
}
Best regards
Chris
I want to be able to call the following function multiple times through out my code to fill different groups of 8 text boxes in my form.
Right now reference is being passed in "tbPlay" from where it is being called initially in the code.
Each time this function will be called it will be to fill different text box groups.
I am trying to think of a way of using the empty for loop to create the necessary variable names to replace tbPlay0-7 in my case statement, so it isn't only usable for one group of text boxes in my code. I am not sure it can be done.
Can anyone help.
private void convertBasetoDrawn(string numBase, string reference)
{
string baseNumber = numBase;
for (int i = 0; i < 8; i++)
{
//some code here to create variables to replace the text box names in the
//following case statement
}
switch (baseNumber)
{
case "000":
tbPlay0.Text = "000";
tbPlay0.ForeColor = Color.Red;
tbPlay1.Text = "500";
tbPlay2.Text = "050";
tbPlay3.Text = "005";
tbPlay4.Text = "550";
tbPlay5.Text = "505";
tbPlay6.Text = "055";
tbPlay7.Text = "555";
tbPlay7.ForeColor = Color.Red;
break;
}
}
Create a List<TextBox> for each group:
List<TextBox> list01 = new List<TextBox>() { tbPlay0, tbPlay1, ....};
List<TextBox> list02 = new List<TextBox>() { ..., ... , ....};
// ..
}
And pass such a group to the function:
private void convertBasetoDrawn(List<TextBox> list, string numBase, string reference)
{
string[] texts = new string[8]
{ "000", "500", "050", "005", "550", "505", "055", "555" };
for (int t = 0; t < list.Count; t++) list[t].Text = texts[t];
list[0].ForeColor = Color.Red;
list[7].ForeColor = Color.Red;
}
Assuming the texts will always look like that. If they depend on, maybe numbase you can construct them dynamically as well, as long as you know the rules.. Maybe even a simple replacement will do the job?
You didn't use reference, btw..
Now, I'm just guessig here, but maybe this is the pattern for your texts..:
string[] texts = new string[8]
{ "nnn", "dnn", "ndn", "nnd", "ddn", "dnd", "ndd", "ddd" };
for (int s = 0; s < texts.Length; s++)
texts[s] = texts[s].Replace("d", numBase).Replace("n", reference);
Now you can call it like this:
convertBasetoDrawn(list01, "0","5");
Update: For the rules as I understand them now you could do:
string newText = "";
for (int s = 0; s < texts.Length; s++)
{
newText = "";
for (int i = 0; i < 3; i++)
{
if (texts[s][i] == 'n') newText += numBase[i];
else newText += (Convert.ToByte(numBase[i].ToString()) +
Convert.ToByte(reference[0].ToString()) ).ToString("0");
}
texts[s] = newText;
}
and call it like this:
convertBasetoDrawn(list01, "001", "5");
or
convertBasetoDrawn(list02, "000", "1");
Note: no carry over here.. You'd have to define rules for that and code it yourself..
It's not clear how you plan to identify the specific group of eight. But let's assume you have somehow.
Then, if I were writing this code, I would use a UserControl to encapsulate the repeated pattern, exposing the eight TextBox controls — or rather, the properties of them that you want access to — as properties. E.g.
class TextBoxGroup : UserControl
{
public string Text1
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
public Color ForeColor1
{
get { return textBox1.ForeColor; }
set { textBox1.ForeColor = value; }
}
public string Text2
{
get { return textBox2.Text; }
set { textBox2.Text = value; }
}
public Color ForeColor2
{
get { return textBox2.ForeColor; }
set { textBox2.ForeColor = value; }
}
// ...
public string Text8
{
get { return textBox8.Text; }
set { textBox8.Text = value; }
}
public Color ForeColor8
{
get { return textBox8.ForeColor; }
set { textBox8.ForeColor = value; }
}
}
Then in your method, rather than whatever logic you planned on using to figure the starting index for your group, instead you just retrieve the appropriate TextBoxGroup instance and use it in the switch, like this:
case "000":
textBoxGroup.Text1 = "000";
textBoxGroup.ForeColor1 = Color.Red;
textBoxGroup.Text2 = "500";
textBoxGroup.Text3 = "050";
textBoxGroup.Text4 = "005";
textBoxGroup.Text5 = "550";
textBoxGroup.Text6 = "505";
textBoxGroup.Text7 = "055";
textBoxGroup.Text8 = "555";
textBoxGroup.ForeColor8 = Color.Red;
break;
A variation on the above would encapsulate the properties with setter methods taking an index. E.g.
class TextBoxGroup : UserControl
{
// Initialized in constructor to be the eight TextBoxes
private TextBox[] _textboxes;
public void SetText(int i, string text)
{
_textboxes[i].Text = text;
}
}
Of course, if you don't want to use a UserControl, you could just initialize a similar data structure in the main form instead, so that the controls can be accessed by index. But personally, I'd prefer the UserControl as it makes it easier to reuse and ensure consistency across all the groups of TextBox controls.
How to Clone or Serialize a Windows Forms Control?
When I am trying to Clone windows forms controls using this code "CloneControl(Control ct1)", it allows me to duplicate controls with some Serializable properties, not with all properties.
public Form1()
{
InitializeComponent();
Columns = new DataGridViewTextBoxColumn[2];
for (int i = 0; i < 2; i++)
{
Columns[i] = new System.Windows.Forms.DataGridViewTextBoxColumn();
//
// Columns[i]
//
Columns[i].HeaderText = "j" + (i + 1);
Columns[i].Name = "Column" + (i + 1);
Columns[i].Width = 50;
}
dataGridView1 = new System.Windows.Forms.DataGridView();
dataGridView1.Name = "dataGridView1";
dataGridView1.Location = new System.Drawing.Point(100, 100);
dataGridView1.RowHeadersWidth = 50;
dataGridView1.RowTemplate.Height = 25;
dataGridView1.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
dataGridView1.Columns.AddRange(Columns);
dataGridView1.TabIndex = 3;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
dataGridView1.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView1.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView1.Rows[0].Cells[0].Value = "value1";
Controls.Add(dataGridView1);
Control cloned1 = CloneControl(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
}
public Control CloneControl(Control ct1)
{
Hashtable PropertyList = new Hashtable();
PropertyDescriptorCollection Properties = TypeDescriptor.GetProperties(ct1);
Assembly controlAsm = Assembly.LoadWithPartialName(ct1.GetType().Namespace);
Type controlType = controlAsm.GetType(ct1.GetType().Namespace + "." + ct1.GetType().Name);
Control cloned1 = (Control)Activator.CreateInstance(controlType);
foreach (PropertyDescriptor pr1 in Properties)
{
if (pr1.PropertyType.IsSerializable)
{
PropertyList.Add(pr1.Name, pr1.GetValue(ct1));
}
if (PropertyList.Contains(pr1.Name))
{
try
{
Object obj = PropertyList[pr1.Name];
pr1.SetValue(cloned1, obj);
}
catch (Exception ex)
{
}
}
}
return ct2;
}
If you run the code... the you will get
As you can see in the main method I create a clone of dataGridView1, which has a few properties.
And actually each cell value is null in a cloned dataGridView.
Also size of a columns are not cloned!
You may have a question: if Visual Studio or SharpDeveloper as IDE which is written in C# can handle this problem, then it might be possible to write that kind of code! Right?
In Visual Studio When you are trying drag and drop controls, or copy and paste controls, it not only duplicates that controls with all properties (including Serializable or non-Serializable) but also it changes the name of control itself from "dataGridView1" to "dataGridView2" as well as in SharpDeveloper!
What should I do?
What kind of method should I create?
Maybe another control has a many non-Serializable properties!
How to duplicate all of them?
Please anyone.....
Like #Hans mentioned in the comment, Clone is not that easy. If you want to get some identical controls with only a bit different, you'd better use a function to define general behavior and pass the different properties in as parameters. For example, we define a function with some general properties which apply to DataGridView:
private void InitDataGridView(DataGridView dataGridView, string name)
{
dataGridView.Name = name;
// configure other properties here
dataGridView.Location = new System.Drawing.Point(100, 100);
dataGridView.RowHeadersWidth = 50;
dataGridView.RowTemplate.Height = 25;
dataGridView.Size = new System.Drawing.Size(55 + 50 * 2, 25 + dataGridView1.RowTemplate.Height * 2);
dataGridView.Anchor = System.Windows.Forms.AnchorStyles.None;
dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
// remember to initialize your columns, or pass it in as a parameter
dataGridView.Columns.AddRange(Columns);
dataGridView.AllowUserToAddRows = false;
dataGridView.Rows.Add();
dataGridView.Rows.Add();
dataGridView.Rows[0].HeaderCell.Value = "i" + 1;
dataGridView.Rows[1].HeaderCell.Value = "i" + 2;
dataGridView.Rows[0].Cells[0].Value = "value1";
}
public Form1()
{
InitializeComponent();
var dataGridView1 = new DataGridView();
var dataGridView2 = new DataGridView();
InitDataGridView(dataGridView1, "dataGridView1");
InitDataGridView(dataGridView2, "dataGridView2");
}
IDE (e.g. Visual Studio) is using PropertyDescriptors, DesignerSerializationVisibility and ShouldSerializeValue, but DataGrid Rows are something special, because you cannot add them at design time! IDE cannot copy something that is not there, so, the solution must be different (if you want to clone controls beyond what IDE/Designer can do - see other answers and comments for this). Try my code (everything except grid rows got cloned without the extra check - the columns got cloned).
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
Note: The above can be done better (by check for the class at the end), but is as it is to be obvious.
using System;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace CloneControls {
public partial class Form1: Form {
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e) {
dataGridView1.Rows.Add();
dataGridView1.Rows.Add();
foreach(Control c in splitContainer1.Panel1.Controls)
splitContainer1.Panel2.Controls.Add((Control)Clone(c));
}
static object Clone(object o) {
return Copy(o, Activator.CreateInstance(o.GetType()));
}
static object Copy(object src, object dst) {
IList list = src as IList;
if(list != null) {
IList to = dst as IList;
foreach(var x in list)
to.Add(Clone(x));
return dst; }
foreach(PropertyDescriptor pd in TypeDescriptor.GetProperties(src)) {
if(!pd.ShouldSerializeValue(src)) {
if(src is DataGridView && pd.Name == "Rows")
CopyDataGridRows((DataGridView)src, (DataGridView)dst);
continue; }
switch(pd.SerializationVisibility) {
default: continue;
case DesignerSerializationVisibility.Visible:
if(pd.IsReadOnly) continue;
pd.SetValue(dst, pd.GetValue(src));
continue;
case DesignerSerializationVisibility.Content:
Copy(pd.GetValue(src), pd.GetValue(dst));
continue;
}
}
return dst;
}
static void CopyDataGridRows(DataGridView src, DataGridView dst) {
foreach(DataGridViewRow row in src.Rows)
if(!row.IsNewRow) dst.Rows.Add((DataGridViewRow)Clone(row));
}
}
}
I think I made more better method here.
This Method at first checks interface of property: if it is ICollection then it does the first job.
After this one loop ends in the method "DeepClone()", then it is necessary to do another loop without checking PropertyType Interface... I mean I could not mix these two operation into one loop?!
Also You can detect that there will be some kind of Run-time Exceptions and for this reason I put this code into try-catch block...
Control cloned1 = (Control)DeepClone(dataGridView1);
cloned1.SetBounds(cloned1.Location.X, cloned1.Location.Y + 300, cloned1.Width, ct1.Height);
Controls.Add(cloned1);
cloned1.Show();
public dynamic DeepClone(dynamic ob1)
{
dynamic ob2 = null;
if (ob1.GetType().IsSerializable && !ob1.GetType().IsArray)
{
if (ob1 != null)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(ms, ob1);
ms.Position = 0;
ob2 = formatter.Deserialize(ms);
}
}
}
else
{
if (ob1.GetType().IsArray)
{
var r1 = ob1.Rank;
object[] d1 = new object[r1];
long[] V1 = new long[r1];
for (int i = 0; i < r1; i++)
{
V1[i] = 0;
d1[i] = ob1.GetUpperBound(i) + 1;
}
ob2 = Activator.CreateInstance(ob1.GetType(), d1);
for (long i = 0; i <= ob2.Length; i++)
{
ob2.SetValue(DeepClone(ob1.GetValue(V1)), V1);
for (int j = 0; j <= V1.GetUpperBound(0); j++)
{
if (V1[j] < ob2.GetUpperBound(j))
{
V1[j]++;
break;
}
else
{
V1[j] = 0;
}
}
}
}
else
{
PropertyInfo[] P1 = ob1.GetType().GetProperties();
ob2 = Activator.CreateInstance(ob1.GetType());
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.GetInterface("System.Collections.ICollection", true) != null)
{
dynamic V2 = p1.GetValue(ob1) as IEnumerable;
MethodInfo gm1 = p1.PropertyType.GetMethods().Where(m => m.Name == "Add").Where(p => p.GetParameters().Count() == 1).First(f => V2[0].GetType().IsSubclassOf(f.GetParameters()[0].ParameterType) || f.GetParameters()[0].ParameterType == V2[0].GetType());
if (V2[0].GetType().IsSubclassOf(gm1.GetParameters()[0].ParameterType) || gm1.GetParameters()[0].ParameterType == V2[0].GetType())
{
for (int i = 0; i < V2.Count; i++)
{
dynamic V3 = DeepClone(V2[i]);
gm1.Invoke(p1.GetValue(ob2), new[] {V3});
}
}
}
}
catch (Exception ex)
{
}
}
foreach (PropertyInfo p1 in P1)
{
try
{
if (p1.PropertyType.IsSerializable && p1.CanWrite)
{
var v2 = p1.GetValue(ob1);
p1.SetValue(ob2, v2);
}
if (!p1.PropertyType.IsSerializable && p1.CanWrite)
{
dynamic V2 = p1.GetValue(ob1);
if (p1.PropertyType.GetMethod("Clone") != null)
{
dynamic v1 = V2.Clone();
p1.SetValue(ob2, v1);
}
}
}
catch (Exception ex)
{
}
}
}
}
return ob2;
}
You may say that this Method does not copy some kind of property, But it does copy of main properties and the Cloned control will look like an original control!
Trying to clone a control is overkill except if you really need a totally generic control clone method. Most of the time, you only need to clone a specific control and you have an easy access to the code that created it (see the Form designer generated code, and the setup code you wrote yourself).
But nevertheless, I once used a trick to duplicate many controls at once in order to fill the new tabs of a TabControl, choosing one out of ten tab designs.
I also wanted to use the Form design tool of the C# IDE to edit and modify the 10 template.
So, besides my Tab control form, and using the VS IDE, I created 10 "control factory dummy forms" in my project. I put a dummy Panel control in each of it.
Each time I had to dynamically create a new Tab, I simply instantiated a new dummy window of the desired style. Then I simply moved the Parent pane to my ControlTab (using the Controls.Add() method of the new tab).
This way, you must link the event handlers after the Tab creation (after the controls move). And the event handler's code should be written in you main window class, otherwise you will have "this" reference problems.
Obviously, you will have to store control references somewhere, to be able to access them. The easiest way to do this is to just keep track of each "dummy template Form" you instantiate and to set the "modifier" of your controls to be "public". You can use the Tag property of the destination tab page to store that reference. But, to avoid many casts, it is better to declare an array of each form class, and to store the references there.
I need to create the names of my placeholders dynamically in a loop in C# but I don't know how i can get the right name.
The placeholders are set in the aspx page and with currMaxValues is the max. number of Placeholders set.
My current markup:
const int currMaxValues = 6;
for (int i = 0; i < currMaxValues; i ++)
{
//new placeholdernames
string currPlaceholderTime = "placeholderTime" + i;
string currPlaceholderMore = "placeholderZusatz" + i;
string currPlaceholderIco = "placeholderIco" + i;
string currPlaceholderTemp = "placeholderTemp" + i;
//elements for placeholders
Label time = null;
Label more = null;
Image img = null;
Label temp = null;
//built the stuff for the placeholders
DateTime currTime = DateTime.Now;
int hour = currTime.Hour;
time.Text = hour.ToString();
//(PlaceHolder)currPlaceholderTime.Controls.add(time);
}
Is it possible to use the name of the Placholder that I created at the beginning of the loop to access the controls?
Thanks for any help!
You have to use FindControl function to find a control with dynamic id as below
FindControl("controlname")
In your case you need to change the code as below
for (int i = 0; i < currMaxValues; i ++)
{
//new placeholdernames
string currPlaceholderTime = "placeholderTime" + i;
string currPlaceholderMore = "placeholderZusatz" + i;
string currPlaceholderIco = "placeholderIco" + i;
string currPlaceholderTemp = "placeholderTemp" + i;
//elements for placeholders
Label time = null;
Label more = null;
Image img = null;
Label temp = null;
//built the stuff for the placeholders
DateTime currTime = DateTime.Now;
int hour = currTime.Hour;
time.Text = hour.ToString();
Placeholder placeHolderTime = FindControl(currPlaceholderTime) as PlaceHolder;
placeHolderTime.Controls.Add(time);
//(PlaceHolder)currPlaceholderTime.Controls.add(time);
}
However note that, the above code works only if your page not under master page or the placeholders are directly under the main page.
If you are not able find with the above code, you need to have a function like below, to find the control hierarchically.
private Control FindControl(Control rootControl, string controlID)
{
if (rootControl.ID == controlID) return rootControl;
foreach (Control controlToSearch in rootControl.Controls)
{
Control controlToReturn =
FindControl(controlToSearch, controlID);
if (controlToReturn != null) return controlToReturn;
}
return null;
}
Then you can call the above function using the following code to find the placeholders
Placeholder placeHolderTime = FindControl(this,currPlaceholderTime) as PlaceHolder;
Is there a way to reference buttons using a numerical value in C#? I am trying to manipulate the text on buttons using one reusable method. Here is my current coding:
One button click method (there are a total of 16):
private void Card1_Click(object sender, EventArgs e)
{
buff = CardClick(1);
if (buff != null)
{
Card1.Text = buff;
}
}
And the reusable method (the code does have holes, it's in development):
private string CardClick(int card)
{
guesses[g++] = card; //alternate g
if ((guesses[0] != null) && (guesses[1] != null))
{
//Reset Card guesses[0]
//Reset Card guesses[1]
return null;
}
else
{
if (card > 8)
{
return map[2, card];
}
else
{
return map[1, card];
}
}
You can also use Controls.Find() to get a reference to your desired button based on its name:
int i = 1;
Control[] matches = this.Controls.Find("Card" + i.ToString(), true);
if (matches.Length > 0 && matches[0] is Button)
{
Button btn = (Button)matches[0];
// ... do something with "btn" ...
btn.PerformClick();
}
You can use an array of buttons
Button[] buttonArray = new Button[10];
You can get all the buttons from your form by Type and then extract an array:
public Button[] AllButtons()
{
var buttons = new List<Button>();
foreach (var control in this.Controls)
{
if (control.GetType() == typeof(Button))
buttons.Add((Button)control);
}
return buttons.ToArray();
}