文章目录
本解决方案包括三个项目:
- CLCDataGridView:自定义的DataGridView,实现了自动生成列样式以及保存加载列顺序;
- CLCComboBox:自定义的ComboBox,实现了自定义下拉项;
- user-control:针对自定义控件的测试项目;
1. CLCDataGridView 介绍
1.1 自动生成列样式规则介绍
当改变CLCDataGridView中的数据源(DataSoource)时,会利用c#中的反射机制,根据实体中的属性自动生成列样式。
默认情况下,对于一个正常的属性,例如:
public string Name{get;set;}
会生成如下的列样式:
格式为DataGridViewTextBoxColumn
Name、HeaderText、DataPropertyName默认为属性名
DisplayIndex默认为属性在实体中的声明次序
Visible默认为true
其余未指明的DataGridView列属性,按照DataGridView中的默认值进行确定
为了更改默认的列属性,我们可以在属性上标注自定义的特性[ColumnStyle]
,例如:
[ColumnStyle(
HeaderText = "姓名",
ColumnType = ColumnStyleEnum.ButtonColumn)]
public string Name { get; set; }
表示属性Name对应的列,他的HeaderText为“姓名”,格式为DataGridViewButtonColumn。
完整的ColumnStyleAttribute
定义如下:
public class ColumnStyleAttribute : System.Attribute
{
public string Name { get; set; }
public string HeaderText { get; set; }
public int Width { get; set; }
private int displayIndex;
public int DisplayIndex
{
get => displayIndex;
set
{
if (value <= 0)
{
displayIndex = -1;
return;
}
displayIndex = value;
}
}
public string DataPropertyName { get; set; }
public string Visible { get; set; }
public ColumnStyleEnum ColumnType { get; set; }
public DataGridViewAutoSizeColumnMode AutoSizeMode { get; set; }
}
public enum ColumnStyleEnum
{
TextBoxColumn,
ButtonColumn,
CheckBoxColumn,
ComboBoxColumn,
ImageColumn,
LinkColumn
}
如果我们不想再CLCDataGridView中生成对应属性的列,那么我们可以在属性上标注[Ignore]
:
[Ignore]
public int Age { get; set; }
1.2 列顺序的自动保存与加载
在CLCDataGridView中,我们提供了两个属性:
- EnableSave:指示是否保存列顺序
- SaveModel:指示列顺序的保存方式,有两种模式,模式一表示在程序的运行目录下以XML格式保存,模式二表示在一用户电脑缓存中以JSON格式保存;
演示:启动测试项目,加载数据源,然后更改列的属性,再次加载同样的数据源时,列顺序保持用户更改后的样子。
首先将列顺序保存属性置为true,然后选择保存模式一:
当切换数据源或关闭程序时,会保存当前的列顺序,模式一的保存路径和XML格式如下:
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<clcDataGridView1>
<Name>Tag</Name>
<DisplayIndex>2</DisplayIndex>
</clcDataGridView1>
<clcDataGridView1>
<Name>Name</Name>
<DisplayIndex>1</DisplayIndex>
</clcDataGridView1>
<clcDataGridView1>
<Name>Gender</Name>
<DisplayIndex>0</DisplayIndex>
</clcDataGridView1>
......省略
如果我们将保存模式切换为模式二:
那么列顺序的保存路径为:
C:\Users\Administrator\AppData\Local\user_control\user-control.exe_Url_yjbhv1k2vle1gqa3obi1vjsego23avin\1.0.0.0
JSON保存样式为:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="ColumnStyles" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<ColumnStyles>
<setting name="DataTablesJsonString" serializeAs="String">
<value>{"clcDataGridView1-Com.Lee.Model.Student":[{"Name":"Tag","DisplayIndex":"1"},{"Name":"Name","DisplayIndex":"2"},{"Name":"Gender","DisplayIndex":"0"},{"Name":"Age","DisplayIndex":"3"},{"Name":"School","DisplayIndex":"4"},{"Name":"Address","DisplayIndex":"5"},{"Name":"Phone","DisplayIndex":"6"}],"clcDataGridView1-Com.Lee.Model.User":[{"Name":"Tag","DisplayIndex":"1"},{"Name":"Name","DisplayIndex":"2"},{"Name":"Password","DisplayIndex":"3"},{"Name":"Address","DisplayIndex":"0"}]}</value>
</setting>
</ColumnStyles>
</userSettings>
</configuration>
1.3 关于数据源
CLCDataGridView可接受的数据源(DataSource)的格式为列表(List<T>
)或实体数组。
例如,以下两类数据都是合法的数据源:
public static List<Student> Students = new List<Student>()
{
new Student("张三", "男", 20, "中山大学", "广州", "18909880099"),
new Student("小红", "女", 21, "厦门大学", "厦门", "19093222099"),
new Student("肖华", "男", 20, "重庆大学", "重庆", "16743746759")
};
public static Student[] StudentArray = new[]
{
new Student("张三数组", "男", 20, "中山大学", "广州", "18909880099"),
new Student("小红数组", "女", 21, "厦门大学", "厦门", "19093222099"),
new Student("肖华数组", "男", 20, "重庆大学", "重庆", "16743746759")
};
但是,以下类型的数据是不合法的:
- 非列表或非数组类型,如string 、int 等,当给CLCDataGridView赋值如下时,会提示如下:
-
object类型列表:
List<object> objects = new List<object>() { new User("name","pwd",12), new Student("张三", "男", 20, "中山大学", "广州", "18909880099"), new Student("肖华", "男", 20, "重庆大学", "重庆", "16743746759") };
上面的声明是正确的,但是无法从object中解析出列样式,所以无法赋值给CLCDataGridView:
2. CLCComboBox 介绍
2.1 CLCComboBox的基础使用
CLCComboBox提供了让您自定义下拉面板的灵活性,只需要在我们的项目中引入CLCComboBox相关的库文件,然后继承BaseDropDownItem
实现定义您自己的下拉面板,最后通过以下代码指定下拉面板:
clcComboBox.DropDownItem = new xxxDropDownItem();
我们提供了两种下拉面板:
- ListDropDownItem:带搜索的列表展示
- CategoryDropDownItem:分类的列表展示
2.2 数据源、显示成员、值成员
数据源(DataSource),显示成员(DisplayMember)、值成员(ValueMember)的语义与ComboBox相同。
当需要给CLCComboBox设置数据源时,我们提供了以下方法:
public void SetDataSource(object dataSource){ }
public void SetDataSource(object dataSource,string displayMember,string valueMember){}
注意:
- 我们不能通过
clcComboBox.DataSource = xxx;
来设置数据源; - 我们可以通过
clcComboBox.DisplayMember = xxx;
和clcComboBox.ValueMember = xxx;
来设置显示成员和值成员;
2.3 不同下拉面板的数据源要求
2.3.1 ListDropDownItem
由于ListDropDownItem仅仅包含了CLCDataGridView,所以对于下拉内容为ListDropDownItem的CLCComboBox来说,数据源要求与CLCDataGridView的数据源要求相同。此处不再赘述。
2.3.2 CategoryDropDownItem
由于CategoryDropDownItem是用作分类显示的,所以合法的数据源有以下两类:
List<T>
:其中的T的某个属性标注了[Typed]特性;Dictionary<object,List<T>>
:object表示分类对象,List<T>表示该分类下的详细列表;
案例一:Lsit<Student>
List<Student> Students = new List<Student>()
{
new Student("张三", "男", 20, "中山大学", "广州", "18909880099"),
new Student("小红", "女", 21, "厦门大学", "厦门", "19093222099"),
new Student("肖华", "男", 20, "重庆大学", "重庆", "16743746759")
};
clcComboBox.SetDataSource(Students,"Name","Tag");
其中Student的Gender属性标注了[Typed]
特性,表示将该学生列表按照性别属性进行分类:
public class Student
{
...
[Typed]
[ColumnStyle(
HeaderText = "性别")]
public string Gender { get; set; }
...
}
案例二:Dictionary<Gender, List<Student>>
Dictionary<Gender, List<Student>> dictionary = new Dictionary<Gender, List<Student>>()
{
{
new Gender("01", "男"), new List<Student>()
{
new Student("张三", "男", 20, "中山大学", "广州", "18909880099"),
new Student("李四", "男", 18, "中山大学", "广州", "13509722122"),
}
},
{
new Gender("02", "女"), new List<Student>()
{
new Student("小花", "女", 19, "厦门大学", "厦门", "19988661232"),
new Student("小红", "女", 21, "厦门大学", "厦门", "19093222099")
}
}
};
clcComboBox.SetDataSource(dictionary,"Name","Tag");
以Gender对象为分类对象:
Gender对象作为分类对象,我们可以指定Gender的属性为DisplayMember
,只需要在对应的属性上标注[DisplayMember]
特性即可:
public class Gender
{
public string Code { get; set; }
[DisplayMember]
public string Value { get; set; }
public Gender(string code, string value)
{
Code = code;
Value = value;
}
}
案例三:List<Employee> employees,分类对象为自定义对象
Gender male = new Gender("01", "男");
Gender female = new Gender("02", "女");
List<Employee> employees = new List<Employee>()
{
new Employee() {Name = "员工1", Gender = male, Phone = "2891"},
new Employee() {Name = "员工2", Gender = female, Phone = "1891"},
new Employee() {Name = "员工3", Gender = female, Phone = "3891"},
new Employee() {Name = "员工4", Gender = male, Phone = "6891"},
};
clcComboBox2.SetDataSource(employees,"Name","Tag");
2.4 关于自定义属性
在CLCComboBox中,我们自定义了两个属性:
- EnableColumnSort:指示下拉内容中的CLCDataGridView中的行是否可以排序
- EnableResiveDropDownItem:指示下拉面板是否可以改变大小。如果为false,则下拉面板为默认大小;只有该项为true,DropDownHeight和DropDownWidth才起作用。不过不建议使用该项,否则会导致下拉面板中的控件布局不美观。
2.5 关于搜索、选择、获取选择项
当我们在搜索框输入搜索条件时,会实时在下面的列表中显示符合条件的实体,找到符合的实体后,可以点击确认关闭下拉面板,也可以双击选中的实体进行关闭面板:
当我们选择了目标内容后,可以通过CLCComboBotx
的SelectedValue
获取目标值。
3. 项目地址
github:https://github.com/Lee-0o0/CSharp-user-control