views:

328

answers:

3

Does anyone know of a good component (C# WinForms) which would allow creating an options (settings) form, given a custom class with a bunch of properties? I am not looking for something shiny, but something merely better than a property grid. I can easily take care of the visual part, but I simply don't want to lose time doing reflection to add and bind controls if it already exists.

I am pretty sure I've seen a Visual Studio options-like form somewhere before, which was created dynamically (with some attributes attached to the properties of the class, to allow grouping and additional info).

[Edit] For example, I might have an options class:

public class Options : SerializableOptions<Options>
{
     [Category("General")]
     [Name("User name")]
     [Description("Some text")]
     public string Username { get; set; }

     [Category("General")]
     [Name("Log in automatically")]
     public bool LogInAutomatically { get; set; }

     [Category("Advanced")]
     // ConnectionType is enum
     public ConnectionType ConnectionType { get; set; }

     // ...
}

After passing it to this form, it would create two panels ("General" and "Advanced"), with a CheckBox and a TextBox on the first panel, and one ComboBox (with all available enums) on the second panel.

If there isn't such a control, what do you guys use? Manually add, populate, format and bind controls for each option?

+2  A: 

I'm not aware of any controls that allow you to do this, but it isn't difficult to do yourself. The easiest way is to create the dialog shell, a user control which acts as the base class for the options "panels", one (or more) attribute to control the name and grouping information, and an interface (which the user control implements).

Each of your custom options panels derives from the user control and overrides some sort of Initialize() and Save() method (provided by the user control). It also provides your attribute (or attributes) that determine the name/grouping information.

In the dialog shell, reflectively inspect all public types from your assembly (or all loaded assemblies) looking for types that implement your interface. As you find a type, get the attributes to determine where to place it in your grouping (easiest thing here is to use a tree view), call Activator.CreateInstance to create an instance of the user control and store it in the Tag property. When the user clicks on an entry in the grouping (a tree node), get the Tag and set the panel which contains the user control to the object in the Tag property. Finally, when the user clicks "OK" on the dialog, loop through the tree nodes, get the Tag property and call the Save method.

Update: Another option would be to use a property grid control. It doesn't have a "pretty" UI look to it, but it is very functional, already supports grouping by a category attribute, and allows a great deal of flexibility. You could go with a single property grid that shows all of the options, or go with a "hybrid" approach with a tree view that groups by major functions (plugin, capability, etc.), probably based on the type. When the user clicks that node, give the property grid the object instance. The only drawback to this approach is that when changes are made to the property grid values they are "live" in that the underlying property is immediately changed, which means there is no concept of "Cancel" short of saving a copy of each value that could change and performing some type of "reset" yourself.

Scott Dorman
But that would mean I have to implement actual functionality for each panel. I was hoping I could pass an instance of my class and get these panels populated automatically (e.g. each property might have a Category attribute which would define the appropriate panel).
Groo
@Groo: You could also do that. Pass an instance of your class to the panel's `Initialize` method, which would then reflectively inspect properties and populate itself. The biggest problem will be getting the layout of the controls, labels, etc. correct. It's **much** easier to write the panel yourself.
Scott Dorman
+1  A: 

I don't know if such a control exists, but writing the required reflection code is really not that hard. E.g. something like this:

// the class for which to create an UI
public class MyClass
{
    public string Text { get; set; }
    public int ID { get; set; }
}

...

// basic reflection code to build the UI for an object
var obj = new MyClass() { Text="some text", ID=3};

foreach (var pi in obj.GetType().GetProperties())
{
 var name = pi.Name;
 var type = pi.PropertyType;
 var value = pi.GetValue(obj, null);

 //now setup the UI control for this property and display the value
}
M4N
This part wouldn't be difficult, but there would have to be a factory (dictionary?) to instantiate and bind a proper control to each property, based on its type. This is the part (along with formatting, properly docking etc.) that I wanted to avoid doing from scratch. As I said, I could implement it, but would like to know if it already exists.
Groo
A: 

I accidentally found something similar to this, I remebered that I had this problem a while ago and thought I should share it.

Here is a simple example: http://blog.denouter.net/2008/08/simple-reflection-form.html. It uses reflection to create several controls based on object's properties.

Groo