在运行时在propertygrid中设置属性的类别

时间:2013-10-21 13:21:15

标签: c# attributes runtime categories propertygrid

我有一个问题要问你。我想在运行时在我的数据类中设置某些属性的category属性。我的数据类是自动生成的,我无法在设计时生成期间设置类别。

我在运行时使用了以下设置2个属性的类别,不幸的是,当我为第二个参数设置category属性时,也会再次更改第一个参数的类别....我缺少什么?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Diagnostics;

namespace myApplication
{
  public partial class Form1 : Form
  {
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
      this.SuspendLayout();
      // 
      // propertyGrid1
      // 
      this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
      this.propertyGrid1.Location = new System.Drawing.Point(0, 0);
      this.propertyGrid1.Name = "propertyGrid1";
      this.propertyGrid1.Size = new System.Drawing.Size(284, 262);
      this.propertyGrid1.TabIndex = 0;
      // 
      // Form1
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(284, 262);
      this.Controls.Add(this.propertyGrid1);
      this.Name = "Form1";
      this.Text = "Form1";
      this.Load += new System.EventHandler(this.Form1_Load);
      this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.PropertyGrid propertyGrid1;
    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
      Object selectedObject = new Shape()
        {
          x = 100,
          y = 200
        };

      var x = TypeDescriptor.GetProperties(selectedObject)["x"].Attributes;
      CategoryAttribute attrx = (CategoryAttribute)x[typeof(CategoryAttribute)] as CategoryAttribute;

      FieldInfo category_x = attrx.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
      if (category_x != null)
      {
        if (category_x.FieldType == "string".GetType())
        {
          category_x.SetValue(TypeDescriptor.GetProperties(selectedObject)["x"].Attributes[typeof(CategoryAttribute)], "A_Category_For_x");
        }
      }

      Debug.Assert(attrx.Category == "A_Category_For_x");

      var y = TypeDescriptor.GetProperties(selectedObject)["y"].Attributes;
      CategoryAttribute attry = (CategoryAttribute)y[typeof(CategoryAttribute)] as CategoryAttribute;

      FieldInfo category_y = attry.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
      if (category_y != null)
      {
        if (category_y.FieldType == "string".GetType())
        {
          category_y.SetValue(TypeDescriptor.GetProperties(selectedObject)["y"].Attributes[typeof(CategoryAttribute)], "A_Category_For_y");
        }
      }

      Debug.Assert(attrx.Category == "A_Category_For_x"); // here is stops... why is category for x changed????
      Debug.Assert(attry.Category == "A_Category_For_y");



      propertyGrid1.SelectedObject = selectedObject;
      // Force the PropertyGrid to redraw itself
      propertyGrid1.Refresh();
    }

  }

  public partial class Shape : System.ComponentModel.INotifyPropertyChanged
  {
    private short yField;
    private short xField;

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public short y
    {
      get
      {
        return this.yField;
      }
      set
      {
        if ((this.yField != null))
        {
          if ((yField.Equals(value) != true))
          {
            this.yField = value;
            this.OnPropertyChanged("y");
          }
        }
        else
        {
          this.yField = value;
          this.OnPropertyChanged("y");
        }
      }
    }


    [System.Xml.Serialization.XmlAttributeAttribute()]
    public short x
    {
      get
      {
        return this.xField;
      }
      set
      {
        if ((this.xField != null))
        {
          if ((xField.Equals(value) != true))
          {
            this.xField = value;
            this.OnPropertyChanged("x");
          }
        }
        else
        {
          this.xField = value;
          this.OnPropertyChanged("x");
        }
      }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string propertyName)
    {
      System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
      if ((handler != null))
      {
        handler(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
      }
    }
  }
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

更新/额外信息 我认为.net没有明确设置时,它与CategoryAttribute具有相同的实例。如果我设置它,我可以改变每个属性的类别,如下所示:

    using System.ComponentModel;
    using System.Diagnostics;
    using System.Reflection;
    using System;
    using System.Linq;

    namespace CSharpConsoleApplication
    {
      public static class Program
      {
        public static void Main()
        {
          Object selectedObject = new myDataClass
          {
            x = 100,
            y = 200
          };

          CategoryAttribute attrx =
             selectedObject.GetType().GetProperty("x").GetCustomAttributes(typeof(CategoryAttribute), false).Single() as
             CategoryAttribute;

          FieldInfo category_x = attrx.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
          if (category_x != null)
          {
            if (category_x.FieldType == "string".GetType())
            {
              category_x.SetValue(attrx, "categoryX");
            }
          }

          Debug.Assert(attrx.Category == "categoryX");

          CategoryAttribute attry =
             selectedObject.GetType().GetProperty("y").GetCustomAttributes(typeof(CategoryAttribute), false).Single() as
             CategoryAttribute;

          FieldInfo category_y = attry.GetType().GetField("categoryValue", BindingFlags.NonPublic | BindingFlags.Instance);
          if (category_y != null)
          {
            if (category_y.FieldType == "string".GetType())
            {
              category_y.SetValue(attry, "categoryY");
            }
          }

          Debug.Assert(attrx.Category == "categoryX"); //success now!
          Debug.Assert(attry.Category == "categoryY");
        }

        public partial class myDataClass
        {
          [CategoryAttribute("Test")]
          public int x { get; set; }

          [CategoryAttribute("Default")]
          public int y { get; set; }
        }
      }
    }

正如你所看到的,这是成功的!不幸的是,我的部分类 myDataClass 是自动生成的,我必须在每次生成后手动设置CategoryAttribute。我不想这样做但是在运行时设置它......

可能这个额外的信息可以帮助??

1 个答案:

答案 0 :(得分:1)

我已经解决了这个问题!使用此anwer:How to add property-level Attribute to the TypeDescriptor at runtime?

我创建了2个类

  • PropertyOverridingTypeDescriptor.cs
  • TypeDescriptorOverridingProvider.cs

并在我的更新方法中放置以下代码段(就像上面的回答链接中所述):

// prepare our property overriding type descriptor
PropertyOverridingTypeDescriptor ctd = new PropertyOverridingTypeDescriptor(TypeDescriptor.GetProvider(selectedObject).GetTypeDescriptor(selectedObject));

// iterate through properies in the supplied object/type
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(selectedObject))
{
  // for every property that complies to our criteria
  if (pd.Name.EndsWith("x") || pd.Name.EndsWith("y"))
  {
    // we first construct the custom PropertyDescriptor with the TypeDescriptor's
    // built-in capabilities
    PropertyDescriptor pd2 =
        TypeDescriptor.CreateProperty(
            selectedObject.GetType(), // or just _settings, if it's already a type
            pd, // base property descriptor to which we want to add attributes
      // The PropertyDescriptor which we'll get will just wrap that
      // base one returning attributes we need.
            new CategoryAttribute("Location")
      // this method really can take as many attributes as you like,
      // not just one
        );

    // and then we tell our new PropertyOverridingTypeDescriptor to override that property
    ctd.OverrideProperty(pd2);
  }

}

// then we add new descriptor provider that will return our descriptor istead of default
TypeDescriptor.AddProvider(new TypeDescriptorOverridingProvider(ctd), selectedObject);