Execute a c# statement stored in a string variable [duplicate] - c#

This question already has answers here:
Is it possible to dynamically compile and execute C# code fragments?
(7 answers)
Closed 9 years ago.
I want to execute a C# statement which is stored in a string variable. For instance:
string statement1 = "button1.Visible = true";
string statement2 = "button1.Text = \"Number\"";

Looking at your comments and that you have 80 controls requiring very similar action, dynamic compilation may be an overkill for this purpose. You can use use Controls collection of the parent container along with the Tag property of your buttons to achieve it. A single event handler would suffice.
You can use LINQ members like OfType and Cast to make your code even smaller.
Edit
After looking at your latest comment, what you should do is to programmatically create your buttons and add them to your Form or whatever container you have. You can then keep a Dictionary<string, Button> that will let you either iterate over the collection, or access an individual button through its name. Something like:
//Declare this globally
Dictionary<string, Button> Dic = new Dictionary<string, Button>(81);
//put this in the constructor
for(int i=0; i<81; i++)
{
Button b = new Button();
b.Text = i; //or Random or whatever
b.Name = "btn" + i.ToString();
this.Controls.Add(b);
Dic.Add(b.Name, b);
}
Later you can do both iteration like this:
foreach(var item in Dic)
{
item.Value.Visible = true; //or false
}
and key-based access like this:
Dic["btn45"].Visible = true; //or false
Since you're creating Sudoku, i probably think you should use TableLayoutPanel with appropriate number of rows and columns at design-time and then add your buttons to the panel and set their Row/Column property in the loop. This will help better respond to resizing events etc.

From your comments it appears you simply want to iterate over 80 similar buttons and configure them the same way. This can be accomplished by far simpler means than executing dynamic code.
Here's a couple of ways.
Use the tag property
First set the Tag property to a specific number in the designer for each of the 80 buttons.
Then execute this code:
foreach (var button in Controls.OfType<Button>().Where(button => button.Tag == "TAG"))
{
button.Visible = true;
button.Text = "Number";
}
Use the name to identify them:
Ensure all the 80 buttons have a name of "buttonX" where X is a number.
Then execute this code:
foreach (var button in Controls.OfType<Button>().Where(button => button.Name.StartsWith("button"))
{
button.Visible = true;
button.Text = "Number";
}
Actually "execute code"
If, as you say in your comments, you only need to solve this problem: "Execute" this type of code:
object.member=value
Where object refers to something stored in a field or property. member refers to a public member (field or property) of that object, and value is something that will always be easily convertible to the member type, then the following code will work.
Note that it is short on error checking, so please make sure you vet the code before using it.
ALSO I am not convinced in the very slightest that this is the appropriate solution, but since you've decided to ask about the solution you had in mind instead of the actual problem, it's hard to come up with a better solution.
You can run this code in LINQPad:
void Main()
{
button1.Dump();
SetProperties(this,
"button1.Visible=true",
"button1.Text=\"Number\""
);
button1.Dump();
}
public static void SetProperties(object instance, params string[] propertySpecifications)
{
SetProperties(instance, (IEnumerable<string>)propertySpecifications);
}
public static void SetProperties(object instance, IEnumerable<string> propertySpecifications)
{
var re = new Regex(#"^(?<object>[a-z_][a-z0-9_]*)\.(?<member>[a-z_][a-z0-9_]*)=(?<value>.*)$", RegexOptions.IgnoreCase);
foreach (var propertySpecification in propertySpecifications)
{
var ma = re.Match(propertySpecification);
if (!ma.Success)
throw new InvalidOperationException("Invalid property specification: " + propertySpecification);
string objectName = ma.Groups["object"].Value;
string memberName = ma.Groups["member"].Value;
string valueString = ma.Groups["value"].Value;
object value;
if (valueString.StartsWith("\"") && valueString.EndsWith("\""))
value = valueString.Substring(1, valueString.Length - 2);
else
value = valueString;
var obj = GetObject(instance, objectName);
if (obj == null)
throw new InvalidOperationException("No object with the name " + objectName);
var fi = obj.GetType().GetField(memberName);
if (fi != null)
fi.SetValue(obj, Convert.ChangeType(value, fi.FieldType));
else
{
var pi = obj.GetType().GetProperty(memberName);
if (pi != null && pi.GetIndexParameters().Length == 0)
pi.SetValue(obj, Convert.ChangeType(value, pi.PropertyType));
else
throw new InvalidOperationException("No member with the name " + memberName + " on the " + objectName + " object");
}
}
}
private static object GetObject(object instance, string memberName)
{
var type = instance.GetType();
var fi = type.GetField(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
if (fi != null)
return fi.GetValue(instance);
var pi = type.GetProperty(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
if (pi != null && pi.GetIndexParameters().Length == 0)
return pi.GetValue(instance, null);
return null;
}
private Button button1 = new Button();
public class Button
{
public bool Visible;
public string Text { get; set; }
}
This will output (the two button1.Dump(); statements) the button configuration before and after, and you'll notice that the property and the field have been set.
You can execute this as follows:
`SetProperties(this, "...", "...");
where this must refer to your form object (the one that owns the buttons).

What about using delegates?
Action statement1 = () => button1.Visible = true;
Action statement2 = () => button1.Text = "Number";

Related

How to access a group of Text Boxes based on the Index Id in their Name

I have 16 text boxes in my Form whose names are suffixed sequentially from 1 to 16 respectively.
i.e. The 16 test boxes are names TextBox1, 'TextBox2, .... all the way until the 16th one, which is namedTextBox16`.
I would like to read the contents of these 16 text boxes in a loop and modify the ith TextBox's contents or properties based on a certain condition.
How do I do this?
If you use WinForms, easiest way is to store text boxes references in array, in constructor of window:
TextBox[] data = new TextBox[16]{textBox1,textBox2, [...],textBox16};
then you can use for loop to access them.
You can try something like this:
Dictionary<string, string> yourValues = new Dictionary<string, string>();
foreach (Control x in this.Controls)
{
if (x is TextBox)
{
yourValues.Add(((TextBox)x).Name, ((TextBox)x).Text);
}
}
NOTE: On your future question please provide more information and make your question more clear.
i would try to find and modify using Linq:
using System.Linq;
//...
int x = 5; //number of textbox to search
var textbox = this.Controls.OfType<TextBox>().Single(tb => tb.Name.EndsWith(x.ToString()));
textbox.Text = "abc";
In case you have to loop thru all the texboxes in form, you can do something like this:
List<TextBox> textboxes = this.Controls.OfType<TextBox>().ToList();
foreach (TextBox box in textboxes)
{
box.Text = "something";
}
Easiest way according to what you specified is to use Linq.
Let's assume you have 3 TextBoxes :
// 1st -> which is named meTextBox1
// 2nd -> which is named meTextBox2
// 3rd -> which is named meTextBox3
As you can see from above every line differs only by the number ( index .. call it whatever you want ).
Now you can make your base "query" which would look like :
const string TB_NAME = "meTextBox{0}";
And as you can presume right now this will be used inside of string.Format method. Now to retrieve desired TextBox all you have to do is to make Linq statement :
string boxName = string.Format(TB_NAME, 7); // retrieve 7th text box
TextBox tBox = Controls.OfType<TextBox>().FirstOrDefault(tb => tb.Name == boxName);
This example does not consider nested Controls but you can make do this recursively which will retrieve nested Controls:
TextBox ByIndex(int idx, Control parent)
{
TextBox result = null;
string searchFor = string.Format(TB_NAME, idx);
foreach(Control ctrl in parent.Controls)
{
if(!(ctrl is TextBox) && ctrl.HasChildren)
{
result = ByIndex(idx, ctrl);
if( result != null)
break;
}
else if(ctrl is TextBox)
{
if(ctrl.Name = searchFor)
{
result = ctrl as TextBox;
break;
}
}
}
return result;
}
To use above method you can just call it like such :
public class MeForm : Form
{
//.. your code here ..
void meImaginaryMethodToRetrieveTextBox()
{
int meRandomIndex = 7;
TextBox tb = ByIndex(meRandomIndex, this);
// rest of the code...
}
}

does passing a collection to a function means the function can change the collection's elements?

I actually know the answer to the question (I think) but I don't know the reason...
So, I know that if I have a class like the following:
class Man
{
public string Name;
public int Height;
public Man() { }
public Man(string i_name, int i_height)
{
Name = i_name;
Height = i_height;
}
}
And I have the following Program class (with main function):
class Program
{
static void Main(string[] args)
{
Program p = new Program();
Man g = new Man("greg", 175);
//assigning null to g inside the function.
p.ChangeMan(g);
Console.WriteLine(g == null? "the function changed g out side the function" : "the function did not change g out side the function");
//the output of course is that the function did not change g outside the function.
//now I am creating a list of Man and adding 5 Man instances to it.
List<Man> manList = new List<Man>();
for (int i = 0; i < 5; i++)
{
manList.Add(new Man("Gadi" + i.ToString(), 10 * i));
}
//assigning null to the list insdie the function
p.ChangeList(manList);
Console.WriteLine(manList == null ? "the function changed the list out side the function" : "the function did not change the list out side the function");
//the output of cousre again is the function did not change the list out side the function
//now comes the part I dont understand...
p.ChangeManInAList(manList);
Console.WriteLine("list count = " + manList.Count());
//count is now 6.
Console.WriteLine(manList[0] == null ? "the function changed the element out side the function" : "the function did not change the element out side the function");
//the out again - the function did not change...
}
public void ChangeMan(Man g)
{
g = null;
}
public void ChangeManInAList(IList<Man> gadiList)
{
Man g = gadiList.First<Man>();
g = null;
Console.WriteLine(g == null? "g is null" : "g is not null");
gadiList.Add(new Man("a new gadi", 200));
Console.WriteLine("list count = " + gadiList.Count());
}
public void ChangeList(List<Man> list)
{
list = null;
}
}
I am assigning null to the first element of the list + adding one Man to the list. I expected that if I can add to the list, I can also change the elements, but I saw different...
I was able to add a Man to the list but could not assign null to one of the elements, how come? I know the list is passed by value so I can not change the list itself (like assigning null to it), but I can add to it? and can not assign null to the elements? are they being passed by val as well?
will be happy for some good and clear explanation :)
Here is your point of confusion:
Man g = gadiList.First<Man>();
g = null;
What you are essentially doing is getting a Man out of the list and assigning it to the local variable g.
Then, you assign a different value to the variable g.
At no point here did you change the value of any member of the list, you simply changed the value which the variable g refers to.
Let's try to compare it to this example:
int a = 5;
int b = a;
b = 3;
//you wouldn't expect `a` to be 3 now, would you?
In order to change the value of the list item, you would need to explicitly set the list index to a different value:
Man g = gadiList.First<Man>();
gadiList[gadiList.IndexOf(g)] = null;
//or specifically in this case:
gadiList[0] = null;
When you get element from list you get new reference to list item.
So as a result you get two references: one (private reference in list object), your reference.
When you set your reference to null it do not affect reference in list object. You reference became null, but private list reference remains the same.

What could be null here?

My app is crashing after I clear the Bing Map of its pushpins and then open a flyout. This is what I see in the debugger:
The textblock is definitely not null - it is defined declaratively in XAML.
The List of String (CurrentlyMappedPhotosets) is not null - as you can see, it contains a string / has a count of 1.
The code just before what's seen on the scream shot is:
int count = App.CurrentlyMappedPhotosets.Count;
What could be null here?
Just before this happens, I call ClearMap:
private void ClearMap()
{
var mapLayerChildren = from c in DataLayer.Children select c;
var kinderGarten = mapLayerChildren.ToArray();
for (int i = 0; i < kinderGarten.Count(); i++)
{
if (kinderGarten[i] is Pushpin)
{
DataLayer.Children.Remove(kinderGarten[i]);
}
}
CloseInfobox();
App.CurrentlyMappedPhotosets.Clear();
if (null != App.photosetLocationCollection)
{
App.photosetLocationCollection.Clear();
}
appbarbtnClearMap.IsEnabled = false;
UpdateGUI(false);
}
...which calls UpdateGUI():
private void UpdateGUI(bool resizeMap)
{
appbarbtnRenamePhotoset.IsEnabled = App.CurrentlyMappedPhotosets.Count > 0;
if (resizeMap)
{
ResizeMap();
}
}
Have you looked if the value present in the collection is not null?
Any non primitive type class is by default null if not constructed with a value.
So you can have as many null values of the type you are trying to have.
I suggest that you do a test by changing the line to:
textblock0.Text = App.CurrentlyMappedPhotosets[0].HasValue ? App.CurrentlyMappedPhotosets[0].Value : "";

create an object using a string for the name in C#

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().

Using database query results instead of displaying in datagrid

I am using C# to create a Silverlight 4 application.
I am trying to do the following:
MapNode endNode = null;
if (keyword != null && keyword != "")
{
EntityQuery<NodeIDProj> res = CampusQueries.getNodeIDByNameQuery(keyword);
var queryres = CampusQueries.Load<NodeIDProj>(res, (items) =>
{
foreach (var item in items.Entities)
{
MapNode n = mapHelp.getNodeByID(item.NodeID);
if (n != null)
{
endNode = n;
TrackAnimation();
}
}
}, true);
}
However, after this point, my variable endNode is still null. TrackAnimation() works as though endNode has a valid value, but outside of the Load statement, endNode is back to null.
I know that I am lacking in understanding of how this works, and I would really appreciate an help given.
What I am trying to do, is query my database and I want to use those results in other methods rather than displaying them in a datagrid.
I want endNode to have value which I can use in other methods.
Please help me to figure out a way to do this, thank you!
EDIT:
Thank you, SLaks
Can I do something like:
MapNode endNode = null;
if (keyword != null && keyword != "")
{
EntityQuery<NodeIDProj> res = CampusQueries.getNodeIDByNameQuery(keyword);
var queryres = CampusQueries.Load<NodeIDProj>(res, (items) =>
{
foreach (var item in items.Entities)
{
MapNode n = mapHelp.getNodeByID(item.NodeID);
if (n != null)
{
endNode = n;
TrackAnimation();
}
}
}, true);
}
queryres.Completed += new EventHandler(queryres_Completed);
void queryres_Completed(object sender, EventArgs e)
{
//stuff
}
If so, how can I get access to the endNode variable, as it is declared within another method?
Your Load method is probably asynchronous, meaning that the callback happens some time after the rest of your code runs.
You can only use the result after you actually receive it.

Categories