代码之家  ›  专栏  ›  技术社区  ›  bernhof

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

  •  1
  • bernhof  · 技术社区  · 15 年前

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

    这是为了简化当客户(或我自己)第10次改变主意时更改控件默认外观/行为的过程。这尤其与控制有关,比如 DataGridView 或第三方控制,其中有吨布局相关的财产维护。

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

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

    我就是这样想的:

    • 例1: 数据表格控件 默认情况下具有背景色 SystemColors.Window . 我自己设定 默认值为 Color.Blue (如何) 太可恶了!)。在设计器中, 使用默认背景色, 即背景色未设置 在.designer.cs文件中显式。 在运行应用程序时,会执行一部分代码,从而使网格 蓝色,由我指定。

    • 例2: 相同的背景色 数据表格控件 设置为 Color.Red 在设计师中。这个 覆盖我自己的蓝色默认值,显示红色背景 在网格中,包括设计时和运行时。


    解决方案

    我的解决方案是使用反射来检查 默认值 属性,由建议 Daniel Brückner .

    我重复一遍窗体上的所有控件,调用 SetDefaultValues 对于每个控件。对于要设置的每个属性值,我调用 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 回复  |  直到 15 年前
        1
  •  2
  •   Daniel Brückner Pradip    15 年前

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

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

    虽然不如仿制药那么漂亮,但你能让我做点什么吗? Control Builders 把这个取下来。

    编辑:

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

        3
  •  1
  •   wefwfwefwe    15 年前

    您可以覆盖页面,并通过所有控件进行循环,例如

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