C#控件PropertyGrid使用

一、创建winform并在界面添加propertyGrid控件。

二、创建一个类并新增需要的属性和字段,用于添加到propertyGrid控件上显示,并用XML序列化和反序列化,保存在本地和读取显示。

三、用特性设定类中属性和字段,在propertyGrid控件上的:名称、显示、分组、描述。

四、用反射设定propertyGrid控件第1列(属性名称)的列宽,说明窗口高度,属性显示或隐藏


using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization;
using System.Xml.Serialization;

namespace LearningRecords
{
    /// <summary>
    /// 自定义属性下拉效果的类,该类主要继承StringConverter类,并重载该类的一些虚拟方法
    /// </summary>
    public class genderItem : StringConverter
    {
        //true 启用,false 禁用
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            return new StandardValuesCollection(new string[] { "MoveMinPowerDiff", "MoveMaxPowerSum" }); //编辑下拉框中的items
        }

		//true:禁用文本编辑,false:启用文本编辑
		public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }
    }

    / <summary>
    / 
    / </summary>
    //[XmlInclude(typeof(DeviceSetting))]
    //[TypeConverter(typeof(ExpandableObjectConverter)), Category("参数")]
    public class DeviceSetting
    {

		private int singleScanMode_m;
        
        /// <summary>
		///  单轴扫描模式,0=范围步长方式,1=范围速度方式,2=递增方式
		/// </summary>
		[Browsable(true)]//是否在窗口显示
		[Category("1.扫描参数")]//对参数进行分组
		[DisplayName("单轴扫描模式")]//设置参数显示名称
		[Description("0=范围(步长)\r\n1=范围(速度),2=递增")]//参数描述
		public int SingleScanMode
		{
			get
			{
				return singleScanMode_m;
			}

			set
			{
				if (value < 0 || value > 2)//限制设定值,避免错误
				{
					throw new Exception("给定的模式只能是:0,1,2");//抛出异常提示
				}
				singleScanMode_m = value;
			}
		}

		#region//常用参数
		/// <summary>
		/// PI耦合扫描范围um
		/// </summary>
		//[ReadOnly(false)]//是否只读
		//[DefaultValue(false)]//默认值

		[Browsable(false)]//是否在窗口显示
		[Category("1.参数1")]//对参数进行分组
		[DisplayName("设备名称")]//设置参数显示名称
		[Description("以此保存加载参数")]//参数描述
		public string DevName
		{
			get;
			set;
		}

		/// <summary>
		/// 扫描步长um
		/// </summary>
		[Browsable(true)]//是否在窗口显示
        [Category("2.参数2")]//对参数进行分组
        [DisplayName("扫描步长um")]//设置参数显示名称
        [Description("最小步长为0.1um")]//参数描述
        public double ScanStep
        {
            get;
            set;
        }

        /// <summary>
        /// Z轴最大绝对位置mm
        /// </summary>
        [Browsable(true)]//是否在窗口显示
        [Category("2.参数2")]//对参数进行分组
        [DisplayName("Z轴最大绝对位置mm")]//设置参数显示名称
        [Description("芯片与耦合头最小间距\r\n安全防呆以免碰撞")]//参数描述
        public double MaxPosZ
        {
            get;
            set;
        }

        /// <summary>
        /// 额定参考值
        /// </summary>
        [Browsable(true)]//是否在窗口显示	
        [Category("2.参数2")]//对参数进行分组
        [DisplayName("额定参考值")]//设置参数显示名称
        [Description("判定扫描结果\r\n及自动跟随时是否运动")]//参数描述
        public double RatedRefValue
        {
            get;
            set;
        }

        /// <summary>
        /// 多路耦合尾光束额定参考值
        /// </summary>
        [Browsable(true)]//是否在窗口显示	
        [Category("3.参数3")]//对参数进行分组
        [DisplayName("尾光束参考值")]//设置参数显示名称
        [Description("判定多路耦合,\r\n尾光束功率是否达标")]//参数描述
        public double RatedRefValue_End
        {
            get;
            set;
        }

        /// <summary>
        /// 扫描范围um
        /// </summary>
        [Browsable(true)]//是否在窗口显示	
        [Category("3.参数3")]//对参数进行分组
        [DisplayName("扫描范围um")]//设置参数显示名称
        [Description("单边最大移动距离\r\n如100um则总长200um")]//参数描述
        public double ScanRange
        {
            get;
            set;
        }

        /// <summary>
        /// 单方向扫描一直递减时,最大移动的步长次数--爬山算法扩展
        /// </summary>
        [Browsable(true)]//是否在窗口显示
        [Category("3.参数3")]//对参数进行分组
        [DisplayName("单方向递减次数")]//设置参数显示名称
        [Description("参考值连续递减次数\r\n超过此值则反方向找寻")]//参数描述
        public uint DecrementNum
        {
            get;
            set;
        }

		/// <summary>
		/// 角度扫描完后,移动的函数
		/// </summary>
		[Browsable(true)]//是否在窗口显示	
		[Category("4.算法参数")]//对参数进行分组
		[DisplayName("多路-移动函数")]//设置参数显示名称
		[Description("RzRy轴角度扫描完后,\r\n移动到最佳点函数")]//参数描述
		[TypeConverter(typeof(genderItem))] //使用自定义的属性下拉item类3.项目参数
		public string MethodName
		{
			get;
			set;
		}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		public DeviceSetting()
        {
            RatedRefValue = -0.15;
            RatedRefValue_End = -15;

            MaxPosZ = 10;
            ScanStep = 0.001;
            ScanRange = 0.5;
            DecrementNum = 5;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="strName">硬件名称</param>
        /// <returns></returns>
        public DeviceSetting(string strName)
        {
            DevName = strName;
        }

		/// <summary>
		/// 加载参数
		/// </summary>
		/// <param name="strName">硬件名称</param>
		/// <returns></returns>
		public static DeviceSetting Load(string strName)
		{
			DeviceSetting setting = new DeviceSetting();
			string configFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, strName + "_Config.xml");

			if(File.Exists(configFileName))
			{
				FileStream fs = new FileStream(configFileName, FileMode.Open);
				try
				{
					XmlSerializer formatter = new XmlSerializer(typeof(DeviceSetting));
					setting = (DeviceSetting)formatter.Deserialize(fs);
				}
				catch (SerializationException e)
				{
					Console.WriteLine("Failed to deserialize. Reason: " + e.Message);
				}
				finally
				{
					fs.Close();
				}
			}
			
			setting.DevName = strName;

			return setting;
		}
		
		/// <summary>
		/// 保存参数
		/// </summary>
		/// <returns></returns>
		public bool Save()
		{
			if (string.IsNullOrEmpty(DevName))
				return false;

			bool result = true;
			string configFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DevName + "_Config.xml");
			FileStream fs = new FileStream(configFileName, FileMode.Create);
			XmlSerializer formatter = new XmlSerializer(typeof(DeviceSetting));
			try
			{
				formatter.Serialize(fs, this);
			}
			catch (SerializationException e)
			{
				Console.WriteLine("Failed to serialize. Reason: " + e.Message);
				result = false;
			}
			finally
			{
				fs.Close();
			}
			return result;
		}
	}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LearningRecords
{
	public partial class Form1 : Form
	{
		DeviceSetting m_Setting;
		public Form1()
		{
			InitializeComponent();
		}

		private void Form1_Load(object sender, EventArgs e)
		{
			m_Setting = DeviceSetting.Load("配置文件");
			propertyGrid1.SelectedObject = m_Setting;

			int width = Convert.ToInt32(propertyGrid1.Width * 0.7);
			int height = Convert.ToInt32(propertyGrid1.Height * 0.2);
			SetPropertyGridColumnWidth(propertyGrid1, width);
			SetDescriptionHeight(propertyGrid1, height);
		}

		private void AddLog(string strLog)
		{
			if (strLog == null || strLog == "")
			{
				return;
			}
			string logFilePath = string.Format(@"D:\Data\Log\{0}\{1}.csv", DateTime.Now.ToString("yyyMM"), DateTime.Now.ToString("yyyMMdd"));
			string dirPath = Path.GetDirectoryName(logFilePath);
			if (!Directory.Exists(dirPath))
			{
				Directory.CreateDirectory(dirPath);
			}
			strLog = DateTime.Now.ToString("HH:mm:ss.f") + "," + strLog;
			string strError = "";

			try
			{
				File.AppendAllText(logFilePath, strLog + "\r\n", Encoding.UTF8);
			}
			catch (Exception ex)
			{
				strError = DateTime.Now.ToString("HH:mm:ss.f") + "," + ex.Message.ToString();
			}

			this.Invoke((MethodInvoker)delegate
			{
				lstShowLog.Items.Add(strLog);
				if (strError != "")
				{
					lstShowLog.Items.Add(strError);
				}

				lstShowLog.SelectedIndex = lstShowLog.Items.Count - 1;
				lstShowLog.TopIndex = lstShowLog.Items.Count - 1;

				if (lstShowLog.Items.Count > 200)//满200行,就删除前190行
				{
					for (int i = 0; i < 40; i++)
					{
						lstShowLog.Items.RemoveAt(0);
					}
				}
			});
		}

		/// <summary>
		/// 设置propertyGrid第1列(属性名称)的列宽
		/// </summary>
		/// <param name="grid"></param>
		/// <param name="width"></param>
		private void SetPropertyGridColumnWidth(PropertyGrid grid, int width)
		{
			try
			{
				if (grid == null)
					return;

				FieldInfo fi = grid.GetType().GetField("gridView", BindingFlags.Instance | BindingFlags.NonPublic);
				if (fi == null)
					return;

				Control view = fi.GetValue(grid) as Control;
				if (view == null)
					return;

				MethodInfo mi = view.GetType().GetMethod("MoveSplitterTo", BindingFlags.Instance | BindingFlags.NonPublic);
				if (mi == null)
					return;
				mi.Invoke(view, new object[] { width });
			}
			catch (Exception ex)
			{
				AddLog("设置属性窗格名称列宽异常:" + ex.ToString());
			}
		}

		/// <summary>
		/// 设置propertyGrid说明窗格高度
		/// </summary>
		/// <param name="grid"></param>
		/// <param name="height"></param>
		private void SetDescriptionHeight(PropertyGrid grid, int height)
		{
			try
			{
				if (grid == null)
					return;

				foreach (Control control in grid.Controls)
				{
					if (control.GetType().Name == "DocComment")
					{
						control.Invoke((MethodInvoker)delegate
						{
							control.Height = height;
							FieldInfo fieldInfo = control.GetType().BaseType.GetField("userSized", BindingFlags.Instance | BindingFlags.NonPublic);
							fieldInfo.SetValue(control, true);
						});
					}
				}
			}
			catch (Exception error)
			{
				AddLog("设置属性说明窗格高度异常:" + error.ToString());
			}
		}
		
		/// <summary>
		/// 通过反射以“类别属性”名称判别是否可见
		/// </summary>
		/// <param name="obj">属性对象</param>
		/// <param name="showCategoryContains">需显示的属性名称包含字符串</param>
		/// <param name="hideCategoryContains">需隐藏的属性名称包含字符串</param>
		public static void SetCategoryVisibility(object obj, string showCategoryContains, string hideCategoryContains)
		{
			Type typeBrow = typeof(BrowsableAttribute);
			Type typeCate = typeof(CategoryAttribute);

			PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj);

			for (int i = 0; i < props.Count; i++)
			{
				AttributeCollection attrs = props[i].Attributes;//获取特性集合,索引、属性名称都可以获取

				CategoryAttribute category = (CategoryAttribute)attrs[typeCate];
				if (showCategoryContains != "" && category.Category.Contains(showCategoryContains))
				{
					FieldInfo fld = typeBrow.GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic);
					fld.SetValue(attrs[typeBrow], true);
				}

				if (hideCategoryContains != "" && category.Category.Contains(hideCategoryContains))
				{
					FieldInfo fld = typeBrow.GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic);
					fld.SetValue(attrs[typeBrow], false);
				}
			}
		}

		/// <summary>
		/// 通过反射以“显示名称”判别是否可见
		/// </summary>
		/// <param name="obj">属性对象</param>
		/// <param name="showNameContains">需显示的名称包含字符串</param>
		/// <param name="hideNameContains">需隐藏的名称包含字符串</param>
		public static void SetDisplayNameyVisibility(object obj, string showNameContains, string hideNameContains)
		{
			Type typeBrow = typeof(BrowsableAttribute);
			Type typeCate = typeof(DisplayNameAttribute);

			PropertyDescriptorCollection props = TypeDescriptor.GetProperties(obj);

			for (int i = 0; i < props.Count; i++)
			{
				AttributeCollection attrs = props[i].Attributes;//获取特性集合,索引、属性名称都可以获取

				DisplayNameAttribute display = (DisplayNameAttribute)attrs[typeCate];
				if (showNameContains != "" && display.DisplayName.Contains(showNameContains))
				{
					FieldInfo fld = typeBrow.GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic);
					fld.SetValue(attrs[typeBrow], true);
				}

				if (hideNameContains != "" && display.DisplayName.Contains(hideNameContains))
				{
					FieldInfo fld = typeBrow.GetField("browsable", BindingFlags.Instance | BindingFlags.NonPublic);
					fld.SetValue(attrs[typeBrow], false);
				}
			}
		}

		private void Form1_FormClosing(object sender, FormClosingEventArgs e)
		{
			if(m_Setting !=null)
			{
				m_Setting.Save();
			}
		}
	}
}

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的 C# PropertyGrid 实例: ```csharp using System.ComponentModel; using System.Windows.Forms; public class Person { public string Name { get; set; } public int Age { get; set; } public string Gender { get; set; } } public class MainForm : Form { private PropertyGrid propertyGrid; private Person person; public MainForm() { InitializeComponent(); InitializePerson(); InitializePropertyGrid(); } private void InitializeComponent() { this.propertyGrid = new PropertyGrid(); this.SuspendLayout(); // // propertyGrid // this.propertyGrid.Dock = DockStyle.Fill; this.propertyGrid.Location = new System.Drawing.Point(0, 0); this.propertyGrid.Name = "propertyGrid"; this.propertyGrid.Size = new System.Drawing.Size(284, 262); this.propertyGrid.TabIndex = 0; // // MainForm // this.ClientSize = new System.Drawing.Size(284, 262); this.Controls.Add(this.propertyGrid); this.Name = "MainForm"; this.Text = "PropertyGrid Example"; this.ResumeLayout(false); } private void InitializePerson() { person = new Person(); person.Name = "John Doe"; person.Age = 30; person.Gender = "Male"; } private void InitializePropertyGrid() { propertyGrid.SelectedObject = person; } } ``` 在此示例中,我们创建了一个名为“Person”的类,该类具有三个属性:Name、Age 和 Gender。然后,我们创建一个名为“MainForm”的窗体,并在其中添加一个 PropertyGrid 控件。在 MainForm 的构造函数中,我们初始化了 Person 对象并将其分配给 PropertyGrid 的 SelectedObject 属性。这将使 PropertyGrid 显示 Person 对象的所有属性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值