tags:

views:

179

answers:

5

Hi,

I am wondering if it is possible to do something. I have a function that reads an xml file and adds controls to a form based on the contents on the file. An xml node like this will create it:

<Button Top="300" Left="100">Automatic</Button>

I have a function that saves the controls back to the xml file if I have added any in an edit mode. It is working, but I am wondering if there is an easier way. Currently, I have code that looks like this to create an instance of each control:

            switch (xmlchild.Name)
            {
                // Create a new control whose type is specified.
                case "Button":
                    c = new Button();
                    break;
                case "Label":
                    c = new Label();
                    break;
                default:
                    c = null;
                    break;
            }

But, when I want to use more types of controls, I will need to keep adding switch cases. Can I do something that will just take the text and add a control of that type? I would appreciate any feedback!

Thanks!

+2  A: 

If you control the contents of the XML file, then yes. You could use:

string fullNameSpace = "System.Windows.Controls.";
Type controlType = Type.GetType( fullNameSpace + xmlchild.Name );
if( controlType != null )
{
  // get default constructor...
  ConstructorInfo ctor = controlType.GetConstructor(Type.EmptyTypes);
  object control = ctor.Invoke(null);
}

You could also use the Activator class to clean this up a bit:

object control = Activator.CreateInstance( "System.Windows.Presentation", 
                                           xmlchild.Name );

Alternatively, if you can create a valid XAML file, you could use XamlReader to restore the control tree.

LBushkin
+1: Thanks for teaching me about Type.EmptyTypes (and also a good answer, but can only upvote once)
Lasse V. Karlsen
+1 Even though the code above will work, one issue is that the type name has to be correct so the developer won't/can't get away from checking the name of the control. In the end there will be an error checking method with a similar switch or if/else block.
Paul Sasik
@Paul - or a nice big `try ... catch (Exception)`... OK, maybe a smaller one with a more specific Exception type, but you don't HAVE to check it ahead of time.
Nelson
+1  A: 

You can use some convention to save yourself some code. Basically, use reflection to create an instance of whatever control you want, passing in the code from the XML. Something like:

Activator.CreateInstance(xmlchild.Name, ...)

Just fill in the right bits and a lot of that code goes away.

CubanX
+1  A: 

It sounds like you're looking for something like Generics.

You could do something as follows:

public static class ControlFactory
{
    public static T CreateControl<T>() where T : Type, new()
    {
        return new T();
    }
}

I haven't tested this code yet so I'll update it as I test.

Jamie Dixon
+1  A: 

See the Activator class in the first place. Then you can store the type name of the control as an attribute in the xml file and use the Activator class to create instances based on the value stored in an attribute from the current xml element (type="fullTypeName").

Further if it comes to properties you can consider reflection to set the properties on the created instances based an attribute name (i.e. you have an instance of a control - created with the Activator class - and you assign the vale from the Top attribute in xml to the Top property of the created instance which you can get/set by using reflection).

Padel
+1  A: 

It's possible to use Reflection as others have described to construct the control directly from its name. However, this design is fairly brittle and could also pose security concerns if the XML file is publicly visible, since arbitrary control types can be constructed at runtime based on the text in the file. This may not be a concern for your particular application.

I would prefer a design using a Dictionary<string, Func<Control>>, where you have code that registers a callback that creates the control for each name. This gives you some additional flexibility (you can dynamically swap out control construction methods or combine available controls from multiple sources) and can also look cleaner (the switch is replaced with a few 'register' calls and the actual construction broken out into separate methods or (in the simple case) as a lambda expression). This also allows backwards compatibility if you replace the TextBox in your application with a MyImprovedTextBox and want previous configuration files to use the improved version.

Dan Bryant