What ways can you dynamically create controls in C#?
This was objects at first but it would have been more precise to say controls. My terminology was messed up. Thanks Joel.
Edit{
Controls that are created during runtime. And are able to be accessed and edited by the program.
Does this help?
}
I like the idea of Dynamic creation and was wondering what ways there were to do this.
Please only one per answer, I would like to see how people rank them.
eg
private Label _lblCLastName = new Label();
private static List<ChildrenPanel> _ListCP = new List<ChildrenPanel>();
public void CreatePanel(Panel Container)
{
// Created Controls
#region Controls
_pnlStudent.Controls.Add(_lblCLastName);
//
// lblCLastName
//
_lblCLastName.AutoSize = true;
_lblCLastName.Location = new System.Drawing.Point(6, 32);
_lblCLastName.Name = "lblCLastName";
_lblCLastName.Size = new System.Drawing.Size(58, 13);
_lblCLastName.TabIndex = 10;
_lblCLastName.Text = "Last Name";
// Adds controls to selected forms panel
Container.Controls.Add(_pnlStudent);
// Creates a list of created panels inside the class
// So I can access user input
ListCP.Add(this);
}
This is a code snippet from something that is close to what I'm talking about. I made another post but didn't quite post the question right. I will be deleting it but atm it is still viewable.
If there are still problems please be constructive I don't mind negitive input as long as it's helpful.
Edit:
I was able to get some answers I was looking for. Thank you to everyone who replied. I will close this when I am able too. If someone else can close it that would be appreciated.
I use the new keyword to dynamically create objects.
Creating GUI objects dynamically can be extremely useful, however, it can also be a nightmare for maintenance.
A good rule of thumb is to limit the amount of GUI object you dynamically create.
One situation where you may actually want to use a dynamically created GUI object is when you don't know the amount or count of objects you need. For example, one label for each row in a result set (even then you may consider a DataGrid or GridView type object).
This works for both WinForms and ASP.NET. Just be sure to document your code correctly.
My advice would be to stick with the Visual Designer for simpler forms and only create and add objects dynamically when it's absolutely necessary.
(FWIW, the code snippet you posted could probably be simplified and/or refactored as it seems to be going in the wrong direction.)
Anonymous Types, C# 3.x
This is fairly dynamic-esque because you don't have to code a class template to get custom objects.
// An anonymous object with two properties: obj.Name and obj.Age
var obj = new { Name = "Joe", Age = 35 };
The compiler will infer the Types of the properties from the initialization values you provide.
The type is not accessible from your source code, but can be seen in the IL. However if you create multiple anonymous objects with the same properties the C# compiler will use the same type for all of them.
// All objects use the same anonymous type
var obj1 = new { Name = "Joe", Age = 1 };
var obj2 = new { Name = "Art", Age = 30 };
var obj3 = new { Name = "Sally", Age = 25 };
// A different (second) anonymous type
var objDifferent = new { Phone = "555-555-1212", Name = "Joe", Age = 1 };
Stipulations There are more, but these are important.
var can only be used at the method scope (as a local variable), not the class scope.
anonymous objects have read-only properties; you cannot assign data back to them.
Activation (for remote objects too)
Use the System.Activator class' overloaded Activator.CreateInstance methods. This gets into the realm of creating objects locally or remotely.
using System;
/* Create instances of a Random number generator (or any class)
* without using the 'new' operator.
*/
Random rand1 = Activator.CreateInstance<Random>();
Random rand2 = (Random)Activator.CreateInstance(typeof(Random));
//etc...
(MSDN Documentation about Remote Objects.)
Assuming you are talking about the creation of dynamic objects:
You'll obviously need a library to support this, unless you want to get into Reflection.Emit yourself - LinFu supported this in version 1:
http://code.google.com/p/linfu/
However, it's a feature that was dropped before version 2 I seem to remember.
Related
I am attempting to add multiple flags of similar types (arrows) to a live chart using a C# windows forms project. This is to provide a label when a value falls out of a pre-defined specification.
I am currently stuck in how to create new instances of the ArrowAnnotation class so if multiple events happen there will be multiple flags for the people checking the chart. I am able to create one instance and manipulate the position to the latest data point in the series (it shouldn't be a stretch to lock it to a historical point, I just haven't done that yet.)
I have an understanding of creating multiple instances of other classes and keeping track of them with lists/ dictionaries but this one has me stumped (or maybe I don't have as good an understanding as I think?)
I can't share the code I have directly but I think I can write some example code if needed.
edit-
I am looking into using a memberwise clone to copy common attributes of each arrow and add those objects to a dictionary.
Thanks
Okay, I have managed to figure out how to do this for my use case.
When updating the live chart I can call a method if a parameter falls out of specification. In that method I create a new instance of the annotation, and the properties that I want to use as a template. (these values can also be changed with conditional logic if you want slight variation, and can be passed in with the argument) than add that newly made annotation to the annotation group. I am still looking for improvements to the code. I am still wanting a way to assign a name to the arrow, than recall that one and modify it (if I wanted to). And change the pass/fail criteria than apply annotations for the new failure points (but that is going into new territory)
// This is not the full method just the part that counts for this question.
private ArrowAnnotation floatArrow;
private void UpdateChart()
{
GenerateArrows(dateTime, sensorValue);
this.chart1.Annotations.Add(floatArrow);enter code here
}
private void GenerateArrows(DateTime x, double y)
{
floatArrow = new ArrowAnnotation();
floatArrow.Name = Convert.ToString(x);
floatArrow.ToolTip = Convert.ToString(x);
floatArrow.AxisXName = "ChartArea1\\rX";
floatArrow.AxisYName = "ChartArea1\\rY";
floatArrow.X = x.ToOADate();
floatArrow.Y = y;
floatArrow.Height = 5;
floatArrow.Width = 0;
floatArrow.BackColor = Color.Red;
}
I have the following object:
dynamic person = new {Id = 1, Name = "SpiderMan"};
I need to be able to iterate through the property names e.g. "Id", "Name".
I also need to be able to achieve this in the most efficient way therefore I chose to use FastMember however it's api does not allow me to iterate through the properties.
Any ideas?
[UPDATE]
Thanks to Marc I managed to achieve what I wanted using:
dynamic person = new { Id = 1, Name = "SpiderMan" };
MemberSet members = TypeAccessor.Create(person.GetType()).GetMembers();
foreach (Member item in members)
{
// do whatever
}
For the scenario you show, TypeAccessor.Create(obj.GetType()) and GetMember() should work fine, since that type is fine for reflection.
In the more general case: that's a fair question - I honestly can't remember whether FastMember exposes this for true dynamic types, but one important consideration here is that by the very nature of dynamic objects, the set of properties may not even be enumerable - i.e. the code could respond to obj.Whatever on the fly, without knowing about Whatever in advance. For the object you actually have, however, simple reflection is your best bet. The scenario you show does not need dynamic.
I am new to C# and reflection in specific and I am trying to solve a very specific problem. I want to implement the following code using reflection (since in some machines the System.Windows.Forms.DataVisualization.dlls might not be present, in those case i will skip generating charts).
Chart chart1 = new Chart();
string chartTitle = "Chart Title";
chart1.Titles.Add(chartTitle);
I figured out how to load a dll, get its class type, get/set its static/non-static properties, use constructors to create objects etc via Reflections. But I am quite lost about how to invoke "add" method on a collection.
Say, i have object Chart1 and chartTitle via Reflection, how do i implement the 3rd line of code using Reflection.
Appreciate your help. Thanks in advance.
Green Apple
If you already have the object chart1 instance then you can use
object titles = typeof(chart1).GetProperty("Titles").GetValue(chart1);
or
object titles = chart1.GetType().GetProperty("Titles").GetValue(chart1);
to get the chart1.Titles instance.
Then use titles.GetMethod("Add").Invoke(titles, chartTitle); to add the new title.
I'm trying to create objects dynamically but I don't know how to. What I need is, I have a class for that object, and objects properties are stored in the database. Then I'll need to compare the properties of each object to get the desired result.
So I need to dynamically create objects on the fly with the properties loaded from database.
I don't think you need to create objects dynamically, just create one statically that matches your db schema with the property details, then you can compare the values of the properties across rows, or within an instance of your object.
I have been working on something similar to this. There are several things:
Include the System.Reflection namespace
Create an object dynamically using Activator
Get the object properties using the myObjectType.GetProperties() method
Here is an example of a generic object creation function using the above methods:
using System.Reflection;
public static Item CreateItem<Item>(object[] constructorArgs, object[] propertyVals)
{
//Get the object type
Type t = typeof(Item);
//Create object instance
Item myItem = (Item)Activator.CreateInstance(t, constructorArgs);
//Get and fill the properties
PropertyInfo[] pInfoArr = t.GetProperties();
for (int i = 0; i < pInfoArr.Length; ++i)
pInfo.SetValue(myItem, propertyVals[i], null); //The last argument is for indexed properties
return myItem;
}
Of course the above example assumes that the values in the property value array are arranged correctly, which is not necessarily the case, but you get the idea.
With the PropertyInfo class you can get properties, get property names, get attributes associated with the properties, etc. Powerful technology. You should be able to do what you need with the above info, but if not let me know and I will add more info.
If you have a number of objects you want to instantiate from database values it can be done something like this.
//database code goes here, results go in results
List<ClassName> l = new List<ClassName>()
foreach(Row r in results){
l.Add(new ClassName(){ClassProperty1 = r.Property1,ClassProperty2 = r.Property2});
}
Are you talking about Dictionary?
var dict=new Dictionary<string, string>();
dict.Add("property1", "val1");
dict.Add("property2", "val2");
var prop2val=dict["property2"];
Maybe Activator is what your looking for?
http://msdn.microsoft.com/en-us/library/system.activator.aspx
Check this class, compile in the realtime. But it's performance is not quite good.
http://msdn.microsoft.com/zh-cn/library/microsoft.csharp.csharpcodeprovider(VS.80).aspx
You could use reflection to dynamically build your objects:
Reflection msdn reference
I think that you want to retrieve rows from the DB and directly assign them to object given that the properties of the object are equivalent to the columns of DB table. If that what you mean then I believe you can't :)
Rob Conery did a small project called Massive that pretty much does what you're trying to accomplish. It's essentially a small ORM, in 400 lines of Dynamic C# 4.0 code.
Rob has been doing this kind of thing for quite some time with SubSonic, so you might find his approach with Massive quite interesting.
http://blog.wekeroad.com/helpy-stuff/and-i-shall-call-it-massive
Some of the code is explained here, with examples:
http://blog.wekeroad.com/microsoft/the-super-dynamic-massive-freakshow
I'm not good at C# at all, I just don't get the logics. But VB I seem to understand alot better since it seems much more logical. Atleast to me.
So I'm run into something which isn't a problem at all in VB, accessing controls on a different form then the one you're currently in.
In VB, if I want to set the state of a button say, in Form2. I just type the following:
Form2.Button1.Text = "Text"
In C# I cannot seem to do this. Why? There must be a pretty good reason for this right?
Edit: So if I have this code, what would it look like to be able to access controls on the other form?
if (!AsioOut.isSupported())
{
SoundProperties.radioButtonAsio.Enabled = false;
SoundProperties.buttonControlPanel.Enabled = false;
SoundProperties.comboBoxAsioDriver.Enabled = false;
}
else
{
// Just fill the comboBox AsioDriver with available driver names
String[] asioDriverNames = AsioOut.GetDriverNames();
foreach (string driverName in asioDriverNames)
{
SoundProperties.comboBoxAsioDriver.Items.Add(driverName);
}
SoundProperties.comboBoxAsioDriver.SelectedIndex = 0;
}
Just tried to add this "SoundProperties SoundProperties = new SoundProperties();
And I do get access to the controls. But do I need to add this bit of code in both parts of this IF-statement? Seems like I do, but still, adding that line to the last part of this code doesn't do anything ang gives me the error message:
"A local variable named 'SoundProperties' cannot be declared in this scope because it would give a different meaning to 'SoundProperties', which is already used in a 'child' scope to denote something else"
Removing the line gives me the following error:
"An object reference is required for the non-static field, method, or property 'NAudio.SoundProperties.comboBoxAsioDriver'"
Here's the code after adding these lines in two places:
if (!AsioOut.isSupported())
{
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.radioButtonAsio.Enabled = false;
SoundProperties.buttonControlPanel.Enabled = false;
SoundProperties.comboBoxAsioDriver.Enabled = false;
}
else
{
// Just fill the comboBox AsioDriver with available driver names
String[] asioDriverNames = AsioOut.GetDriverNames();
foreach (string driverName in asioDriverNames)
{
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.comboBoxAsioDriver.Items.Add(driverName);
}
SoundProperties SoundProperties = new SoundProperties();
SoundProperties.comboBoxAsioDriver.SelectedIndex = 0;
}
Please don't hate me for saying this - but I think this is an issue that I've seen a lot of VB coders run into.
VB allows you to not deal with classes if you don't want to. When in C# you are adding a form to your project - visual studio is just making a class file for you that inherits from "Form".
In C# you have to actually instantiate this into an object and then work with that object. VB allows you to just access the class as if it was already instantiated - but it is actually just making a new "Form2" for you.
In vb if you want to have more than 1 actual "Form2" you would say something like this...
Dim window1 as new Form2()
Dim window2 as new Form2()
window1.Show()
window2.Show()
Now you will have two copies of "Form2" on your screen when you run this.
The difference between VB and C# is you also need to actually make(instantiate) your first copy of Form2 - C# will not do it for you.
Now to answer your question:
Once you have an actual object that has been instantiated - you need to actually make "Button1" public instead of private.
To do this - on Form2 - select Button1 and look at the properties...
Find the "Modifiers" property and set this to public.
You will now be able to see "Button1" on both window1 and window2.
Hope that helped.
You can access another form in c# too.
But you need a reference to the Form instance you want to interact with.
So you have to hold the variable of the 2nd Form instance and access it via this.
E.g.:
From the code of the first form call:
Form2 my2ndForm = new Form2();
my2ndForm.Button1.Text = "Text";
Be sure to set the access modifier of the Button1 to public or internal.