Excel催化剂内部大量使用了DataGridView,这其中有一些小坑,花了力气才解决的,在此给广大开发者作简单分享。
为何要使用DataGridView而不是其他控件如ListBox、ListView、ComboBox之类的?因为大量的数据,特别是配置信息,都是以数据库表结构的一维表存储最为合理,一般一个配置是多列内容来定义其多样的属性。
用DataGridView最有优势之处在于,可以直接让DataTable直接绑定即可,同时带有丰富的事件可与用户交互,并且保留有用户常用的排序功能,筛选功能也容易实现,用Dataview来绑定数据源即可。
在Excel催化剂的【工作表导航】功能中,有用到DataGridView存储工作表信息,需要和用户交互的是用户点击复选框,可以对工作表的显示隐藏状态进行改变。
image.png
若直接在Checkbox列上写CellValueChanged事件,会发现不起作用的。当然这一步也不可少。
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (Common.ExcelApp.ActiveWorkbook.ProtectStructure == false)
{
if (e.RowIndex != -1 && !dataGridView1.Rows[e.RowIndex].IsNewRow)
{
//复选框列
if (e.ColumnIndex == 1)
{
string worksheetName = this.dataGridView1[0, e.RowIndex].Value.ToString();
Excel._Worksheet wht = Common.ExcelApp.ActiveWorkbook.Worksheets[worksheetName];
try
{
if ((bool)this.dataGridView1[e.ColumnIndex, e.RowIndex].Value == true)
{
wht.Visible = Excel.XlSheetVisibility.xlSheetVisible;
}
else
{
wht.Visible = Excel.XlSheetVisibility.xlSheetHidden;
}
}
catch (Exception)
{
MessageBox.Show($"操作有误,错误原因为:\r\n工作薄至少需要一个工作表为可见的!");
this.dataGridView1[e.ColumnIndex, e.RowIndex].Value = true;
}
}
}
}
else
{
MessageBox.Show("工作薄受保护状态不可操作工作表显示/隐藏操作!");
this.dataGridView1.CellValueChanged -= dataGridView1_CellValueChanged;
this.dataGridView1[e.ColumnIndex, e.RowIndex].Value = true;
this.dataGridView1.CellValueChanged += dataGridView1_CellValueChanged;
}
}
需要加上这个事件才可生效,具体原理可自行百度
/// <summary>
/// 这个为了让复选框可以单击时产生变化而用的。网上抄的。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dataGridView1.IsCurrentCellDirty)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
DataGridView原生功能没有实现通过按住某行拖动的方式实现不同行之间的排序问题。在百度上搜索了一些代码实现了此功能。
在Excel催化剂上的使用场景为,对工作表的手工排序操作。
手工排序效果
首先要打开这个AllowDrop属性
AllowDrop打开
具体代码段
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
namespace Excel催化剂
{
public partial class formSheetSort : Form
{
public formSheetSort()
{
InitializeComponent();
this.Icon = Properties.Resources.Excel催化剂logo32;
}
private void formSheetSort_Load(object sender, EventArgs e)
{
ReloadSheetName();
}
private void btnEnsureOrderSheetName_Click(object sender, EventArgs e)
{
try
{
Common.ExcelApp.ScreenUpdating = false;
int shtCount = Common.ExcelApp.ActiveWorkbook.Worksheets.Count;
foreach (DataGridViewRow row in this.dgv.Rows)
{
Excel.Worksheet sht = Common.ExcelApp.ActiveWorkbook.Worksheets[row.Cells[0].Value];
sht.Move(After: Common.ExcelApp.ActiveWorkbook.Worksheets[shtCount]);
}
}
catch (Exception ex )
{
MessageBox.Show("操作出错,出错原因为" + ex.Message);
}
finally
{
Common.ExcelApp.ScreenUpdating = true;
}
}
private void ReloadSheetName()
{
foreach (Excel.Worksheet item in Common.ExcelApp.ActiveWorkbook.Worksheets)
{
DataGridViewRow row = new DataGridViewRow();
int sheetNameIndex = this.SheetName.Index;
int rowIndex = this.dgv.Rows.Add();
this.dgv.Rows[rowIndex].Cells[sheetNameIndex].Value = item.Name;
}
}
private int selectionIdx = 0;
private void dgv_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex >= 0)
selectionIdx = e.RowIndex;
}
private void dgv_CellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if ((e.Clicks < 2) && (e.Button == MouseButtons.Left))
{
if ((e.ColumnIndex == -1) && (e.RowIndex > -1))
dgv.DoDragDrop(dgv.Rows[e.RowIndex], DragDropEffects.Move);
}
}
private void dgv_DragDrop(object sender, DragEventArgs e)
{
DataGridView dgv = sender as DataGridView;
int idx = GetRowFromPoint(e.X, e.Y);
if (idx < 0) return;
if (e.Data.GetDataPresent(typeof(DataGridViewRow)))
{
DataGridViewRow row = (DataGridViewRow)e.Data.GetData(typeof(DataGridViewRow));
int rowIndex = row.Index;
if (idx < rowIndex)
{
dgv.Rows.Remove(row);
selectionIdx = idx;
dgv.Rows.Insert(idx, row);
}
else
{
dgv.Rows.Remove(row);
selectionIdx = idx - 1;
dgv.Rows.Insert(idx - 1, row);
}
}
}
private void dgv_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
{
DataGridView dgv = sender as DataGridView;
if (selectionIdx > -1 && dgv.Rows.Count > 0)
{
dgv.Rows[selectionIdx].Selected = true;
dgv.CurrentCell = dgv.Rows[selectionIdx].Cells[0];
}
}
private void dgv_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private int GetRowFromPoint(int x, int y)
{
for (int i = 0; i < this.dgv.RowCount; i++)
{
Rectangle rec = this.dgv.GetRowDisplayRectangle(i, false);
if (this.dgv.RectangleToScreen(rec).Contains(x, y))
return i;
}
return -1;
}
}
}
在.Net下写VSTO插件,没有理由再用VBA那些落后的控件,DataGridView、Ado.Net这些在.Net环境里的数据控件和数据存储技术是首选,代码书写更流畅,开发效率更高,用户体验也是超棒。
此篇给大家扫清几个小坑,让大家开发过程中,更顺畅,尽情地在业务逻辑代码上发挥,少关注底层这些莫名的坑坑洼洼。