Get a List of User-Defined Controls From Current Namespace - c#

How could I get a list of all user-defined controls that were added to current namespace?
I am using C# 2010.

You can use reflection. Try this code:
public static class ControlsFinder
{
public static List<Type> FindControls(Assembly controlsLibrary,
string rootNamespace, bool includeNestedTypes = true)
{
var parent = typeof(UserControl);
return controlsLibrary.GetTypes()
.Where(t => (includeNestedTypes
? t.FullName.StartsWith(rootNamespace)
: t.FullName.Equals($"{rootNamespace}.{t.Name}"))
&& parent.IsAssignableFrom(t))
.ToList();
}
}
Usage example:
var controls = ControlsFinder.FindControls(Assembly.GetExecutingAssembly(), "WinFrm");
If you only want names you can select them from controls:
var names = controls.Select(t => t.Name).ToArray();

With these method i can extract list of all user-defined controls in my project and create an instance of the User-defined Control by it's name on my form.
var assemblies = AppDomain.CurrentDomain.GetAssemblies(); // Get my CurrentDomain Object
Assembly myType = Assembly.GetExecutingAssembly(); // Extract list of all references in my project
foreach (var assembly in assemblies) // Search for the library that contains namespace that have needed controls
{
if (assembly.GetName().ToString().ToUpper().IndexOf("FIBACONTROLS") > -1)
{
myType = assembly; // Get All types in the library
List<Type> myTps = myType.GetTypes().ToList();
Type mT = null;
foreach (Type selType in myTps) // Find the type that refer to needed user-defined control
{
if (selType.Name.ToUpper() == "FIBACOLORPICKER")
{
mT = selType;
break;
}
}
if (mT == null)
return;
object myInstance = Activator.CreateInstance(mT); // Created an instance on the type
Control mFib = (Control)myInstance; // create the control's object
mFib.Name = "Hahah"; // add the control to my form
mFib.Left = 100;
mFib.Top = 200;
mFib.Visible = true;
this.Controls.Add(mFib);
break;
}
}
I try add some comment to code to describe it.
It work and sure there are some better way to do it, but I'm new one in C# and I am sure that the solution that I found is not the best.

Related

Unloading AppDomain

Is there a way to unload parent AppDomain?
I am trying to load a different version of an assembly in my new AppDomain, but it keeps loading the version from the parent domain. When I am loading the assembly in the new AppDomain I am showing the correct path.
Or maybe there is another way I can do that?
Thanks in advance.
EDIT
AppDomain MailChimpDomain = AppDomain.CreateDomain("MailChimpDomain");
string path = AppDomain.CurrentDomain.BaseDirectory + "ServiceStack_V3\\ServiceStack.Text.dll";
MailChimpDomain.Load(AssemblyName.GetAssemblyName(path));
EDIT2
Code 2:
var MailDom = AppDomain.CreateDomain("MailChimpDomain");
MailDom.AssemblyLoad += MailDom_AssemblyLoad;
MailDom.AssemblyResolve += new ResolveEventHandler(MailDom_AssemblyResolve);
MailDom.DoCallBack(() =>
{
string name = #"ServiceStack.Text.dll";
var assembly = AppDomain.CurrentDomain.Load(name);
string name2 = #"MailChimp.dll";
var assembly2 = AppDomain.CurrentDomain.Load(name2);
//mailChimp object with API key found in mailChimp profile
MailChimp.MailChimpManager mc = new MailChimp.MailChimpManager("111111111111222f984b9b1288ddf6f0-us1");
//After this line there are both versions of ServiceStack.Text Assembly
MailChimp.Helper.EmailParameter em = new MailChimp.Helper.EmailParameter();
em.Email = strEmailTo;
//Creating email parameters
string CampaignName = "Digest for " + strEmailTo + " " + DateTime.Now.ToShortDateString();
MailChimp.Campaigns.CampaignCreateOptions opt = new MailChimp.Campaigns.CampaignCreateOptions();
opt.ListId = "l338dh";
opt.Subject = strSubject;
opt.FromEmail = strEmailFrom;
opt.FromName = strNameFrom;
opt.Title = CampaignName;
//creating email content
MailChimp.Campaigns.CampaignCreateContent content = new MailChimp.Campaigns.CampaignCreateContent();
content.HTML = strEmailContent;
//Creating new email and sending it
MailChimp.Campaigns.CampaignFilter par = null;
MailChimp.Campaigns.CampaignSegmentOptions SegOpt = null;
MailChimp.Campaigns.CampaignTypeOptions typeOpt = null;
mc.CreateCampaign("regular", opt, content, SegOpt, typeOpt);
MailChimp.Campaigns.CampaignListResult camp2 = mc.GetCampaigns(par, 0, 5, "create_time", "DESC");
foreach (var item in camp2.Data)
{
if (item.Title == CampaignName)
{
mc.SendCampaign(item.Id);
break;
}
}
});
static Assembly MailDom_AssemblyResolve(object sender, ResolveEventArgs args)
{
byte[] rawAssembly = File.ReadAllBytes(Path.Combine(path, args.Name));
return Assembly.Load(rawAssembly);
}
What your code actually does is it loads assembly to your parent domain. If you want to load assembly into child domain you have to do it from inside child domain. This is kind of chicken-egg problem, because the parent assembly (which loads child assembly into child domain) has to be loaded into your child domain as well in order to be executed.
Assuming simple example that you have console application and assembly called MyAssembly.dll it can be done like this:
static void Main(string[] args) {
var domain = AppDomain.CreateDomain("MailChimpDomain");
domain.AssemblyResolve +=new ResolveEventHandler(domain_AssemblyResolve);
domain.DoCallBack(() => {
string path = #"MyAssembly.dll";
var assembly = AppDomain.CurrentDomain.Load(path);
// to do something with the assembly
var type = assembly.GetType("MailChimp.MailChimpManager");
var ctor = type.GetConstructor(new[] { typeof(string) });
var mc = ctor.Invoke(new object[] { "111111111111222f984b9b1288ddf6f0" });
});
}
static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args) {
byte[] rawAssembly = File.ReadAllBytes(Path.Combine(#"c:\MyAssemblyPath", args.Name));
return Assembly.Load(rawAssembly);
}
In this case child domain has same root directory for resolving assemblies as parent domain (and thus it can execute the code which loads "MyAssembly.dll").
If the code working with reflection is longer than this, you may consider using bootstrapper instead.
I.E. you create new library called MyBootstrapper.dll, you'll reference directly version of ServiceStack.Text.dll and MailChimp.dll you like from MyBootstrapper.dll, and you'll create bootstrap class - lets call it Bootstrapper that will be static and will have single public static method called Run that will do dirty work.
Then inside DoCallBack() method you'll call this bootstrapper instead.
string path = #"MyBootstrapper.dll";
var assembly = AppDomain.CurrentDomain.Load(path);
// to do something with the assembly
var type = assembly.GetType("MyBootstrapper.Bootstrapper");
var method = type.GetMethod("Run", BindingFlags.Static);
method.Invoke(null, null);
// or if the Run method has one parameter of "string" type
var method = type.GetMethod("Run", BindingFlags.Static, Type.DefaultBinder, new[] { typeof(string) }, null);
method.Invoke(null, new object[] { "Parameter to run" });
No, you may not unload the default AppDomain or any assemblies loaded in the default AppDomain.
What you can do, however, is load both versions of the ServiceStack assembly in two child domains. You should be able to unload either of them. However, using types from these domains may prove more difficult than usual. You'll have to do it via remoting.
Given the overhead imposed by this, you should consider using only one version of that assembly (even if this means adapting part of your app, the one that runs in the default domain).

How to get the Text property of each form

I have been searching for almost a week now, but I can't seem to solve my simple problem. I want to get all the name and text properties of all the forms in my project.
Here is my code:
using System.Reflection;
Type Myforms = typeof(Form);
foreach (Type formtype in Assembly.GetExecutingAssembly().GetTypes())
{
if (Myforms.IsAssignableFrom(formtype))
{
MessageBox.Show(formtype.Name.ToString()); // shows all name of form
MessageBox.Show(formtype.GetProperty("Text").GetValue(type,null).ToString()); // it shows null exception
}
}
I need the name and the .Text of the form to store it in the database to control user privileges.
MessageBox.Show(formtype.GetProperty("Text").GetValue(type,null).ToString()); shows exception because you need an instance of Form to get its Text property as Form does not static Text property.
To get the default Text property create a instance
var frm = (Form)Activator.CreateInstance(formtype);
MessageBox.Show(formtype.GetProperty("Text").GetValue(frm, null).ToString());
The properties .Text and .Name are not static. So, you can't get the value of that property without calling a Constructor of that form. You must create an object of that form to read the property.
List<String> formList = new List<String>();
Assembly myAssembly = Assembly.GetExecutingAssembly();
foreach (Type t in myAssembly.GetTypes())
{
if (t.BaseType == typeof(Form))
{
ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);
if (ctor != null)
{
Form f = (Form)ctor.Invoke(new object[] { });
formList.Add("Text: " + f.Text + ";Name: " + f.Name);
}
}
}
To read properties, you need to make new instance of the form. Above you're browsing all the types that are inheriting from the Form-class. You can read the different Form-class names, but that's it.
To read the Text-property, you need to browse instances of your Forms. You can use Application.OpenForms to read the Text and Name properties of your open forms.
You can try this to read the properties:
List<KeyValuePair<string, string>> formDetails = new List<KeyValuePair<string, string>>();
Type formType = typeof(Form);
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
if (formType.IsAssignableFrom(type))
{
using (var frm = (Form)Activator.CreateInstance(type))
{
formDetails.Add(new KeyValuePair<string, string>(frm.Name, frm.Text));
}
}
}
I fixed the code and it should work now.

How to get the value of a property of a class in a non executing assembly through reflection

I'm having trouble while getting the value of the text property in a non-executing assembly; I read an assembly from disk via reflection, then i get all classes in the assembly to search for the Text property in a windows form class which is initialized by win forms designer. So far i have the following code:
static void Main(string[] args)
{
Assembly asm = Assembly.LoadFrom(Path.Combine(path, "Assembly.exe"));
PropertyInfo[] props;
foreach (Type t in asm.GetTypes())
{
var value = t.GetProperty("Text").GetValue(/*Not sure what to put here*/)
}
}
And this is how the designer generated the form
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None
Me.BackColor = System.Drawing.Color.FromArgb(CType(CType(0, Byte), Integer), CType(CType(128, Byte), Integer), CType(CType(128, Byte), Integer))
Me.ClientSize = New System.Drawing.Size(234, 181)
Me.Cursor = System.Windows.Forms.Cursors.Default
Me.Font = New System.Drawing.Font("Arial", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
Me.ForeColor = System.Drawing.SystemColors.WindowText
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
Me.Location = New System.Drawing.Point(581, 222)
Me.MaximizeBox = False
Me.MinimizeBox = False
Me.Name = "winform"
Me.RightToLeft = System.Windows.Forms.RightToLeft.No
Me.StartPosition = System.Windows.Forms.FormStartPosition.Manual
Me.Text = "Title"
Me.fraDías.ResumeLayout(False)
Me.ResumeLayout(False)
Keep in mind that the assembly is on disk and non-executing and that I want to retrieve the value of the Text property of every winform (I guess it should be somewhere hardcoded in the assembly since it was generated by the winforms designer)
Please tell me if this is possible, thanks!
Your requirements are contradictory, when you load an aseembly via reflection, and instantiate an object or try to get a property value, what happens is that some code begins to run, there is no way around that.
Remember that properties are just "syntax sugar" for a pair of methods, the getter and setter. Their current value is nothing but the value returned by the getter method, and when you change its value, you're in fact calling its setter method. So, to retrieve property values, you must make some code to run, even if it's a trivial get method.
I think maybe your confusion comes from the fact that you're using a designer to create the form. Particularly with the WinForms designer (WPF for instance is substantially different), all it does is to autogenerate some code for you. Setting properties, placing and moving controls around, what's happening under the hood is that it writes code that replicate your actions at runtime, specifically, it codes the InitializeComponent method. The real property value is set when the constructor is called (that in turn calls InitializeComponent), and then you may read/change using many properties.
What you would need to read those designer attributes is that those were hardcoded in some form of metadata, so that it's simply read as data and not as the result of code execution. That's not the case with WinForms, as it "saves" the form as code.
You cannot read a property of a class that has not been instantiated! The parameter you are missing is an instance of your type.
You must create an instance of the type with object o = Activator.CreateInstance(type); before accessing its members (unless they are static).
Your problem is related to how add-ins (plug-ins) can be loaded at runtime.
Here is how I made an Add-In Loader. Below, I will explain how you can adapt it to your problem. Add-Ins have to implement the IAddIn interface in my example. You are totally free in the definition of IAddIn. You could define it like this:
public interface IAddIn
{
bool OnLoad();
string Version { get; set; }
string Text { get; set; }
}
This allows you to access members without reflection.
public class AddInLoader
{
// Loads all non abstract types implementing IAddIn in all DLLs found in a folder.
public IList<IAddIn> Load(string folder)
{
var addIns = new List<IAddIn>();
string[] files = Directory.GetFiles(folder, "*.dll");
foreach (string file in files) {
addIns.AddRange(LoadFromAssembly(file));
}
return addIns;
}
// Loads all non abstract types implementing IAddIn found in a file.
private static IEnumerable<IAddIn> LoadFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
string addInInterfaceName = typeof(IAddIn).FullName;
foreach (Type type in asm.GetExportedTypes()) {
Type interfaceType = type.GetInterface(addInInterfaceName);
if (interfaceType != null &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract){
IAddIn addIn = (IAddIn)Activator.CreateInstance(type);
addIn.Version = asm.GetName().Version.ToString();
yield return addIn;
}
}
}
}
Now you can load and access the add-ins like this:
var loader = new AddInLoader();
IList<IAddIn> addIns = loader.Load(folderPath);
foreach (IAddIn addIn in addIns) {
if (addIn.OnLoad()) {
Console.WriteLine("Version = {0}, Text = {1}", addIn.Version, addIn.Text);
}
}
Reading the titles of Forms at runtime:
You can easily adapt this example. Instead of searching for types implementing an interface, search for types deriving from System.Windows.Forms.Form.
private static IEnumerable<Form> LoadFormsFromAssembly(string fileName)
{
Assembly asm = Assembly.LoadFrom(fileName);
foreach (Type type in asm.GetExportedTypes()) {
if (typeof(Form).IsAssignableFrom(type) &&
(type.Attributes & TypeAttributes.Abstract) != TypeAttributes.Abstract) {
Form form = (Form)Activator.CreateInstance(type);
yield return form;
}
}
}
Now you can get the texts of the forms like this:
var forms = LoadFormsFromAssembly(path);
foreach (Form frm in forms) {
Console.WriteLine(frm.Text);
}
Note: You must instantiate the forms, however you do not need to open (show) them. The code works only if the forms have a default constructor, i.e. a constructor without parameters.
You need an instance object for that type to get the value of a property.
It looks like you just want to check if a type has a "Text" property or not. You can to it by checking
bool hasTextProperty = t.GetProperty("Text") !=null;

C# Get form names of project A from Project B

I have two Projects in one solution, project A and project B (using VS2010 Ultimate and C# windows application).
Project B acts as a user management application for project A.
In project B i have a form that contains a chekcedlistbox control that will list all project A forms names and texts (this form will let system administrator to grant users the forms that are allowed to view/edit based on their security groups)
this is my code:
private void GetFormNames()
{
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type t in a.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
string FormText = f.Text;
string FormName = f.Name;
checkedListBox1.Items.Add("" + FormText + "//" + FormName + "");
}
}
}
}
}
the result i am getting is the form names of my current project (B) and Empty lines(//) and Select Window//MdiWindowDialog, PrintPreview.
I'm going to assume you've referenced ProjectA correctly and all the forms you're interested in actually have a public parameterless constructor. The problem is likely caused by ProjectA not being loaded yet, you can fix this in multiple ways. Probably the most direct is to use the static Assembly.Load (as long as the files are in the same directory, if not it gets more complicated).
try
{
Assembly projectA = Assembly.Load("ProjectA"); // replace with actual ProjectA name
// despite all Microsoft's dire warnings about loading from a simple name,
// you should be fine here as long as you don't have multiple versions of ProjectA
// floating around
foreach (Type t in projectA.GetTypes())
{
if (t.BaseType == typeof(Form))
{
var emptyCtor = t.GetConstructor(Type.EmptyTypes);
if (emptyCtor != null)
{
var f = (Form)emptyCtor.Invoke(new object[] { });
// t.FullName will help distinguish the unwanted entries and
// possibly later ignore them
string formItem = t.FullName + " // " + f.Text + " // " + f.Name;
checkedListBox1.Items.Add(formItem);
}
}
}
}
catch(Exception err)
{
// log exception
}
Another (probably cleaner solution) would be to have all the forms you're interested in inherit from a single base form. You could then load the assembly from that known Type and check that each enumerated Type inherits from it before adding it to your list. This is a more extensive change, however, and touches ProjectA.
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
{
Type[] types = a.GetTypes();
foreach (Type t in types)
{
if (t.BaseType == typeof(Form))
{
//Do Your works
}
}
}
Try This Code:
private void GetFormNames()
{
Type[] AllTypesInProjects = Assembly.GetExecutingAssembly().GetTypes();
for (int i = 0; i < AllTypesInProjects.Length; i++)
{
if (AllTypesInProjects[i].BaseType == typeof(Form))
{ /* Convert Type to Object */
Form f = (Form)Activator.CreateInstance(AllTypesInProjects[i]);
string FormText = f.Text;
listBox1.Items.Add(FormText);
}
}
}

I've stored form names in a data base. now i want to load forms by using those names

I've stored form names in a data base. Now I want to load forms by using those names.
Here is my table structure:
frmID, Item_Name, formName
Here is my code:
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
//TreeNode node = treeView1.SelectedNode;
string item = Convert.ToString(treeView1.SelectedNode);
int index = item.IndexOf(" ");
if (index > 0)
item = item.Substring(index + 1);
//MessageBox.Show(item);
var selectedFRM = from Menu in dbdata.Menus
where Menu.Item_Name == item
select Menu;
foreach (var pick in selectedFRM.Take(1))
{
string sel = pick.Form_Name;
Assembly asm = typeof(Form).Assembly;
Type type = asm.GetType(sel);
string df = Convert.ToString(type);
MessageBox.Show(df);
AssemblyName assemName = asm.GetName();
MessageBox.Show(assemName.Name);
try
{
Form frmChk = (Form)Activator.CreateInstance(type);
frmChk.Show();
}
catch (Exception)
{
MessageBox.Show("Error in loading form");
}
// MessageBox.Show(sel);
}
upto Assembly asm = typeof(Form).Assembly; code is working properly. How can I load a form by using the "formName" which is in the database
This is the problem, I suspect:
Assembly asm = typeof(Form).Assembly;
Type type = asm.GetType(sel);
That assembly will be the System.Windows.Forms assembly - which obviously doesn't include your specific forms. Just use any of the forms you know is in the right assembly, instead of Form:
// Or whatever you know about, of course...
Assembly asm = typeof(LoginForm).Assembly;
Note that the name will still need to be the namespace-qualified name.
Another (more flexible) alternative is to store the assembly-qualified name of the type which you can obtain via Type.AssemblyQualifiedName. Then you can just use Type.GetType(aqName) - which means your code will still work if you split your forms across multiple assemblies.
I found the solution. the Type type = asm.GetType(sel); line should corrected like this.
Type type = asm.GetType("WindowsFormsApplication6." + sel);
That means
Type type = asm.GetType("Namespace.Class");

Categories

Resources