繁体   English   中英

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

[英]Application-wide control defaults

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

这是为了简化客户端(或我本人)第十次改变主意时更改默认外观/控件行为的过程。 这尤其涉及到诸如DataGridView或第3方控件之类的控件,其中需要维护大量与布局相关的属性。

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

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

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

  • 示例1:默认情况下, DataGridView的背景色为SystemColors.Window 我将自己的默认值设置为Color.BlueColor.Blue离谱了!)。 在设计器中,使用默认的背景色,即未在.designer.cs文件中明确设置背景色。 运行应用程序时,将执行一部分代码,从而使网格变成蓝色,这是我指定的。

  • 示例2:在设计器中,同一DataGridView的背景色设置为Color.Red 这将覆盖我自己的默认值blue,即在设计时和运行时都在网格中显示红色背景。


对我来说,解决方案是使用反射来检查DefaultValue属性,所建议丹尼尔·布鲁克纳

我遍历窗体上的所有控件,为每个控件调用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);
  }
}

虽然不如泛型那么漂亮,但您也许可以通过Control Builders进行一些操作以实现这一目标。

编辑:

昨晚,我使用ControlBuilder对通用包装控件进行了快速原型设计。 我对结果不满意。 虽然您可能可以使它工作,但我相信一个新的Page或Container类可能会更简单。 我在测试中使用的源代码可在我的博客上找到

我已经使用或可以想到几种解决方案。

  1. 继承控件,但是您已经提到了。
  2. 一些更高级的控件库(如DevExpress)具有内置功能,可从配置文件(对于DevExpress,则为XML)加载布局,甚至完全可换肤(对于DevExpress也是如此)。
  3. 有时,我为控件创建扩展方法,并在用户控件或窗体的构造函数中调用它们。 这是启用或禁用功能集(例如排序)的简单方法。 数据网格中的多选或列重新排序,并提供一致的行为和外观。
  4. 使用数据绑定并将属性绑定到某些配置数据。 我相信甚至还有内置功能-用户设置或类似功能-但我从未使用过此功能。
  5. 在大型项目中,像上面建议的那样在所有控件上调用扩展方法不是很方便。 您可以在创建窗体时递归访问所有控件,查看属性,将其与默认值进行比较(使用反射来获取DefaultValue属性),如果它们不匹配(也就是说,该值已在中覆盖)设计人员)从某些文件或内存存储中加载默认值并应用它。

您可以改为覆盖页面,并循环浏览所有控件,例如

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM