编程与数学 03-007 《看潮资源管理器》项目开发 07 主窗口设计(3-3)
编程与数学 03-007 《看潮资源管理器》项目开发 07 主窗口设计(3-3)
- 六、数据呈现:使用DataGridView
- (一)需求描述
- (二)初始风格控件
- (三)网格视图
- (四)加载数据
- (五)表格切换
- 七、状态栏与屏幕参数
- (一)需求描述
- (二)属性及成员设置
- (三)事件及功能代码
- 全文总结
摘要:本文完整实现《看潮资源管理器》主窗口,包括左右分区、动态菜单、四表切换、快捷键搜索、状态栏、窗体记忆等功能,提供可直接编译的C#与.NET8 WinForm代码,覆盖初始化、数据绑定、事件驱动与配置保存全流程,可作为资源管理类系统界面框架参考。
关键词:WinForm、主窗口布局、FlowLayoutPanel、DataGridView、动态菜单、快捷键、搜索过滤、状态栏、配置保存、资源管理器
六、数据呈现:使用DataGridView
(一)需求描述
在主窗口中,使用DataGridView来展示和操作数据。这在设计器就可以完成。
(二)初始风格控件
这里使用了四个控件:gridSys, gridDisk, gridDir, gridFile。
private void InitializeDataGridView(){List<DataGridView> dvList = new List<DataGridView>();dvList.AddRange([gridSys, gridDisk, gridDir, gridFile]);foreach (DataGridView dgv in dvList){// DataGridView配置dgv.AutoGenerateColumns = false;dgv.AllowUserToAddRows = false;dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;dgv.Font = new Font("微软雅黑", 12, FontStyle.Regular);dgv.ColumnHeadersDefaultCellStyle.Font = new Font("微软雅黑", 14, FontStyle.Bold);dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;dgv.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.None;dgv.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2; // 按键或F2进入编辑dgv.RowsDefaultCellStyle.BackColor = Color.LightGreen; // 设置奇偶行不同的背景颜色dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.LightGray;}}
(三)网格视图
/// <summary>
/// 根据数据字典动态构造 DataGridView 的列,并设置样式与行为。
/// 仅生成列结构,不加载实际数据。
/// </summary>
/// <param name="gridData">待初始化的 DataGridView</param>
/// <param name="cuTable">表名(也是字典主键)</param>
private void loadDv(ref DataGridView gridData, string cuTable)
{string sqlCollumn = ""; // 最终 SELECT 子句的列名列表gridData.DataSource = null; // 先清空数据源gridData.Columns.Clear(); // 再清空旧列/* ---------- 1. 从字典表读取当前表的列定义 ---------- */string sqlDict ="SELECT [Number],[ColumnName],[DataType],[Length],[Prec],[Scale],[InputType]," +"[Width],[Description],[Alignment],[Hide],[Sortable],[Editable],[EditControl]," +"[FormatString],[ToolTip] " +"FROM [dbo].[fm_Dict] " +"WHERE [TableName]=@tablename " +"ORDER BY [Number]";DataTable dtDict = _dbHelper.GetTable(cuTable, sqlDict,new SqlParameter("@tablename", cuTable));/* ---------- 2. 按字典定义逐列构造 ---------- */foreach (DataRow dr in dtDict.Rows){#region 读取字典当前行各字段string columnName = dr["ColumnName"].ToString(); // 数据库列名string inputtype = dr["InputType"].ToString(); // 输入类型(预留)int columnWidth = (int)dr["Width"]; // 显示宽度string description = dr["Description"].ToString(); // 表头文字int alignment = (int)dr["Alignment"]; // 0左 1中 2右bool hide = (bool)dr["Hide"]; // 是否隐藏bool sortable = (bool)dr["Sortable"]; // 是否允许点击排序bool editable = (bool)dr["Editable"]; // 是否可编辑string formatString = dr["FormatString"].ToString(); // 格式化字符串string tooltip = dr["ToolTip"].ToString(); // 列提示string editcontrol = dr["EditControl"].ToString(); // 编辑控件类型// 容错:alignment 只能 0/1/2if (alignment < 0 || alignment > 2) alignment = 0;#endregion#region 构造 SELECT 子句(用于后续加载数据)sqlCollumn += string.IsNullOrEmpty(sqlCollumn)? $"[{columnName}]": $",[{columnName}]";#endregion#region 根据 EditControl 决定列类型if (editcontrol.Equals("CheckBox", StringComparison.OrdinalIgnoreCase)){/* ---- 复选框列 ---- */DataGridViewCheckBoxColumn chkCol = new DataGridViewCheckBoxColumn{Name = columnName,HeaderText = description,DataPropertyName = columnName,ThreeState = false,Width = columnWidth,Visible = !hide,ReadOnly = !editable,SortMode = DataGridViewColumnSortMode.NotSortable,DefaultCellStyle = new DataGridViewCellStyle{ForeColor = Color.Blue,Alignment = DataGridViewContentAlignment.MiddleCenter,Format = formatString}};gridData.Columns.Add(chkCol);}else{/* ---- 文本列 ---- */// 通过 2^alignment*16 把 0/1/2 转成 Left/Center/RightDataGridViewContentAlignment align =(DataGridViewContentAlignment)(Math.Pow(2, alignment) * 16);DataGridViewTextBoxColumn txtCol = new DataGridViewTextBoxColumn{Name = columnName,HeaderText = description,DataPropertyName = columnName,Width = columnWidth,Visible = !hide,ReadOnly = !editable,SortMode = sortable ? DataGridViewColumnSortMode.Automatic: DataGridViewColumnSortMode.NotSortable,DefaultCellStyle = new DataGridViewCellStyle{ForeColor = Color.Blue,Alignment = align,Format = formatString}};gridData.Columns.Add(txtCol);}#endregion/* ---------- 3. 统一设置网格外观 ---------- */gridData.ColumnHeadersHeight = 45; // 表头高gridData.RowHeadersWidth = 20; // 行头宽gridData.RowTemplate.Height = 40; // 行高}
}
(四)加载数据
/// <summary>
/// 根据布尔开关,按需重新加载当前所有者的四张核心数据表,
/// 并自动将最新行数写入状态栏 ts5。
/// </summary>
/// <param name="disk">true 重新加载 fm_DriveInfo</param>
/// <param name="dir"> true 重新加载 fm_DirectoryInfo</param>
/// <param name="file">true 重新加载 fm_FileInfo</param>
/// <param name="owner">true 重新加载 fm_Owner</param>
private void loadDt(bool disk, bool dir, bool file, bool owner)
{try{/* ---------- 1. 所有者表 ---------- */if (owner){// 从数据库拉取当前所有者的系统信息dtOwner = _dbHelper.GetOwnerData(cuOwner, "fm_Owner");gridSys.DataSource = dtOwner; // 直接绑定}/* ---------- 2. 磁盘(存储器)表 ---------- */if (disk){dtDisk = _dbHelper.GetOwnerData(cuOwner, "fm_DriveInfo");gridDisk.DataSource = dtDisk;}/* ---------- 3. 文件夹表 ---------- */if (dir){dtDir = _dbHelper.GetOwnerData(cuOwner, "fm_DirectoryInfo");bdsDir.DataSource = dtDir; // 先绑定到中间 BindingSourcegridDir.DataSource = bdsDir; // 再绑定到网格,便于后续排序/过滤}/* ---------- 4. 文件表 ---------- */if (file){dtFile = _dbHelper.GetOwnerData(cuOwner, "fm_FileInfo");bdsFile.DataSource = dtFile;gridFile.DataSource = bdsFile;}/* ---------- 5. 状态栏行数刷新 ---------- */switch (currentProject) // 0-磁盘 1-文件夹 2-文件 3-系统{case 0: ts5.Text = gridDisk.RowCount.ToString(); break;case 1: ts5.Text = gridDir.RowCount.ToString(); break;case 2: ts5.Text = gridFile.RowCount.ToString(); break;case 3: ts5.Text = gridSys.RowCount.ToString(); break;}}catch (Exception ex){MessageBox.Show("加载所有者数据时:\r\n" + ex.Message,"错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);}
}
(五)表格切换
根据选项按钮,切换到不同的表格。
// 单选框选项变更事件处理:切换到“存储器”视图
private void radioDisk_CheckedChanged(object sender, EventArgs e)
{if (comboOwner.Items.Count <= 0){MessageBox.Show("尚未建立所有者数据,使用所有者维护功能完成设置", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);radioSys.Select(); // 若没有所有者数据,则切换回系统视图return;}if (radioDisk.Checked){ts2.Text = " 存储器 "; // 更新状态栏文本ts3.Text = "使用存储器菜单,完成文件夹或文件名称整理、扫描存储器、数据导入导出等操作";lastProject = currentProject; // 保存当前项目currentProject = 0; // 设置当前项目为存储器(索引0)recurrentProject(); // 刷新当前项目视图}else return;
}
// 单选框选项变更事件处理:切换到“系统”视图
private void radioSys_CheckedChanged(object sender, EventArgs e)
{if (radioSys.Checked){ts2.Text = " 系统 ";ts3.Text = "使用系统菜单,操作所有者、用户、数据词典等数据";lastProject = currentProject;currentProject = 3; // 设置当前项目为系统(索引3)recurrentProject();}else return;
}
// 单选框选项变更事件处理:切换到“文件”视图
private void radioFile_CheckedChanged(object sender, EventArgs e)
{if (radioFile.Checked){ts2.Text = " 当前管理对象:文件 ";ts3.Text = "Ctrl+N:取消过滤 Ctrl+i:过滤到本级 Ctrl+u:过滤到上级 Ctrl+T:条件过滤 Ctrl+L:过滤选定 双击行:转到Windows资源管理器";lastProject = currentProject;currentProject = 2; // 设置当前项目为文件(索引2)recurrentProject();}else return;
}
// 单选框选项变更事件处理:切换到“文件夹”视图
private void radioDir_CheckedChanged(object sender, EventArgs e)
{if (radioDir.Checked){ts2.Text = " 文件夹 ";ts3.Text = "Ctrl+N:取消过滤 Ctrl+1-6 级次过滤 Ctrl+i:过滤到本级 Ctrl+u:过滤到上级 Ctrl+T:条件过滤 Ctrl+L:过滤选定 双击行:转到Windows资源管理器";lastProject = currentProject;currentProject = 1; // 设置当前项目为文件夹(索引1)recurrentProject();}else return;
}
七、状态栏与屏幕参数
(一)需求描述
需要在窗口底部显示一个状态栏,以显示当前程序运行及操作状态。
(二)属性及成员设置
//
// statusStripfm
//
statusStripfm.Font = new Font("微软雅黑", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
statusStripfm.ImageScalingSize = new Size(20, 20);
statusStripfm.Items.AddRange(new ToolStripItem[] { ts1, ts2, ts3, ts5, tsR });
statusStripfm.Location = new Point(0, 594);
statusStripfm.Name = "statusStripfm";
statusStripfm.Padding = new Padding(2, 0, 20, 0);
statusStripfm.Size = new Size(1784, 27);
statusStripfm.TabIndex = 13;
statusStripfm.Text = "ss1";
//
// ts1
//
ts1.Margin = new Padding(3);
ts1.Name = "ts1";
ts1.Size = new Size(90, 21);
ts1.Text = "当前所有者";
ts1.TextAlign = ContentAlignment.MiddleLeft;
//
// ts2
//
ts2.Margin = new Padding(3);
ts2.Name = "ts2";
ts2.Size = new Size(74, 21);
ts2.Text = "当前类别";
//
// ts3
//
ts3.Margin = new Padding(3);
ts3.Name = "ts3";
ts3.Size = new Size(1231, 21);
ts3.Spring = true;
ts3.Text = "记录数";
ts3.TextAlign = ContentAlignment.MiddleLeft;
//
// ts5
//
ts5.Margin = new Padding(3);
ts5.Name = "ts5";
ts5.Size = new Size(74, 21);
ts5.Text = "当前操作";
//
// tsR
//
tsR.Margin = new Padding(3);
tsR.Name = "tsR";
tsR.Size = new Size(263, 21);
tsR.Text = " 看潮资源管理器 作者:明月看潮生 ";
tsR.TextAlign = ContentAlignment.MiddleRight;
//以上代码来自设计器文件
(三)事件及功能代码
/// <summary>
/// 状态栏与屏幕参数
/// </summary>private bool recurrentProject()
{// 根据上一个项目类型保存对应的DataGridView设置switch (lastProject){case 0:SaveFormSettingsdgv(ref gridDisk);break;case 1:SaveFormSettingsdgv(ref gridDir);break;case 2:SaveFormSettingsdgv(ref gridFile);break;case 3:SaveFormSettingsdgv(ref gridSys);break;}// 根据当前项目类型显示对应的菜单和DataGridViewsysMenu.Visible = (currentProject == 3);gridSys.Visible = (currentProject == 3);fileMenu.Visible = (currentProject == 2);gridFile.Visible = (currentProject == 2);dirMenu.Visible = (currentProject == 1);gridDir.Visible = (currentProject == 1);diskMenu.Visible = (currentProject == 0);gridDisk.Visible = (currentProject == 0);// 根据当前项目类型更新状态栏显示的行数switch (currentProject){case 0:ts5.Text = gridDisk.RowCount.ToString();break;case 1:ts5.Text = gridDir.RowCount.ToString();break;case 2:ts5.Text = gridFile.RowCount.ToString();break;case 3:ts5.Text = gridSys.RowCount.ToString();break;}return true;
}
// 从文件加载保存设置的状态
private void LoadSavedSeting()
{saveSeting = true; // 默认启用设置保存if (File.Exists(CredentialsFilePath)){try{string[] lines = File.ReadAllLines(CredentialsFilePath);saveSeting = bool.Parse(lines[3]); // 从文件第四行读取设置状态}catch{}}
}
// 保存窗体位置、大小和状态
public void SaveFormSettingsfm()
{try{if (saveSeting != true) return; // 检查是否启用设置保存if (fmactivated != true) return; // 检查窗体是否已激活// 保存窗体位置和大小、状态。string fmset = $"{(int)this.WindowState}," +$"{this.Top},{this.Left},{this.Width},{this.Height}";_myScreen.FmSave(this.Name, fmset,"WindowState,Top,Left,Width,Height");}catch (Exception ex){MessageBox.Show("保存窗口参数时出错:\r\n" + ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);}
}
// 保存DataGridView的列宽、行高等设置
public void SaveFormSettingsdgv(ref DataGridView dgv)
{try{if (saveSeting != true) return; // 检查是否启用设置保存if (fmactivated != true) return; // 检查窗体是否已激活if (dgv.Rows.Count <= 0) return; // 检查是否有行数据string rowheight = dgv.Rows[0].Height.ToString().Trim();// 保存表格标头高度、行标宽度、行高。string fmset = $"{dgv.ColumnHeadersHeight},{dgv.RowHeadersWidth},{rowheight}";_myScreen.FmSave(this.Name + "_GridHeaders_" + dgv.Name, fmset,"ColumnHeadersHeight,RowHeadersWidth,RowHeight");// 如果 DataGridView 不为空且有行if (dgv != null && dgv.RowCount > 0){// 保存 DataGridView 列宽string gdset = dgv.Columns[0].Width.ToString();string gdmemo = dgv.Columns[0].HeaderText;for (int g = 1; g < dgv.Columns.Count; g++){int cw = -1;if (dgv.Columns[g].Visible){cw = dgv.Columns[g].Width;}gdset += "," + cw.ToString();gdmemo += "," + dgv.Columns[g].HeaderText;}_myScreen.FmSave(this.Name + "_" + "GridColumnsWidth_" + dgv.Name, gdset, gdmemo);}}catch (Exception ex){MessageBox.Show("保存表格参数时出错:\r\n" + ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);}
}
// 加载窗体位置、大小和状态设置
public void LoadFormSettingsfm()
{try{string fmset = _myScreen.FmRead(this.Name);string[] fmay = fmset.Split(',');short ws = 0;if (fmay.Length != 5) return; // 检查参数完整性ws = short.Parse(fmay[0]);if (ws == 2) this.WindowState = FormWindowState.Maximized; // 最大化状态if (ws == 0) // 正常状态{if (int.Parse(fmay[1]) > 0) this.Top = int.Parse(fmay[1]);if (int.Parse(fmay[2]) > 0) this.Left = int.Parse(fmay[2]);if (int.Parse(fmay[3]) > 50) this.Width = int.Parse(fmay[3]);if (int.Parse(fmay[4]) > 50) this.Height = int.Parse(fmay[4]);}}catch (Exception ex){MessageBox.Show("加载窗口参数时出错:\r\n" + ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);}
}
// 加载DataGridView的列宽、行高等设置
public void LoadFormSettingsdgv(ref DataGridView dgv)
{try{string fmset = _myScreen.FmRead(this.Name + "_GridHeaders_" + dgv.Name);string[] fmay = fmset.Split(',');if (fmay.Length != 3) return; // 检查参数完整性if (int.Parse(fmay[0]) > 10) dgv.ColumnHeadersHeight = int.Parse(fmay[0]);if (int.Parse(fmay[1]) > 10) dgv.RowHeadersWidth = int.Parse(fmay[1]);if (int.Parse(fmay[2]) > 10) dgv.RowTemplate.Height = int.Parse(fmay[2]);fmset = _myScreen.FmRead(this.Name + "_" + "GridColumnsWidth_" + dgv.Name);fmay = fmset.Split(',');if (dgv != null){if (fmay.Length == dgv.Columns.Count) // 检查列数匹配{for (int c = 0; c < dgv.Columns.Count; c++){int cw = int.Parse(fmay[c]);if (cw <= 0) // 宽度为负表示隐藏列{dgv.Columns[c].Visible = false;}else{dgv.Columns[c].Width = cw;}}}}}catch (Exception ex){MessageBox.Show("加载表格参数时出错:\r\n" + ex.Message, "错误信息", MessageBoxButtons.OK, MessageBoxIcon.Error);}
}
// 窗体关闭时的处理事件
protected override void OnFormClosing(FormClosingEventArgs e)
{if (saveSeting == true) // 如果启用设置保存{// 根据当前项目类型保存对应的DataGridView设置switch (currentProject){case 0:SaveFormSettingsdgv(ref gridDisk);break;case 1:SaveFormSettingsdgv(ref gridDir);break;case 2:SaveFormSettingsdgv(ref gridFile);break;case 3:SaveFormSettingsdgv(ref gridSys);break;}SaveFormSettingsfm(); // 保存窗体设置_myScreen.FmWrite("FormMain"); // 写入设置到文件}// 停止并释放文件监视器insertWatcher?.Stop();removeWatcher?.Stop();insertWatcher?.Dispose();removeWatcher?.Dispose();base.OnFormClosing(e); // 调用基类方法
}
全文总结
本文围绕《看潮资源管理器》主窗口的实战需求,从零开始给出了一套可直接落地的C# WinForm方案。首先通过Anchor与Dock组合完成左右分栏,左侧FlowLayoutPanel嵌入四组GroupBox按钮菜单,右侧动态切换gridSys/gridDisk/gridDir/gridFile四张DataGridView,实现“系统-存储器-文件夹-文件”四级数据的无缝跳转。菜单采用CreateGroupBox工厂方法批量生成,文本、提示、事件一站式绑定,配合RunMenu路由函数,将三十余项业务功能集中调度。数据层面,先以fm_Dict字典表驱动列自动生成,再借BindingSource实现排序、过滤与编辑;状态栏实时反馈当前所有者、类别、记录数与操作提示;搜索框支持Enter逐条定位,Ctrl组合键提供全选、清除、多级目录筛选、条件过滤等快捷操作。窗体与网格的宽高、列宽、行高等视觉参数全部序列化到本地,启动自动还原。USB插拔监视、所有者下拉切换、异步扫描与查重算法均被封装为独立模块,与界面线程解耦。整套代码遵循“设计器+分部类+业务扩展”的三层结构,逻辑与UI分离,既保证Visual Studio可视化编辑,又支持后续功能横向扩展。开发者可直接复制BuildMenu、loadDv、FormMain_KeyDown等核心方法,快速搭建自己的资源管理、资产盘点或数据审计系统,并借字典表机制实现字段、宽度、对齐、编辑控件等细节的可配置化,显著降低后期维护成本。