C#— —[winforms]自定义DataGridView,实现列顺序、宽度保存

  • Post author:
  • Post category:其他

本文主要介绍如何在DataGridView的基础上,自定义DataGridView,实现列的顺序、宽度、显示名等信息的保存与读取,实现用户的个性化需求。

1. 需求说明

虽说在文章简介中大致说明了要实现的功能是什么,但是这里还是举个例子说明一下:

假设现有一张学生表,表头如下:

image-20210803203909857

每个用户对于每张表的关注点不同,假设我比较关注学校和年级这些信息,那么我想要把学校和年级放在姓名的前面(如果一些表很复杂,列很多,学校和年级导致在后面,需要拉动滑动条才能看见,这是很麻烦的):

image-20210803203942513

如果我退出系统后,下一次再打开系统,发现学校和年级又跑到后面去了,不得不再次拖动相关的列,这对于用户来说是一件很麻烦的事。

所以,我们需要保存DataGridView中的列信息,使得用户下次打开系统时,列的顺序仍然和上次退出时的一样:

image-20210803203944796

这就是本文要介绍的内容。

2. 实现

主要新建一个组件类,然后将父类Component改为DataGridViewMyDataGridView.cs部分代码如下:

public partial class MyDataGridView : DataGridView
{
    private DataTable dataTable; //列样式数据表
    private string path; //XML文件路径

    public MyDataGridView()
    {
        InitializeComponent();
    }

    public MyDataGridView(IContainer container)
    {
        container.Add(this);

        InitializeComponent();
    }

    public void InitAfterConstructor()
    { 
        path = Application.StartupPath + @"\User\" 
            + this.Name + ".xml";

        //列样式数据表
        dataTable = new DataTable();
        dataTable.TableName = this.Name; //表名

        dataTable.Columns.Add("Name"); // 列名
        dataTable.Columns.Add("HeaderText"); // 显示的名字
        dataTable.Columns.Add("DisplayIndex"); // 显示顺序
        dataTable.Columns.Add("Width"); // 列宽度
        dataTable.Columns.Add("DataPropertyName"); // 绑定的数据项名
        dataTable.Columns.Add("Visible"); // 是否可见

        // 加载列样式
        BindColumnStyle();
    }

    /// <summary>
    /// 绑定列样式
    /// </summary>
    private void BindColumnStyle()
    {
        try
        {
            //如果不存在则保存列样式
            if (!File.Exists(path))
            {
                SaveColumnStyle();
                return;
            }

            //加载列样式
            dataTable.ReadXml(path);

            foreach (DataRow row in dataTable.Rows)
            {
                if (Columns.Contains(row["Name"].ToString().Trim()))
                {
                    Columns[row["Name"].ToString()].HeaderText = row["HeaderText"].ToString();
                    Columns[row["Name"].ToString()].DisplayIndex =
                        int.Parse(row["DisplayIndex"].ToString());
                    Columns[row["Name"].ToString()].Width = int.Parse(row["Width"].ToString());
                    Columns[row["Name"].ToString()].DataPropertyName =
                        row["DataPropertyName"].ToString();
                    Columns[row["Name"].ToString()].Visible = bool.Parse(row["Visible"].ToString());
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 保存列样式
    /// </summary>
    private void SaveColumnStyle()
    {
        try
        {
            //如果目录不存在则创建
            var dir = path.Substring(0, path.LastIndexOf('\\'));
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

            //读取列样式
            dataTable.Rows.Clear();
            foreach (DataGridViewColumn column in this.Columns)
            {
                var newRow = dataTable.NewRow();

                newRow["Name"] = column.Name;
                newRow["HeaderText"] = column.HeaderText;
                newRow["DisplayIndex"] = column.DisplayIndex;
                newRow["Width"] = column.Width;
                newRow["DataPropertyName"] = column.DataPropertyName;
                newRow["Visible"] = column.Visible;

                dataTable.Rows.Add(newRow);
            }
            // 保存列样式到XML文件中
            dataTable.WriteXml(path);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

MyDataGridView.designer.cs部分的代码如下,最主要的是在Dispose()方法中添加保存列样式的方法,注意顺序:

partial class MyDataGridView
{
    ......

    /// <summary> 
    /// 清理所有正在使用的资源。
    /// </summary>
    /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
    protected override void Dispose(bool disposing)
    {
        // 关闭保存列样式,注意要先保存列样式,再调用父类的Dispose()
        SaveColumnStyle();

        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    ......
}

3. 使用方式

当编写好自定义的DataGridView后,编译一下项目,这样我们就可以在工具箱中看到自定义的控件了。

然后新建一个界面Form,将自定义的MyDataGridView拖进界面,并设置启用列重新排序,然后添加三列:

image-20210803205420214

在Form的构造方法中添加MyDataGridView的初始化方法(注意:该步骤需要手动添加!):

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        myDataGridView1.InitAfterConstructor();
    }
}

启动项目,改变列的排序,页面如下:

image-20210803211647334

然后关闭页面,再次启动项目,列顺序如之前的一样。

在项目的xxx\bin\Debug\User下,有XML文件记录着列的排序、宽度等信息:

<?xml version="1.0" standalone="yes"?>
<DocumentElement>
    <myDataGridView1>
        <Name>Column1</Name>
        <HeaderText>Column1</HeaderText>
        <DisplayIndex>2</DisplayIndex>
        <Width>72</Width>
        <DataPropertyName />
        <Visible>True</Visible>
    </myDataGridView1>
    <myDataGridView1>
        <Name>Column2</Name>
        <HeaderText>Column2</HeaderText>
        <DisplayIndex>0</DisplayIndex>
        <Width>72</Width>
        <DataPropertyName />
        <Visible>True</Visible>
    </myDataGridView1>
    <myDataGridView1>
        <Name>Column3</Name>
        <HeaderText>Column3</HeaderText>
        <DisplayIndex>1</DisplayIndex>
        <Width>72</Width>
        <DataPropertyName />
        <Visible>True</Visible>
    </myDataGridView1>
</DocumentElement>

整个自定义的MyDataGridView逻辑如下:

  • 在父页面(Form)加载完控件后,调用InitAfterConstructor()方法,加载之前保存的列顺序、宽度等信息;
  • 在页面关闭时,保存列顺序、宽度等信息;

参考资料

[1] https://www.cnblogs.com/atomy/p/11852072.html


版权声明:本文为qq_41261251原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。