背景

  最近看了Winform在DataGrid中怎么嵌入Combo,想到平时项目中写到的一些临时小工具,经常用配置参数,有些配置是简单的地址或文本,有些则是类似1代表SQL,2代表Oracle等。于是想结合刚刚学的知识,做一个示例。

  关于参数的保存,想到用数据库,简单点就是用SQLite,可以无需安装数据库,后来想想,干脆用文件进行存储,这样简单,而且.net对Json的支持很好。

资源下载

  示例代码

  

  

实现

  1.设计数据结构。

    public class ListItem
{
public List<ListItem> lstItem = new List<ListItem>();
public string Text { get; set; }
public object Value { get; set; } public ListItem() { }
public ListItem(string pText, string pValue) { Text = pText; Value = pValue; } public override string ToString()
{
return Text;
}
}

Combo Item数据结构

    public class ConfigRow
{
public string Name { get; set; }//参数名称
public string Text { get; set; }//参数显示值
public string Value { get; set; }//参数真实值
public string Description { get; set; }//参数描述
public int Type { get; set; }//参数类别 1 常规字符 2 下拉框
public List<ListItem> lstItem { get; set; }//参数下拉框数据源列表
#region 数据源操作
//得到数据源组织的字符串
public string GetItemText()
{
var ret = new StringBuilder();
foreach (var li in lstItem)
{
ret.AppendLine(li.Value + "|" + li.Text);
}
return ret.ToString();
}
//根据格式化字符串设置数据源
public void SetItem(string text)
{
if (lstItem == null)
lstItem = new List<ListItem>();
else
lstItem.Clear();
var arr = text.Trim('\r').Split('\n');
foreach (var a in arr)
{
if (a.Length > )
{
var item = a.Split('|');
if (item.Length > )
{
ListItem li = new ListItem();
li.Value = item[];
li.Text = item[];
lstItem.Add(li);
}
}
}
}
//检查某项是否存在数据源中
public bool CheckItem(string text, string value)
{
var index = lstItem.FindIndex((item) =>
{
return item.Text == text && item.Value.ToString() == value;
});
return index > -;
}
#endregion
}

配置参数实体 数据结构

    public class MyConfig
{
#region 公共成员
public ThreadStart ComboChange { get; set; } //下拉框改变事件
public string Path = @"./1.json";//文件保存路径
private List<ConfigRow> lstRow { get; set; } //所有配置行
#endregion #region 私有成员
private ComboBox cbb;
private DataGridView dgv;
private MyConfig() { }
#endregion #region 控件设置与事件
private void Load()
{
#region DataGridView UI
var clName = new System.Windows.Forms.DataGridViewTextBoxColumn();
var clValue = new System.Windows.Forms.DataGridViewTextBoxColumn();
var clDesp = new System.Windows.Forms.DataGridViewTextBoxColumn();
var clType = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.dgv.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
clName,
clValue,
clDesp,
clType});
//
// clName
//
clName.DataPropertyName = "Name";
clName.HeaderText = "参数名称";
clName.Name = "clName";
clName.ReadOnly = true;
clName.Width = (int)Math.Floor(dgv.Width * 0.2);
//
// clValue
//
clValue.DataPropertyName = "Text";
clValue.HeaderText = "参数内容";
clValue.Name = "clValue";
clValue.Width = (int)Math.Floor(dgv.Width * 0.3);
//
// clDesp
//
clDesp.DataPropertyName = "Description";
clDesp.HeaderText = "描述";
clDesp.Name = "clDesp";
clDesp.ReadOnly = true;
clDesp.Width = (int)Math.Floor(dgv.Width * 0.5);
//
// clType
//
clType.DataPropertyName = "Type";
clType.HeaderText = "参数状态";
clType.Name = "clType";
clType.ReadOnly = true;
clType.Visible = false;
#endregion
//其他属性设置
cbb = new ComboBox();
cbb.Visible = false;
cbb.DropDownStyle = ComboBoxStyle.DropDownList;
dgv.AutoGenerateColumns = false;//不允许自动创建列
dgv.Controls.Add(cbb);
//绑定数据源
if (lstRow != null && lstRow.Count != ) dgv.DataSource = lstRow;
//注册控件事件
dgv.CurrentCellDirtyStateChanged += CurrentCellDirtyStateChanged;
dgv.CurrentCellChanged += CurrentCellChanged;
dgv.ColumnWidthChanged += ColumnWidthChanged;
cbb.SelectedIndexChanged += SelectedIndexChanged;
dgv.RowPostPaint += RowPostPaint;
}
private void CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var currentRow = dgv.CurrentRow.DataBoundItem as ConfigRow;
if (currentRow.Type != ) //下拉框类别单元格,在更改下拉框时已修改,此处忽略
{
if (dgv.CurrentCell.Value == null)
currentRow.Value = currentRow.Text = string.Empty;
else
currentRow.Value = currentRow.Text = dgv.CurrentCell.Value.ToString();
}
Update(currentRow);
}
private void CurrentCellChanged(object sender, EventArgs e)
{
if (dgv.CurrentCell == null) return;
//首先清空该事件 否则连续点击2个下拉框执行时有错误
if (ComboChange != null) ComboChange = null;
//若点击的是Value列,而且该参数类别为下拉框
if (dgv.CurrentCell.OwningColumn.Name == "clValue" && dgv.CurrentRow.Cells["clType"].Value.ToString() == "")
{
var current = dgv.CurrentRow.DataBoundItem as ConfigRow;
cbb.DataSource = current.lstItem;
cbb.Text = dgv.CurrentCell.Value.ToString();
//设置下拉框的位置
var rect = dgv.GetCellDisplayRectangle(dgv.CurrentCell.ColumnIndex, dgv.CurrentCell.RowIndex, false);
cbb.Visible = true;
cbb.Bounds = rect;
//注册下拉框事件
ComboChange = new ThreadStart(() =>
{
var li = cbb.SelectedItem as ListItem;
//若值发生变更,则更新DataGridView以及绑定的数据源
if (li.Text != dgv.CurrentCell.Value.ToString())
{
dgv.CurrentCell.Value = li.Text;
current.Text = li.Text;
current.Value = li.Value.ToString();
//这里是引用传递,修改后直接体现在DataBoundItem属性上 无需再次赋值
//dataGridView1.CurrentRow.DataBoundItem = current;
//下拉框直接赋值将不会触发CurrentCellDirtyStateChanged事件 直接更新
Update(current);
}
});
}
else
cbb.Visible = false;
}
private void ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
{
dgv.ClearSelection();
cbb.Visible = false;
}
private void RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
{
SolidBrush b = new SolidBrush(dgv.RowHeadersDefaultCellStyle.ForeColor);
e.Graphics.DrawString((e.RowIndex + ).ToString(System.Globalization.CultureInfo.CurrentUICulture),
dgv.DefaultCellStyle.Font, b, e.RowBounds.Location.X + , e.RowBounds.Location.Y + );
}
private void SelectedIndexChanged(object sender, EventArgs e)
{
if (ComboChange != null) ComboChange();
}
#endregion #region 加载文件
//根据路径加载配置文件 并显示在DataGridView控件上
public static MyConfig Load(DataGridView dataGridView1, string path)
{
MyConfig my = new MyConfig();
my.dgv = dataGridView1;
try
{
var json = File.ReadAllText(path);
my.lstRow = JsonConvert.DeserializeObject<List<ConfigRow>>(json);
}
catch
{
my.lstRow = new List<ConfigRow>();
}
my.Load();
return my;
}
//根据路径加载配置文件
public static MyConfig Load(string path)
{
MyConfig my = new MyConfig();
try
{
var json = File.ReadAllText(path);
my.lstRow = JsonConvert.DeserializeObject<List<ConfigRow>>(json);
}
catch
{
my.lstRow = new List<ConfigRow>();
}
return my;
}
//保存
private void Save()
{
if (dgv != null)
{
dgv.DataSource = null;
dgv.DataSource = lstRow;
}
var json = JsonConvert.SerializeObject(lstRow);
File.WriteAllText(Path, json);
}
#endregion #region 增删改查
//增加配置
public void Add(ConfigRow row)
{
var index = lstRow.FindIndex((config) => { return config.Name == row.Name; });
if (index == -) lstRow.Add(row); //找到则更新 未找到则添加
else lstRow[index] = row;
Save();
}
//删除配置
public bool Remove(string name)
{
var index = lstRow.FindIndex((config) => { return config.Name == name; });
if (index == -) return false;
lstRow.RemoveAt(index);
Save();
//删除最后一行时不会出发CurrentCellChanged事件,下拉框可能未被隐藏
if (lstRow.Count == && cbb != null && cbb.Visible) cbb.Visible = false;
return true;
}
//更新配置
public bool Update(ConfigRow row)
{
var index = lstRow.FindIndex((config) => { return config.Name == row.Name; });
if (index == -) return false;
else lstRow[index] = row;
Save();
return true;
}
//查询配置
public ConfigRow Find(string name)
{
return lstRow.Find((config) => { return config.Name == name; });
}
#endregion #region 读取某项的值,并指定默认值
//根据Name读取配置
public string GetString(string name, string defaultValue)
{
var currentRow = Find(name);
if (currentRow == null) return defaultValue;
return currentRow.Value;
}
//根据Name读取配置
public int GetInt(string name, int defaultValue)
{
var currentRow = lstRow.Find((config) => { return config.Name == name; });
if (currentRow == null) return defaultValue;
int value;
if (Int32.TryParse(currentRow.Value, out value))
return value;
return defaultValue;
}
#endregion //测试
public static MyConfig Create(DataGridView dgv)
{
var my = new MyConfig();
my.lstRow = new List<ConfigRow>();
//添加配置项 用户姓名
var row1 = new ConfigRow();
row1.Name = "UserName";
row1.Value = "admin";
row1.Text = "admin";
row1.Description = "配置用户的姓名";
row1.Type = ;//常规字符
my.lstRow.Add(row1); //添加配置项 地址
var row2 = new ConfigRow();
row2.Name = "UserAddress";
row2.Value = "湖南省长沙市";
row2.Text = "湖南省长沙市";
row2.Description = "配置用户的地址";
row2.Type = ;//常规字符
my.lstRow.Add(row2); //添加配置项 性别
var row3 = new ConfigRow();
row3.Name = "UserSex";
row3.Value = "";
row3.Text = "男";
row3.Description = "配置用户的性别";
row3.Type = ;//下拉数据源
//非常规字符必须添加数据源
var item = new ListItem();
item.Text = "男";
item.Value = "";
var item2 = new ListItem();
item2.Text = "女";
item2.Value = "";
row3.lstItem = new List<ListItem>() { item, item2 };
my.lstRow.Add(row3); //添加配置项 年龄
var row4 = new ConfigRow();
row4.Name = "UserAge";
row4.Value = "";
row4.Text = "15岁";
row4.Description = "配置用户的年龄";
row4.Type = ;//下拉数据源
//非常规字符必须添加数据源
row4.lstItem = new List<ListItem>();
for (var i = ; i < ; i++)
{
row4.lstItem.Add(new ListItem(i.ToString() + "岁", i.ToString()));
}
my.lstRow.Add(row4);
return my;
}
}

配置文件结构

  2.典型的窗体使用实例

        MyConfig my;
public frmMain()
{
InitializeComponent();
} private void Form3_Load(object sender, EventArgs e)
{
my = MyConfig.Load(dataGridView1, "./1.json"); ;
} private void btnAdd_Click(object sender, EventArgs e)
{
frmCreate fc = new frmCreate(my);
if (fc.ShowDialog() == DialogResult.OK)
{
my = fc.my;
}
} private void btnDel_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentCell != null)
{
var name = dataGridView1.CurrentRow.Cells["clName"].Value.ToString();
my.Remove(name);
}
} private void btnUpdate_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentCell != null)
{
var name = dataGridView1.CurrentRow.Cells["clName"].Value.ToString();
frmCreate fc = new frmCreate(my, name);
if (fc.ShowDialog() == DialogResult.OK)
{
my = fc.my;
}
}
}

使用实例

  

  

有些控件列的操作和一些必需的属性设置,把这些代码从Designer.cs中拷贝出来,直接放在方法里直接调用,是为了避免新建一个窗体,又有重复那么多设置操作。但这样肯定会带来一定的不灵活性。

  就这样做了2天,做完发现实用性有限,权当技术研究吧。

  另外还可以配置参数实体上添加正则属性,这样就方便进行正则检查参数是否合法。

Winform 在DataGrid中签入Combo的更多相关文章

  1. winform中DataGrid控件的宽度设置

    最近修改一个win5.0的PDA程式,碰到一个问题.就是给DataGrid控件绑定数据的时候,这个控件的宽度不能调整,有时候数据较长,就显示不全.然后想在程式里自定义它的宽度,设置不成功.然后网上没找 ...

  2. winform窗体间利用委托传值(一)

    前台:在winform窗体Form1放入pictureBox1 后台代码: namespace 点击小图变成大图 { public delegate void ClickDelegateHander( ...

  3. 【转】VS2010/MFC编程入门之二十五(常用控件:组合框控件Combo Box)

    原文网址:http://www.jizhuomi.com/software/189.html 上一节鸡啄米讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常 ...

  4. VS2010/MFC编程入门之二十五(常用控件:组合框控件Combo Box)

    上一节鸡啄米讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常见,例如,在Windows系统的控制面板上设置语言或位置时,有很多选项,用来进行选择的控件就是 ...

  5. 第一个C# Winform实例

    前面我们准备好了相关的库,现在开始搭建环境,本人自动化行业,就用Windorm开发吧,例子仅仅做引导,希望大家能深入.VS版本VS2017 1:打开VS建立一个WInform 项目.拉入两个控件,gr ...

  6. winform 自定义控件(高手)

    高手推荐:https://www.cnblogs.com/bfyx/p/11364884.html   c#Winform自定义控件-目录   前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件 ...

  7. VS2010-MFC(常用控件:组合框控件Combo Box)

    转自:http://www.jizhuomi.com/software/189.html 上一节讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常见,例如, ...

  8. WPF DataGrid 复合表头 (实现表头合并,自定义表头)

    功能说明: 将 DataGrid嵌套在本控件内,使用Label自定义表头,如果需要上下左右滚动 需要在控件外围添加  ScrollViewer 并且设置  ScrollVisibility 为Auto ...

  9. CYQ.Data V5 分布式缓存Redis应用开发及实现算法原理介绍

    前言: 自从CYQ.Data框架出了数据库读写分离.分布式缓存MemCache.自动缓存等大功能之后,就进入了频繁的细节打磨优化阶段. 从以下的更新列表就可以看出来了,3个月更新了100条次功能: 3 ...

随机推荐

  1. strak组件(5):为列表定制预留钩子方法

    效果图:  新增函数 def get_list_display(self): 获取页面上应该显示的列,预留的自定义扩展,例如:以后根据用户的不同显示不同的 一.stark组件 stark/servic ...

  2. linux最大进程数

    使用 ulimit -a 命令,查看 max user processes 的输出,就是系统最大进程数 core file size (blocks, -c) unlimited data seg s ...

  3. Linux两种方式rd.break和init重置root管理员密码

    centos7/rhel7进入单用户方式和重置密码方式发生了较大变化,GRUB由b引导变成了ctrl+x引导. 重置密码主要有rd.break和init两种方法. rd.break方法: 1.启动的时 ...

  4. POJ:2139-Six Degrees of Cowvin Bacon

    传送门:http://poj.org/problem?id=2139 Six Degrees of Cowvin Bacon Time Limit: 1000MS Memory Limit: 6553 ...

  5. 27-Middleware管道介绍

    1-Middleware管道介绍,. 如果匹配上/task,则界面只会显示i am task. public void Configure(IApplicationBuilder app, IHost ...

  6. Altium Designer多原理图、PCB更新处理

    问题扫述: 一般一个同程有多个原理图.PCB.但是AD默认从原理图更新到PCB会把全部原理图都更新过去.因此需要稍加设置. 一.

  7. SpringMVC---RequestMapping注解类的使用方法

    RequestMapping注解的使用 开发Controller控制器类,使用@Controller注解标注,并在配置文件中用<context:component-scan/>扫描相应的包 ...

  8. DNSSec

    Domain Name System Security Extensions (DNSSEC)DNS安全扩展,是由IETF提供的一系列DNS安全认证的机制(可参考RFC2535).它提供了一种来源鉴定 ...

  9. 用Fragment实现如新浪微博一样的底部菜单的切换

    像我这个有强迫症的人来说,自从TabActivity抛弃之后,再使用看到一个个警告和一条条划着的横线,心里很不舒服,现在终于下定决心用Fragment来替换掉TabActivity了!我的研究成果如下 ...

  10. windows版本cloudbase-init流程说明

    源码流程说明 - 程序首先判断操作系统类型,加载对应的模块 - 加载服务,服务共分为四种: 'cloudbaseinit.metadata.services.httpservice.HttpServi ...