views:

136

answers:

3

I'm looking for a way to set up my own default property values for different types of controls in my C# .NET windows application. The default property values should 'override' the existing default values of the controls, but still be 'overridable' by setting the property values explicitly in the designer.

This is to simplify the process of changing default appearance/behaviour of controls when the client (or myself) change their mind for the 10th time. This relates especially to controls like the DataGridView or 3rd party controls where there are tons of layout-related properties to maintain.

I am aware of the ability to create inherited controls and use the DefaultValue attribute, but this is not the solution I'm looking for for a couple of reasons:

  • It's a hassle having to inherit of every type of control I want to specify custom properties for, not to mention overriding/shadowing the properties and setting the DefaultValue attribute.
  • I can no longer use the standard .NET controls, but have to use the inherited controls.
  • The number of inherited controls increases over time and clutters up the toolbox.
  • Myself or other developers on the project forget to use the new inhertied types in times of haste, resulting in inconsitent behaviour/appearance of controls.

This is how I imagined that it will work:

  • Example 1: A DataGridView by default has background color SystemColors.Window. I set my own default value to Color.Blue (how outrageous!). In the designer, the default background color is used, i.e. the background color is not set explicitly in the .designer.cs file. When running the application, a portion of code is executed, causing the grid to turn blue, as specified by me.

  • Example 2: The background color of the same DataGridView is set to Color.Red in the designer. This overrides my own default value of blue, showing a red background in the grid, both in design-time and run-time.


Solution

The solution for me was to use reflection to check the DefaultValue attribute, as suggested by Daniel Brückner.

I recurse through all controls on a form, calling SetDefaultValues for each control. For each property value to set, I call the SetValue method, which makes sure only properties that haven't been changed from their default values, are set.

There is one flaw in this approach, though. Properties that have been set explicitly in the designer, but do not differ from their default values, will be overwritten by the SetValue method.

void SetDefaultValues(Control control)
{
  if (control is DataGridView)
  {
    SetValue(control, "BackColor", Color.Blue);
  }
  else if (control is TextBox)
  {
    // etc.
  }
}

private static void SetValue(object control, string propertyName, object newValue)
{
  System.Reflection.PropertyInfo prop = control.GetType().GetProperty(propertyName);
  if (prop == null)
  {
    throw new ArgumentException(string.Format(
      "Specified property \"{0}\" does not exist on type \"{1}\".", prop.Name, control.GetType().FullName),
      "propertyName");
  }

  bool defaultValueFound = false;
  object defaultValue = null;
  foreach (object attr in prop.GetCustomAttributes(true))
  {
    if (attr is DefaultValueAttribute)
    {
      defaultValue = ((DefaultValueAttribute)attr).Value;
      defaultValueFound = true;
      break;
    }
  }

  if (!defaultValueFound && prop.PropertyType.IsValueType)
  {
    // Get default value for value types if no default value was specified by attributes:
    defaultValue = Activator.CreateInstance(prop.PropertyType);
  }
  if (defaultValue == null || defaultValue.Equals(prop.GetValue(control, null)))
  {
    // If default value matches current value, set new value:
    prop.SetValue(control, newValue, null);
  }
}
+1  A: 

you could overide page instead and have a loop through all the controls e.g.

foreach (Control c in Page.Controls)
{
   if (c is Textbox)
   {
       (Textbox)c.Color.blah.blah.blah ;)
   }
   ///etc
   Recurse through (c.Controls);
}
wefwfwefwe
I have implemented something like this myself, however, this does not solve the issue of not overwriting properies at run-time that have been set explicitly in the designer, which is one of my main concerns.
Bernhof
+2  A: 

There are several solutions I have used or I can think of.

  1. Inheriting the control, but you mentioned that already.
  2. Some more advanced control libraries (like DevExpress) have the build-in ability to load the layout from configuration files (XML in the case of DevExpress) or are even completly skinnable (true for DevExpress, too).
  3. Sometimes I create extension method for the controls and call them in the constructor of the user control or form. This is an easy way to enable or disable sets of functionalities like sorting. multiselect, or column reordering in data grids and gives a consistent behavior and look.
  4. Use data binding and bind the properties to some configuration data. I believe there is even a build in functionality - user settings or something like that - but I have never used this feature.
  5. Calling the extension method on all controls like proposed above is not very handy in larger projects. You could recursivly visit all controls in a form when it is created, look at the properties, compare to the default value (using reflection to get the DefaultValue attribute), and if they don't match (that is the value has been overriden in the designer) load your default value from some file or in-memory store and apply it.
Daniel Brückner
I like your idea of checking the DefaultValue attribute using reflection, since it might actually work. I'll try it out :)
Bernhof
I've updated my post. For now I've solved it by checking the DefaultValue attribute, at you suggested.
Bernhof
+3  A: 

While not as pretty as generics, you might me able to do something with Control Builders to pull this off.

Edit:

Last night I did a quick prototype of a generic wrapper control with the ControlBuilder. I am not happy with the results. While you can probably get it to work, I beleive a new Page or Container class might be a much simpler result. The source code I used in my test is avalible on my blog.

Matthew Whited
I never even knew such a thing existed before today. While I know there's always more to learn and improve in my .NET knowledge it's rare to find a feature that's existed even as far back as 2.0 that I didn't even know of!
Chris Marisic
This sounds interesting, though I'm not able to tell if it addresses the issue of explicitly set properties being ignored at run-time, as described in my second example. I'll read into it a little more, but I would also be grateful if you could give an example, when you find the time.
Bernhof
I didn't know about it until a quick search. just lucky on the search terms I guess :)
Matthew Whited
Thanks for the effort Matthew, I'll check the DefaultValueAttribute instead.
Bernhof