基于DevExpress的GridControl实现的一些界面处理功能

基于,devexpress,gridcontrol,实现,一些,界面,处理,功能 · 浏览次数 : 1010

小编点评

**生成内容方法** * 创建一个名为 `ItemName` 的列。 * 创建一个名为 `Quantity` 的列。 * 创建一个名为 `ItemBigType` 的列。 * 创建一个名为 `ItemType` 的列。 * 创建一个名为 `MapNo` 的列。 * 创建一个名为 `Specification` 的列。 * 创建一个名为 `Unit` 的列。 * 创建一个名为 `Price` 的列。 * 创建一个名为 `Amount` 的列。 * 创建一个名为 `Material` 的列。 * 创建一个名为 `Source` 的列。 * 创建一个名为 `StoragePos` 的列。 * 创建一个名为 `UsagePos` 的列。 * 创建一个名为 `WareHouse` 的列。 * 创建一个名为 `Dept` 的列。 * 设置部分字段不可修改。 *绑定数据源。 *设置默认值。 *处理事件。

正文

DevExpress的GridControl控件能够提供很多强大的操作,其视图GridView能够通过各种设置,呈现出多种复杂的界面效果,本篇随笔探讨一些常见的GridControl控件及其GridView的视图操作处理,以便在大家也需要的时候作为一个参考的代码。其中设计一些常见的操作,如合并单元格、汇总统计、复制粘贴行、导入数据处理、导出Excel、导出PDF等、打印GridView视图、内置插入及保存数据处理等等。

1、合并单元格

 有时候,需要把多行紧挨着的单元格内容进行合并展示,这样有助于界面的美观及查看便利,如下界面所示。

 或者这样的界面,都是类似的效果。

合并的处理比较简单,只需要设置允许合并,以及实现合并的处理事件代码即可。 

    //允许合并
    this.gridView1.OptionsView.AllowCellMerge = true;

然后实现的事件代码如下所示。

        private List<string> mergeFields = new List<string> { "订单号", "客户订单号", "客户编码", "客户名称", "组合编码", "组合名称"};
        private void GridView1_CellMerge(object sender, CellMergeEventArgs e)
        {
            var fieldName = e.Column.FieldName;
            if(mergeFields.Contains(fieldName))
            {
                var view = sender as GridView;
                if(view != null)
                {
                    // 获取当前单元格的值
                    var cellValue = view.GetRowCellValue(e.RowHandle1, fieldName);
                    // 获取前一行相同列的单元格的值
                    var prevCellValue = view.GetRowCellValue(e.RowHandle2, fieldName);

                    if (e.RowHandle2 >= 0 && cellValue != null && cellValue.Equals(prevCellValue))
                    {
                        // 合并单元格
                        e.Merge = true;
                        e.Handled = true;
                    }
                }
            }
            else
            {
                e.Merge = false;
                e.Handled = true;
            }
        }

我们可以根据自己的业务需要,设置一些合并的字段,放在列表中即可。呈现的界面效果类似下面所示。

这里值得注意的时候,如果我们需要把列表设置为具有复选框的记录显示模式,方便勾选来进行其他操作,那么就需要取消合并的功能。 

 this.gridView1.OptionsView.AllowCellMerge = false;

这样才可以进行展示复选框的处理。

 

2、汇总统计处理

汇总的处理,也是类似的操作,需要设置显示底部的面板,以及设置好汇总列的处理信息。

 

 为了更方便的设置统计信息,我们可以建立一个扩展函数的方式,来实现统计行的信息处理,如下扩展方法所示。

/// <summary>
/// 设置统计列内容
/// </summary>
/// <param name="gridView">GridView对象</param>
/// <param name="fieldName">统计字段</param>
/// <param name="summaryItemType">统计类型</param>
/// <param name="prefix">显示前缀</param>
public static void SetSummaryColumn(this GridView gridView, string fieldName, SummaryItemType summaryItemType = SummaryItemType.Sum,
    string prefix = "")
{
    if (!gridView.OptionsView.ShowFooter)
    {
        gridView.OptionsView.ShowFooter = true;
    }

    if (gridView.Columns.Count > 0)
    {
        gridView.Columns[fieldName].SummaryItem.FieldName = fieldName;
        gridView.Columns[fieldName].SummaryItem.DisplayFormat = gridView.Columns[fieldName].DisplayFormat.FormatString;
        gridView.Columns[fieldName].SummaryItem.SummaryType = summaryItemType;
        gridView.Columns[fieldName].SummaryItem.DisplayFormat = prefix + "{0}";
    }
}

如果我们需要在创建统计行的时候,请空它之前的记录信息,那么也可以增加多一个扩展函数来处理清空统计信息,如下所示。

/// <summary>
/// 清空统计项目
/// </summary>
/// <param name="gridView"></param>
public static void ClearSummaryColumns(this GridView gridView)
{
    gridView.OptionsView.ShowFooter = false;
    foreach (GridColumn column in gridView.Columns)
    {
        if (column.SummaryItem != null)
        {
            column.SummaryItem.Collection.Clear();
        }
    }
}

最终我们在界面上创建统计信息的代码如下所示。

    //添加统计行
    gridView1.ClearSummaryColumns();
    gridView1.SetSummaryColumn("订单量", DevExpress.Data.SummaryItemType.Sum);
    gridView1.SetSummaryColumn("完成数量", DevExpress.Data.SummaryItemType.Sum);

最终的界面效果如下所示

 

3、复制粘贴行及导入处理

有时候为了更加便捷的对记录信息进行复制或者粘贴到GridView列表中进行处理,那么需要设置相关的GridView的属性,让它能够允许复制并设置复制的信息,这样的格式化后,就可以用于粘贴到记事本或者粘贴到新增记录模式下的GridView视图中了。

我们只需要设置其中的OptionsClipboard中的某些属性,如下代码所示。

//实现选择复制到新的行中
view.OptionsClipboard.AllowCopy = DefaultBoolean.True;  //允许复制
view.OptionsClipboard.CopyColumnHeaders = DefaultBoolean.False;  //是否复制表头
view.OptionsClipboard.PasteMode = DevExpress.Export.PasteMode.Append; //粘贴模式
view.OptionsClipboard.ClipboardMode = DevExpress.Export.ClipboardMode.Formatted;//格式化模式

然后,如果我们的GridView是设置到编辑模式的话,设置下面的代码,让它可以新增粘贴的记录。

view.OptionsBehavior.Editable = true;
view.OptionsBehavior.ReadOnly = false;
view.OptionsBehavior.AllowAddRows = true;

设置好这些,我们如果需要从剪切板中粘贴记录过来,那么只需要简单的饿调用下即可。

view.PasteFromClipboard();//从剪切板中复制记录过来

如果需要从Excel里面导入到记录表里面我们只需要读取Excel里面的记录,然后设置到当前的列表中即可。

var fileDialog = new OpenFileDialog();
fileDialog.Title = "导入xcel";
fileDialog.Filter = "Excel文件(*.xls)|*.xls";
var dialogResult = fileDialog.ShowDialog(this);
if (dialogResult == DialogResult.OK)
{
    ExcelImporter.ImportExcel(fileDialog.FileName, currentView);
}

 

4、内置的导出Excel处理及导出PDF操作

GridView本身控件提供了ExportToXls、ExportToXlsx两个方法,可以直接把数据导出为Excel文件,可以指定常规的xls或者xlsx的格式。

如下代码所示。

var fileDialog = new SaveFileDialog();
fileDialog.Title = "导出Excel";
fileDialog.Filter = "Excel文件(*.xls)|*.xls";

var dialogResult = fileDialog.ShowDialog(this);
if (dialogResult == DialogResult.OK)
{
    var options = new XlsExportOptions();
    options.TextExportMode = TextExportMode.Text; //修改绑定数据的格式为文本
    view.ExportToXls(fileDialog.FileName);
    MessageDxUtil.ShowTips("导出Excel成功!");

    if (openExcel && File.Exists(fileDialog.FileName))
    {
        System.Diagnostics.Process.Start(fileDialog.FileName);
    }
}

当然,我们也可以利用第三方控件Aspose.Cell或者NPOI、Myxls的控件进行Excel的导出操作,那样也可以提供更多通用的控制处理。

自定义格式的报表导出,可以是一个典型的图文并茂的统计报表,类似样式如下所示。

如我在随笔《使用Aspose.Cell控件实现Excel高难度报表的生成(一)》、《使用Aspose.Cell控件实现Excel高难度报表的生成(二)》、《利用Aspose.Word控件和Aspose.Cell控件,实现Word文档和Excel文档的模板化导出》中介绍过相关的处理方式,一般我们使用封装好的方法,通用的导出Excel内容即可,有时候我们直接利用分页控件进行封装,导出常规的Excel文档。

AsposeExcelTools.DataTableToExcel2(table, (String)e.Argument, out outError);

导出Excel数据的效果如下所示。

 

5、打印当前GridView视图

GridView本身也提供了直接打印的操作方法,如果对一些简单的表格,可以直接使用它进行打印当前视图处理。

 currentView.GridControl.ShowRibbonPrintPreview();

这样的打印效果,呈现出一个Ribbon的报表预览界面,然后直接在上面进行定制打印的格式。

 或者我们也可以在生成打印预览的时候,指定更多的定制信息,如下界面所示。

private void menu_PrintFixColumn_Click(object sender, EventArgs e)
{
    this.winGridViewPager1.gridView1.OptionsPrint.EnableAppearanceEvenRow = true;

    using (PrintableComponentLink link = new PrintableComponentLink(new PrintingSystem()))
    {
        link.Component = this.winGridViewPager1.gridControl1;
        link.Landscape = true;
        link.PaperKind = System.Drawing.Printing.PaperKind.A3;
        link.CreateMarginalHeaderArea += new CreateAreaEventHandler(Link_CreateMarginalHeaderArea);
        link.CreateDocument();
        link.ShowPreview();
    }
}
private void Link_CreateMarginalHeaderArea(object sender, CreateAreaEventArgs e)
{
    string title = this.AppInfo.AppUnit + " -- " + "备件信息报表";
    PageInfoBrick brick = e.Graph.DrawPageInfo(PageInfo.None, title, Color.DarkBlue,
       new RectangleF(0, 0, 100, 21), BorderSide.None);

    brick.LineAlignment = BrickAlignment.Center;
    brick.Alignment = BrickAlignment.Center;
    brick.AutoWidth = true;
    brick.Font = new System.Drawing.Font("宋体", 11f, FontStyle.Bold);
}

类似的打印预览的界面效果如下所示。

 当然我们也可以利用第三方控件的打印处理来实现更多的效果,不过内置的GridView打印操作,基本上也能满足大多数的要求了。

 

6、弹出GridView自定义菜单

 GridView的右键菜单,可以用ContextMenuStrip的常规性菜单控件来定义,我分页控件中就是采用这样的方式,设置比较简单,只需要设置GridCtrol控件的ContextMenuStrip属性即可,如下代码所示。

 this.gridControl1.ContextMenuStrip = this.contextMenuStrip1;

并且通过ContextMenuStrip的Opening事件,可以对它进行一定的设置禁用/可用的处理。

 this.contextMenuStrip1.Opening += new CancelEventHandler(contextMenuStrip1_Opening);

private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
{
    this.menu_Add.Visible = (this.OnAddNew != null && this.ShowAddMenu);
    this.menu_Delete.Visible = (this.OnDeleteSelected != null && this.ShowDeleteMenu);
    this.menu_Edit.Visible = (this.OnEditSelected != null && this.ShowEditMenu);
    this.menu_Refresh.Visible = (this.OnRefresh != null);
}

这样就可以对GridView的右键进行绑定及权限的设置处理,类似下面的界面效果所示。

虽然利用ContextMenuStrip的传统菜单条,可以很好、方便的实现右键菜单的处理,不过缺点是样式没有随着DevExpress本身的效果变化,如果需要追求一样的样式体验,那么可以考虑使用DevExpress的PopupMenu控件来承载菜单或者Ribbon的一些按钮操作。

 PopupMenu控件可以指定Ribbon窗体控件,然后它们右键菜单和Ribbon的按钮集合同样的出现和隐藏。

然后在设计模式下设计对应的菜单项目集合。

在界面设计好Ribbon的按钮和菜单对象的按钮后,我们可以为菜单绑定对应的GridControl事件处理,让它结合GridControl的右键事件出现右键菜单。

this.gridControl.MouseUp += GridControl_MouseUp;

显示右键菜单的事件代码如下所示。

private void GridControl_MouseUp(object sender, MouseEventArgs e)
{
    try
    {
        if (e.Button == MouseButtons.Right)
        {
            var view = gridControl.DefaultView as GridView;
            var info = view.CalcHitInfo(e.Location);
            if (info.InRowCell)
            {
                popupGridMenu.ShowPopup(gridControl.PointToScreen(e.Location));
            }
        }
    }
    catch (Exception ex)
    {
        MessageDxUtil.ShowError(ex.Message);
    }
}

 

7、直接新增保存的处理

之前在随笔《在DevExpress程序中使用GridView直接录入数据的时候,增加列表选择的功能 》 、《在DevExpress程序中使用Winform分页控件直接录入数据并保存》分别介绍了两种不同方式的数据直接在GridView列表中处理的方式,本质上两者是一致的,都是利用GridView本身的一些事件进行操作,实现更加方便的数据录入体验。

我们一般通过 InitNewRow 、ValidateRow 、CellValueChanged来处理数据的录入操作,如下详细操作的界面代码所示。

private void RegisterEvent()
{
    var grd = this.gridControl1;
    var grv = this.gridView1;
    grv.InitGridView(GridType.NewItem, false, EditorShowMode.MouseDownFocused, "");
    //创建显示的列
    grv.CreateColumn("ItemNo", "备件编号", 120).CreateButtonEdit().ButtonClick += (s, e) =>
    {
        #region 选取备件信息,返回后赋值当前记录
        if (grv.GetFocusedRow() == null)
        {
            grv.AddNewRow();//一定要增加
        }
        FrmSelectItemDetail dlg = new FrmSelectItemDetail();
        dlg.WareHouse = this.txtWareHouse.Text;
        if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
        {
            var info = dlg.ItemDetailInfo;
            if (info != null)
            {
                grv.SetFocusedRowCellValue("ItemNo", info.ItemNo);
                grv.SetFocusedRowCellValue("ItemName", info.ItemName);
                grv.SetFocusedRowCellValue("ItemBigType", info.ItemBigType);
                grv.SetFocusedRowCellValue("ItemType", info.ItemType);
                grv.SetFocusedRowCellValue("MapNo", info.MapNo);
                grv.SetFocusedRowCellValue("Specification", info.Specification);
                grv.SetFocusedRowCellValue("Unit", info.Unit);
                grv.SetFocusedRowCellValue("Price", info.Price);
                grv.SetFocusedRowCellValue("Material", info.Material);
                grv.SetFocusedRowCellValue("Source", info.Source);
                grv.SetFocusedRowCellValue("StoragePos", info.StoragePos);
                grv.SetFocusedRowCellValue("UsagePos", info.UsagePos);
                grv.SetFocusedRowCellValue("WareHouse", info.WareHouse);
                grv.SetFocusedRowCellValue("Dept", info.Dept);
                grv.SetFocusedRowCellValue("Quantity", 1);//默认数量为1
            }
        }
        #endregion
    };
    grv.CreateColumn("ItemName", "备件名称", 120);
    grv.CreateColumn("Quantity", "数量").CreateSpinEdit();
    grv.CreateColumn("ItemBigType", "备件属类", 120);
    grv.CreateColumn("ItemType", "备件类别", 120);
    grv.CreateColumn("MapNo", "图号");
    grv.CreateColumn("Specification", "规格型号", 120);
    grv.CreateColumn("Unit", "单位");
    grv.CreateColumn("Price", "单价");
    grv.CreateColumn("Amount", "金额");
    grv.CreateColumn("Material", "材质", 120);
    grv.CreateColumn("Source", "来源", 120);
    grv.CreateColumn("StoragePos", "库位", 120);
    grv.CreateColumn("UsagePos", "使用位置", 120);
    grv.CreateColumn("WareHouse", "所属库房", 120);
    grv.CreateColumn("Dept", "所属部门", 120);

    //设置部分字段不可修改
    var readonlyFields = "ItemName,ItemBigType,ItemType,MapNo,Specification,Unit,Price,Amount,Material,Source,UsagePos,WareHouse,Dept";
    grv.SetColumnsReadOnly(readonlyFields);

    //绑定数据源,否则无法新增存储
    var list = new List<ItemDetailInfo>();
    var dt = DataTableHelper.ConvertToDataTable<ItemDetailInfo>(list);

    //同时增加两列在实体类属性里没有的列
    dt.Columns.Add(new DataColumn("Quantity", typeof(int)));
    dt.Columns.Add(new DataColumn("Amount", typeof(decimal)));
    grd.DataSource = dt;

    grv.InitNewRow += delegate(object sender, InitNewRowEventArgs e)
    {
        //如果是GUID的主键,可以初始化,以及赋值明细记录的父ID等操作
        //GridView gridView = grd.FocusedView as GridView;
        //gridView.SetFocusedRowCellValue("ID", Guid.NewGuid().ToString());
    };
    grv.ValidateRow += delegate(object sender, ValidateRowEventArgs e)
    {
        //校验一些不能为空的字段
        var result = grd.ValidateRowNull(e, new string[]
        {
            "ItemNo",
            "ItemName",
            "Quantity"
        });
    };
    grv.CellValueChanged += (object sender, CellValueChangedEventArgs e) =>
    {
        //根据数量计算金额
        if (e.Column.FieldName == "Quantity" && e.Value != null)
        {
            var Price = string.Concat(grv.GetFocusedRowCellValue("Price")).ToDecimal();
            var Quantity = string.Concat(e.Value).ToDecimal();
            grv.SetFocusedRowCellValue("Amount", Price * Quantity);
        }
    };
    grv.RowCellStyle += (object sender, RowCellStyleEventArgs e) =>
    {
        //设置特殊颜色标志
        if (e.Column.FieldName == "Quantity" )
        {
            e.Appearance.BackColor = Color.Moccasin;
            e.Appearance.ForeColor = Color.Red;
        }
    };    
}

而如果需要结合删除的功能,那么可以增加对RowDeleted的事件处理。

    //行删除操作
    grv.OptionsBehavior.AllowDeleteRows = DefaultBoolean.True;
    grv.RowDeleted += (s, ee) =>
    {
        //同时移除价格列表
        var info = ee.Row as OrderInfo;
        if(info != null)
        {
            this.OrderInfos.Remove(info);
            for (int i = 0; i < this.gridView2.RowCount; i++)
            {
                var code = (string)this.gridView2.GetRowCellValue(i, "产品编码");
                if(info.产品编码 == code)
                {
                    this.gridView2.DeleteRow(i);
                }
            }
        }
    };
    grv.KeyDown += (s, ee) =>
    {
        if (ee.KeyCode == Keys.Delete)
        {
            gridView1.DeleteSelectedRows();
            ee.Handled = true;
        }
    };

以上就是在实际项目中,常用到的GridControl和GridView的常规处理方法,用好这些控件的处理,可以极大程度的提高用户的界面体验。

当然可能还有很多常用的方法或者处理方式,等待大家的进一步挖掘和分享。

与基于DevExpress的GridControl实现的一些界面处理功能相似的内容:

基于DevExpress的GridControl实现的一些界面处理功能

DevExpress的GridControl控件能够提供很多强大的操作,其视图GridView能够通过各种设置,呈现出多种复杂的界面效果,本篇随笔探讨一些常见的GridControl控件及其GridView的视图操作处理,以便在大家也需要的时候作为一个参考的代码。其中设计一些常见的操作,如合并单元格、汇总统计、复制粘贴行、导入数据处理、导出Excel、导出PDF等、打印GridView视图、内置插

在DevExpress的GridView的列中,使用RepositoryItemSearchLookUpEdit控件实现产品列表信息的展示和选择

有时候,我们为了方便,我们往往使用扩展函数的代码方式创建很多GridView的操作功能,如在随笔《在DevExpress中使用BandedGridView表格实现多行表头的处理》中介绍过多行表头的创建及绑定处理,在《基于DevExpress的GridControl实现的一些界面处理功能》也介绍了一些特殊的展示效果,本篇随笔介绍在DevExpress的GridView的列中,使用Repository

基于SqlSugar的开发框架循序渐进介绍(19)-- 基于UniApp+Vue的移动前端的功能介绍

在之前的SqlSugar系列随笔中,介绍了很多我们关于SqlSugar的开发框架的内容,SqlSugar的开发框架的目的是多前端应用场景,因此其中会包含各种不同的前端应用,前面介绍了基于DevExpress的Winform的前端应用,以及基于Vue3+TypeScript+ElementPlus的BS前端应用,本篇随笔继续介绍SqlSugar的开发框架的另一个前端应用,基于UniApp+Vue+T

在Winform分页控件中集成保存用户列表显示字段及宽度调整设置

在Winform的分页控件里面,我们提供了很多丰富的功能,如常规分页,中文转义、导出Excel、导出PDF等,基于DevExpress的样式的分页控件,我们在其上面做了不少封装,以便更好的使用,其中就包括集成保存用户列表显示字段及宽度调整设置。本篇随笔介绍这个实现的过程,通过在当前程序中序列化方式存储一个记录用户设置的文件,提供介质的存储和加载处理。

基于 Three.js 的 3D 模型加载优化

作为一个3D的项目,从用户打开页面到最终模型的渲染加载的时间也会比普通的H5项目要更长一些,从而造成大量的用户流失。为了提升首屏加载的转化率,需要尽可能的降低loading的时间。这里就分享一些我们在模型加载优化方面的心得。

基于MindSpore实现BERT对话情绪识别

本文分享自华为云社区《【昇思25天学习打卡营打卡指南-第二十四天】基于 MindSpore 实现 BERT 对话情绪识别》,作者:JeffDing。 模型简介 BERT全称是来自变换器的双向编码器表征量(Bidirectional Encoder Representations from Trans

基于 Vagrant 手动部署多个 Redis Server

环境准备 宿主机环境:Windows 10 虚拟机环境:Vagrant + VirtualBox Vagrantfile 配置 首先,我们需要编写一个 Vagrantfile 来定义我们的虚拟机配置。假设已经在 D:\Vagrant\redis 目录下创建了一个 Vagrantfile,其内容如下:

基于EF Core存储的Serilog持久化服务

前言 Serilog是 .NET 上的一个原生结构化高性能日志库,这个库能实现一些比内置库更高度的定制。日志持久化是其中一个非常重要的功能,生产环境通常很难挂接调试器或者某些bug的触发条件很奇怪。为了在脱离调试环境的情况下尽可能保留更多线索来辅助解决生产问题,持久化的日志就显得很重要了。目前Ser

基于EF Core存储的国际化服务

前言 .NET 官方有一个用来管理国际化资源的扩展包Microsoft.Extensions.Localization,ASP.NET Core也用这个来实现国际化功能。但是这个包的翻译数据是使用resx资源文件来管理的,这就意味着无法动态管理。虽然官方有在文档中提供了一些第三方管理方案,但是都不太

基于FileZilla上传、下载服务器数据的方法

本文介绍FileZilla软件的下载、配置与使用方法。 在之前的博客中,我们提到了下载高分遥感影像数据需要用到FTP(文件传输协议,File Transfer Protocol)软件FileZilla;这一软件用以在自己的电脑与服务器之间相互传输数据,在进行下载科学数据、网站开发等等操作时,经常需要