基於DevExpress的GridControl實現的一些介面處理功能

2023-05-15 18:01:05

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的匯出操作,那樣也可以提供更多通用的控制處理。

自定義格式的報表匯出,可以是一個典型的圖文並茂的統計報表,類似樣式如下所示。

 或者我們也可以在生成預覽列印的時候,指定更多的客製化資訊,如下介面所示。

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的常規處理方法,用好這些控制元件的處理,可以極大程度的提高使用者的介面體驗。

當然可能還有很多常用的方法或者處理方式,等待大家的進一步挖掘和分享。