一、创建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();
}
}
}
}