[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 697
  • Last Modified:

Windows Forms Usercontrol - Design-time Properties Enhancements

Hi All,

I want to create a usercontrol for a Windows Application that will expose a property in design-time which is a dropdown. The dropdown must contain all the Windows Forms of the project referencing this usercontrol.

Example:
- WindowsApplication1 has Form1, Form2 and Form3.
- WindowsApplication1 references the WindowsFormsControlLibrary1 project.
- Dragged the usercontrol from the toolbox onto Form1.
- I want to see a custom property of the usercontrol in design-time (from VS IDE Properties windows) with a dropdown of all the Forms in the Windows Application (Form1, Form2, and Form3).

I have managed to get a dropdown with all the Forms within the same project as the usercontrol, but can't manage to get the Forms from the Control's host application (see code of usercontrol below).

Can someone please help me modify the code to make it work?

Thanks!
Dave
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Design;
using System.Collections;
using System.Drawing.Design;

namespace PictureBoxControl
{
    [DefaultProperty("FormName")]
    public partial class PictureForm : UserControl
    {
        private string mform;
        private string mPictureFile = string.Empty;
        
        protected string strTest;

        public PictureForm()
        {
            InitializeComponent();


        }
            
        [Category("Custom")]
        [Browsable(true)]
        [Description("Select Windows Form from Project to open")]
        [TypeConverter(typeof(FieldNameConverter))]
       
        public string FormName
        { 
            get
            {
                return mform;
            }
            set 
            {
                mform = value;
            }

        }


        public class FieldNameConverter : TypeConverter
        {
            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                ArrayList alTest = new ArrayList();

                try
                {
                    System.Reflection.Assembly myass = System.Reflection.Assembly.GetExecutingAssembly();


                    foreach (Type type in myass.GetTypes())
                    {
                        if (type.BaseType.Name == "Form")
                        {
                            alTest.Add(type.Name);
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }

                StandardValuesCollection svc = new StandardValuesCollection(alTest);

                return svc;
            }
            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                return true;
            }

            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                return true;
            }

            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                
                return base.ConvertTo(context, culture, value, destinationType);
            }
            
        }

        public class FieldNameTypeEditor : UITypeEditor
        {
            public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
            {
               return UITypeEditorEditStyle.DropDown;
            }
        }



        [Category("Custom")]
        [Browsable(true)]
        [Description("Set path to image file.")]
        [Editor(typeof(System.Windows.Forms.Design.FileNameEditor),
          typeof(System.Drawing.Design.UITypeEditor))]
        public string PictureFile
        {
            get
            {
                return mPictureFile;
            }
            set
            {
                mPictureFile = value;

                if (!string.IsNullOrEmpty(mPictureFile))
                {
                    // set the image to the image file
                    pictureBox1.Image = Image.FromFile(mPictureFile);

                    // resize the image to match the image file
                    pictureBox1.Size = pictureBox1.Image.Size;
                }
            }
        }
        
            
        EmployeeDetails empdetails;

        private void pictureBox1_Click(object sender, EventArgs e)
        {

            MessageBox.Show("The form that we want to try and open is - " + mform);


             empdetails = new EmployeeDetails();
            Point startPoint = pictureBox1.PointToScreen(new Point(pictureBox1.Left, pictureBox1.Top));
            
            empdetails.Show();

            // Move the new form's position and resize
            empdetails.Top = startPoint.Y;
            empdetails.Left = startPoint.X;

            empdetails.Width = pictureBox1.Width;
            empdetails.Height = pictureBox1.Height;

            timer1.Enabled = true;
            
            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            int iGrowRate = 20;

            if (empdetails.Width < 1000)
            {
                // Increase width
                empdetails.Width += iGrowRate;

                // Stop going off the left screen
                if (empdetails.Left > iGrowRate / 2)
                {
                     empdetails.Left -= iGrowRate / 2;
                    
                }
                else
                {
                    empdetails.Left = 0;
                }

                // Increase height
                empdetails.Height += iGrowRate;

                if (empdetails.Top > iGrowRate / 2)
                {
                    empdetails.Top -= iGrowRate / 2;
                }
                else
                {
                    empdetails.Top = 0;
                }
            }
            else
            {
                timer1.Enabled = false;
            }
        }
    }
}

Open in new window

0
Dangeriz
Asked:
Dangeriz
  • 6
  • 6
1 Solution
 
Jens FiedererCommented:
Do you just want the ones added from the entry assembly (in which case you'd just repeat your loop after doing a
myass = System.Reflection.Assembly.GetEntryAssembly()
) or do you want all the forms in all the assemblies loaded?
0
 
Jens FiedererCommented:
(In the latter case, AppDomain.GetAssemblies gives you the assemblies over which you can iterate)
0
 
DangerizAuthor Commented:
I want all the Forms in the Windows Application's Assembly. I've already tried GetEntryAssembly and it doesn't work. I've also just tried AppDomain.CurrentDomain.GetAssemblies() and that didn't work either.
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
Jens FiedererCommented:
How does it not work?  Does GetEntryAssembly not give you the assembly you were expecting, or - once you have the assembly you wanted - do you not find the forms in it?
0
 
Jens FiedererCommented:
One thing that might be giving you trouble is if your forms are not directly derived from Form.

To get ALL forms you'd need to check

bool isMatch = false;
Type t = type;

while (t != null) {
     if (t.Name == "Form")
    {
        isMatch = true;
        break;
    }
    else
    {
        t = t.BaseType;
    }
}

0
 
DangerizAuthor Commented:
GetEntryAssembly does not work - I get the "object reference not set to an instance of the object" when trying to access any of the methods or properties, which means that it is null. It didn't get any assembly for the entry assembly.

However, the GetCallingAssembly works. But it it produces a strange list of forms (see picture attachment).

Here's a summary of the different methods with the existing code:
GetExecutingAssembly - Gets the Forms in the control's assembly (DLL project) and not the host's assembly (Windows Application project).
GetEntryAssembly - Returns NULL.
GetCallingAssembly - Returns a list of other forms (see picture).




GetCallingAssembly.jpg
0
 
DangerizAuthor Commented:
Here's an update:

GetEntryAssembly will get the correct assembly at runtime. But I'm not trying to get the assembly at runtime... I'm trying to get the assembly at design-time, so I can populate a dropdown for the custom property of the user control. I need to get the assembly of the Windows Application at design-time so I can get the forms of that assembly using the code above. If this can't work for some reason (maybe because the assembly of the Windows Application is not "created" yet  (or doesn't exist) in design-time, then I need another way to "get all the forms for the Windows Application project hosting the usercontrol".
0
 
Jens FiedererCommented:
From the "GetCallingAssembly", it looks like you are being called from the System.Windows.Forms assembly.

But all the assemblies should be available using  AppDomain.GetAssemblies - so if you restrict your search to those that interest you (either you know the name of the assembly of interest, or you can ignore the assemblies you are NOT interested in such as System.Windows.Forms), so if you run the same code on the appropriate assembly you get from THAT you should be ok.
0
 
DangerizAuthor Commented:
Yes!
AppDomain.GetAssemblies gets all the assemblies. I was able to load the proper assembly and get all the forms using the code below. I can at least see all the Forms in the dropdown!

The only thing left that needs to be changed is this part:
if (assem.GetName().Name == "ControlHost")
... Because I know that's the assembly name of the Windows Application. But what if the usercontrol is dragged on multiple Windows Applications with a different assembly name for each? How do I distinguish between them? Or the more appropriate question is "how can I identify that this assembly is for the hosting Windows Application?".

Thanks for your efforts thus far... you have been very helpful.



public class FieldNameConverter : TypeConverter
        {
            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                ArrayList alTest = new ArrayList();

                foreach (System.Reflection.Assembly assem in AppDomain.CurrentDomain.GetAssemblies())
                {
                    if (assem.GetName().Name == "ControlHost")
                    {
                        foreach (Type type in assem.GetTypes())
                         {
                             if (type.BaseType.Name == "Form")
                             {
                                 alTest.Add(type.Name);
                             }
                         }
                    }
                }
              
                StandardValuesCollection svc = new StandardValuesCollection(alTest);

                return svc;
            }

Open in new window

0
 
DangerizAuthor Commented:
I've managed to just check for the assemblie's CodeBase to see if it contained "exe", since it seems like the only exe assemblies available in the entire assemblies list is the ones for the Windows Application. Also, it found multiple assemblies (probably cached versions), so I just added code to stop after finding the first one.

Thanks for your help! This works great!
 public class FieldNameConverter : TypeConverter
        {
            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
               
                ArrayList alTest = new ArrayList();

                bool FoundOne = false;

                foreach (System.Reflection.Assembly assem in AppDomain.CurrentDomain.GetAssemblies())
                {
                    if (assem.CodeBase.IndexOf("exe") > -1 && FoundOne == false)
                    {
                        MessageBox.Show(assem.FullName);

                        foreach (Type type in assem.GetTypes())
                         {
                             if (type.BaseType.Name == "Form")
                             {
                                 alTest.Add(type.Name);
                             }
                         }

                        FoundOne = true;
                    }
                }
                              
                StandardValuesCollection svc = new StandardValuesCollection(alTest);

                return svc;
            }

Open in new window

0
 
DangerizAuthor Commented:
BTW:
When I said:
"I've also just tried AppDomain.CurrentDomain.GetAssemblies() and that didn't work either."
I looped through all the assemblies loaded, then did another loop to get all the assemblies' types. I think an exception occurred with one of the assemblies and I didn't have error handling, therefore the whole thing bombed out.

Looks like you answered my question way earlier, I just didn't write good code :p
0
 
Jens FiedererCommented:
> I've managed to just check for the assemblie's CodeBase to see if it contained "exe"

That's creative.  

I still think you might miss some forms if they are not DIRECTLY derived from "Form" - perhaps this is not a problem for you.
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 6
  • 6
Tackle projects and never again get stuck behind a technical roadblock.
Join Now