应用程序范围的控件默认值

时间:2009-08-28 13:03:25

标签: c# .net winforms default-value

我正在寻找一种方法来为我的C#.NET Windows应用程序中的不同类型的控件设置我自己的默认属性值。默认属性值应“覆盖”控件的现有默认值,但仍可通过在设计器中显式设置属性值来“覆盖”。

这是为了简化在客户端(或我自己)第10次改变主意时更改控件的默认外观/行为的过程。这尤其涉及DataGridView或第三方控件等控件,其中需要维护大量与布局相关的属性。

我知道能够创建继承的控件并使用DefaultValue属性,但这不是我正在寻找的解决方案,原因有两个:

  • 要继承我想要指定自定义属性的每种类型的控件,更不用说覆盖/遮蔽属性并设置DefaultValue属性,这是一件麻烦事。
  • 我不能再使用标准的.NET控件,但必须使用继承的控件。
  • 继承控件的数量会随着时间的推移而增加,并使工具箱变得混乱。
  • 我自己或项目的其他开发人员在匆忙的时候忘记使用新的inhertied类型,导致控件的行为/外观不一致。

这就是我想象它会起作用的方式:

  • 示例1:默认情况下,DataGridView具有背景色 SystemColors.Window。我自己设定 默认值为Color.Blue(如何 岂有此理!)。在设计师, 使用默认背景颜色, 即没有设置背景颜色 明确地在.designer.cs文件中。 运行应用程序时,会执行一部分代码,导致网格转动 蓝色,由我指定。

  • 示例2:同一DataGridView的背景颜色设置为 设计师Color.Red。这个 覆盖我自己的默认值蓝色,显示红色背景 在网格中,无论是在设计时还是在运行时。


解决方案

我的解决方案是使用反射检查DefaultValue属性,如Daniel Brückner所示。

我递归表单上的所有控件,为每个控件调用SetDefaultValues。对于要设置的每个属性值,我调用SetValue方法,该方法确保仅设置未从其默认值更改的属性。

这种方法存在一个缺陷,但。已在设计器中显式设置但与默认值不同的属性将被SetValue方法覆盖。

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);
  }
}

3 个答案:

答案 0 :(得分:3)

虽然没有仿制药那么漂亮,但您可以使用Control Builders做一些事情来解决这个问题。

修改

昨晚我用ControlBuilder做了一个通用包装器控件的快速原型。我对结果不满意。虽然你可以让它工作,但我相信一个新的Page或Container类可能是一个更简单的结果。我在我的测试中使用的源代码可以在我的blog上使用。

答案 1 :(得分:2)

我使用过或者我能想到的几种解决方案。

  1. 继承控件,但你已经提到过了。
  2. 一些更高级的控制库(如DevExpress)具有从配置文件加载布局的内置功能(在DevExpress的情况下为XML),或者甚至可以完全换肤(对于DevExpress也是如此)。
  3. 有时我会为控件创建扩展方法,并在用户控件或表单的构造函数中调用它们。这是启用或禁用排序等功能集的简便方法。数据网格中的多选或列重新排序,并提供一致的行为和外观。
  4. 使用数据绑定并将属性绑定到某些配置数据。我相信甚至有功能的构建 - 用户设置或类似的东西 - 但我从未使用过这个功能。
  5. 在上面提到的所有控件上调用扩展方法在大型项目中不是很方便。您可以在创建表单时重新访问表单中的所有控件,查看属性,与默认值进行比较(使用反射获取DefaultValue属性),以及它们是否匹配(即已重写的值)设计者)从某个文件或内存存储中加载默认值并应用它。

答案 2 :(得分:1)

你可以覆盖页面,并循环遍历所有控件,例如

foreach (Control c in Page.Controls)
{
   if (c is Textbox)
   {
       (Textbox)c.Color.blah.blah.blah ;)
   }
   ///etc
   Recurse through (c.Controls);
}