简体   繁体   English

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

[英]Application-wide control defaults

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. 我正在寻找一种在C#.NET Windows应用程序中为不同类型的控件设置自己的默认属性值的方法。 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. 这尤其涉及到诸如DataGridView或第3方控件之类的控件,其中需要维护大量与布局相关的属性。

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

  • 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. 不得不继承我要为其指定自定义属性的每种控件的麻烦,更不用说覆盖/阴影化属性并设置DefaultValue属性了。
  • I can no longer use the standard .NET controls, but have to use the inherited controls. 我不能再使用标准的.NET控件,而必须使用继承的控件。
  • 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 . 示例1:默认情况下, DataGridView的背景色为SystemColors.Window I set my own default value to Color.Blue (how outrageous!). 我将自己的默认值设置为Color.BlueColor.Blue离谱了!)。 In the designer, the default background color is used, ie the background color is not set explicitly in the .designer.cs file. 在设计器中,使用默认的背景色,即未在.designer.cs文件中明确设置背景色。 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. 示例2:在设计器中,同一DataGridView的背景色设置为Color.Red This overrides my own default value of blue, showing a red background in the grid, both in design-time and run-time. 这将覆盖我自己的默认值blue,即在设计时和运行时都在网格中显示红色背景。


Solution

The solution for me was to use reflection to check the DefaultValue attribute, as suggested by Daniel Brückner . 对我来说,解决方案是使用反射来检查DefaultValue属性,所建议丹尼尔·布鲁克纳

I recurse through all controls on a form, calling SetDefaultValues for each control. 我遍历窗体上的所有控件,为每个控件调用SetDefaultValues 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. 对于每个要设置的属性值,我调用SetValue方法,该方法确保仅设置未更改其默认值的属性。

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. 在设计器中已明确设置但与默认值没有区别的属性将被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);
  }
}

While not as pretty as generics, you might me able to do something with Control Builders to pull this off. 虽然不如泛型那么漂亮,但您也许可以通过Control Builders进行一些操作以实现这一目标。

Edit: 编辑:

Last night I did a quick prototype of a generic wrapper control with the ControlBuilder. 昨晚,我使用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. 虽然您可能可以使它工作,但我相信一个新的Page或Container类可能会更简单。 The source code I used in my test is avalible on my blog . 我在测试中使用的源代码可在我的博客上找到

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). 一些更高级的控件库(如DevExpress)具有内置功能,可从配置文件(对于DevExpress,则为XML)加载布局,甚至完全可换肤(对于DevExpress也是如此)。
  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. 您可以在创建窗体时递归访问所有控件,查看属性,将其与默认值进行比较(使用反射来获取DefaultValue属性),如果它们不匹配(也就是说,该值已在中覆盖)设计人员)从某些文件或内存存储中加载默认值并应用它。

you could overide page instead and have a loop through all the controls eg 您可以改为覆盖页面,并循环浏览所有控件,例如

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