From 0ddec0c3951dd897c57d306b5eb7b04c42640a61 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Mon, 6 Apr 2026 17:25:17 +0800 Subject: [PATCH 01/12] updated grid to use built in context menu --- .../Data/WeatherForecastService.cs | 2 +- .../GridWithContextMenu.csproj | 2 +- CS/GridWithContextMenu/Pages/Index.razor | 97 +++++++++++++++++-- 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/CS/GridWithContextMenu/Data/WeatherForecastService.cs b/CS/GridWithContextMenu/Data/WeatherForecastService.cs index a92bdd0..cc4ecb4 100644 --- a/CS/GridWithContextMenu/Data/WeatherForecastService.cs +++ b/CS/GridWithContextMenu/Data/WeatherForecastService.cs @@ -8,7 +8,7 @@ public enum Summaries { public Task> GetForecastAsync() { if (Forecasts == null) { var rnd = new Random(); - Forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast { + Forecasts = Enumerable.Range(1, 25).Select(index => new WeatherForecast { ID = index, Date = DateTime.Today.AddDays(index), TemperatureC = rnd.Next(-20, 55), diff --git a/CS/GridWithContextMenu/GridWithContextMenu.csproj b/CS/GridWithContextMenu/GridWithContextMenu.csproj index 08c7248..8fa22e6 100644 --- a/CS/GridWithContextMenu/GridWithContextMenu.csproj +++ b/CS/GridWithContextMenu/GridWithContextMenu.csproj @@ -8,6 +8,6 @@ - + diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index fdcf795..4ddc944 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -8,14 +8,37 @@ EditMode="GridEditMode.EditRow" CssClass="mw-1100" CustomizeElement="Grid_CustomizeElement" - @oncontextmenu:preventDefault EditModelSaving="Grid_EditModelSaving" - DataItemDeleting="Grid_DataItemDeleting"> + DataItemDeleting="Grid_DataItemDeleting" + ContextMenus="GridContextMenus.All" + CustomizeContextMenu="Grid_CustomizeContextMenu"> + + + + + + + + + + + + + + + + + - + - + @@ -28,18 +51,47 @@ IGrid Grid { get; set; } GridContextMenuContainer ContextMenuContainer { get; set; } object GridData { get; set; } + string GridSearchText = ""; + const string ExportFileName = "ExportResult"; protected override async Task OnInitializedAsync() { GridData = await ForecastService.GetForecastAsync(); } + void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) + { + if (args.Context is GridDataRowCommandContext rowContext) + { + args.Items.AddCustomItem("New Row", async () => + { + rowContext.Grid.BeginUpdate(); + await rowContext.Grid.StartEditNewRowAsync(); + rowContext.Grid.EndUpdate(); + }); + args.Items.AddCustomItem("Edit Row", async () => + { + rowContext.Grid.BeginUpdate(); + await rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex); + rowContext.Grid.EndUpdate(); + }); + args.Items.AddCustomItem("Delete Row", async () => + { + rowContext.Grid.BeginUpdate(); + rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex); + rowContext.Grid.EndUpdate(); + }); + args.Items.Add(GridContextMenuDefaultItemNames.ExpandAll); + args.Items.Add(GridContextMenuDefaultItemNames.CollapseAll); + } + } + void Grid_CustomizeElement(GridCustomizeElementEventArgs e) { - if(GridContextMenuHelper.IsContextMenuElement(e.ElementType)) { - e.Attributes["oncontextmenu"] = EventCallback.Factory.Create( - this, - async mArgs => await ContextMenuContainer.Grid_ContextMenu(e, mArgs) - ); - } + // if(GridContextMenuHelper.IsContextMenuElement(e.ElementType)) { + // e.Attributes["oncontextmenu"] = EventCallback.Factory.Create( + // this, + // async mArgs => await ContextMenuContainer.Grid_ContextMenu(e, mArgs) + // ); + // } } async Task Grid_EditModelSaving(GridEditModelSavingEventArgs e) { @@ -51,4 +103,29 @@ await ForecastService.Remove(item); } } + + async Task NewItem_Click() + { + await Grid.StartEditNewRowAsync(); + } + void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) + { + Grid.ShowColumnChooser(); + } + async Task ExportXlsxItem_Click() + { + await Grid.ExportToXlsxAsync(ExportFileName); + } + async Task ExportXlsItem_Click() + { + await Grid.ExportToXlsAsync(ExportFileName); + } + async Task ExportCsvItem_Click() + { + await Grid.ExportToCsvAsync(ExportFileName); + } + async Task ExportPdfItem_Click() + { + await Grid.ExportToPdfAsync(ExportFileName); + } } \ No newline at end of file From c14636102aecf9831009a46acb8044587dec22ab Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Tue, 7 Apr 2026 15:16:07 +0800 Subject: [PATCH 02/12] add context menu items to row context menu --- .../Data/GridContextMenuHelper.cs | 4 +-- CS/GridWithContextMenu/Pages/Index.razor | 32 +++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index dd18154..521ae1b 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -65,7 +65,7 @@ static List CreateRowContextMenuItems() { } public static bool IsContextMenuElement(GridElementType elementType) { - return IsColumnContextMenuElement(elementType) || IsRowContextMenuElement(elementType); + return IsRowContextMenuElement(elementType); } public static bool IsColumnContextMenuElement(GridElementType elementType) { switch(elementType) { @@ -79,8 +79,6 @@ public static bool IsColumnContextMenuElement(GridElementType elementType) { } public static bool IsRowContextMenuElement(GridElementType elementType) { switch(elementType) { - case GridElementType.DataRow: - case GridElementType.GroupRow: case GridElementType.EditRow: return true; } diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index 4ddc944..d3e86c2 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -11,7 +11,8 @@ EditModelSaving="Grid_EditModelSaving" DataItemDeleting="Grid_DataItemDeleting" ContextMenus="GridContextMenus.All" - CustomizeContextMenu="Grid_CustomizeContextMenu"> + CustomizeContextMenu="Grid_CustomizeContextMenu" + > @@ -61,6 +62,21 @@ { if (args.Context is GridDataRowCommandContext rowContext) { + if (rowContext.Grid.IsEditing()) + { + args.Items.AddCustomItem("Save Changes", async () => + { + rowContext.Grid.BeginUpdate(); + await rowContext.Grid.SaveChangesAsync(); + rowContext.Grid.EndUpdate(); + }); + args.Items.AddCustomItem("Cancel Editing", async () => + { + rowContext.Grid.BeginUpdate(); + await rowContext.Grid.CancelEditAsync(); + rowContext.Grid.EndUpdate(); + }); + } args.Items.AddCustomItem("New Row", async () => { rowContext.Grid.BeginUpdate(); @@ -79,19 +95,17 @@ rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex); rowContext.Grid.EndUpdate(); }); - args.Items.Add(GridContextMenuDefaultItemNames.ExpandAll); - args.Items.Add(GridContextMenuDefaultItemNames.CollapseAll); } } void Grid_CustomizeElement(GridCustomizeElementEventArgs e) { - // if(GridContextMenuHelper.IsContextMenuElement(e.ElementType)) { - // e.Attributes["oncontextmenu"] = EventCallback.Factory.Create( - // this, - // async mArgs => await ContextMenuContainer.Grid_ContextMenu(e, mArgs) - // ); - // } + if(GridContextMenuHelper.IsContextMenuElement(e.ElementType)) { + e.Attributes["oncontextmenu"] = EventCallback.Factory.Create( + this, + async mArgs => await ContextMenuContainer.Grid_ContextMenu(e, mArgs) + ); + } } async Task Grid_EditModelSaving(GridEditModelSavingEventArgs e) { From 9bc2fcbb7e7d59bdbf0da39507564341083ed5f6 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Fri, 17 Apr 2026 17:14:35 +0800 Subject: [PATCH 03/12] Added custom items to built-in column context menu --- .../Data/GridContextMenuHelper.cs | 60 ++----------- .../Pages/GridContextMenuContainer.razor | 2 +- CS/GridWithContextMenu/Pages/Index.razor | 90 ++++++++++++------- 3 files changed, 67 insertions(+), 85 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 521ae1b..4d8c591 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -33,18 +33,8 @@ static List CreateColumnContextMenuItems() { return new List { new ContextMenuItem { ItemType = GridContextMenuItemType.FullExpand, Text = "Expand All", IconCssClass="grid-context-menu-item-full-expand" }, new ContextMenuItem { ItemType = GridContextMenuItemType.FullCollapse, Text = "Collapse All", IconCssClass="grid-context-menu-item-full-collapse" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.SortAscending, Text = "Sort Ascending", BeginGroup = true, IconCssClass="grid-context-menu-item-sort-ascending" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.SortDescending, Text = "Sort Descending", IconCssClass="grid-context-menu-item-sort-descending" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.ClearSorting, Text = "Clear Sorting" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.GroupByColumn, Text = "Group By This Column", BeginGroup = true, IconCssClass="grid-context-menu-item-group-by-column" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.UngroupColumn, Text = "Ungroup", IconCssClass="grid-context-menu-item-ungroup-column" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.ClearGrouping, Text = "Clear Grouping", IconCssClass="grid-context-menu-item-clear-grouping" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowGroupPanel, Text = "Group Panel", IconCssClass="grid-context-menu-item-show-group-panel" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.HideColumn, Text = "Hide Column", BeginGroup = true, IconCssClass="grid-context-menu-item-hide-column" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowColumnChooser, Text = "Column Chooser", IconCssClass="grid-context-menu-item-column-chooser" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.FixColumnToLeft, Text = "Fix Column to the Left", BeginGroup = true, IconCssClass="grid-context-menu-item-fix-column-left" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.FixColumnToRight, Text = "Fix Column to the Right", IconCssClass="grid-context-menu-item-fix-column-right" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.Unfix, Text = "Unfix Column", IconCssClass="grid-context-menu-item-unfix-column" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ClearFilter, Text = "Clear Filter", BeginGroup = true, IconCssClass="grid-context-menu-item-clear-filter" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFilterRow, Text = "Filter Row", IconCssClass="grid-context-menu-item-filter-row" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFooter, Text = "Footer", IconCssClass="grid-context-menu-item-footer" } @@ -65,15 +55,13 @@ static List CreateRowContextMenuItems() { } public static bool IsContextMenuElement(GridElementType elementType) { - return IsRowContextMenuElement(elementType); + return IsCustomContextMenuElement(elementType) || IsRowContextMenuElement(elementType); } - public static bool IsColumnContextMenuElement(GridElementType elementType) { + public static bool IsCustomContextMenuElement(GridElementType elementType) { switch(elementType) { - case GridElementType.HeaderCell: - case GridElementType.HeaderCommandCell: - case GridElementType.HeaderSelectionCell: - case GridElementType.GroupPanelHeader: - return true; + //case GridElementType.ToolbarContainer: + //case GridElementType.PagerContainer: + // return true; } return false; } @@ -95,30 +83,6 @@ public static void ProcessColumnMenuItemClick(ContextMenuItem item, IGridColumn case GridContextMenuItemType.FullCollapse: grid.CollapseAllGroupRows(); break; - case GridContextMenuItemType.SortAscending: - if(dataColumn.SortOrder != GridColumnSortOrder.Ascending) { - var newSortIndex = dataColumn.SortIndex > -1 ? dataColumn.SortIndex : grid.GetSortedColumns().Count; - grid.SortBy(dataColumn.FieldName, GridColumnSortOrder.Ascending, newSortIndex); - } - break; - case GridContextMenuItemType.SortDescending: - if(dataColumn.SortOrder != GridColumnSortOrder.Descending) { - var newSortIndex = dataColumn.SortIndex > -1 ? dataColumn.SortIndex : grid.GetSortedColumns().Count; - grid.SortBy(dataColumn.FieldName, GridColumnSortOrder.Descending, newSortIndex); - } - break; - case GridContextMenuItemType.ClearSorting: - grid.SortBy(dataColumn.FieldName, GridColumnSortOrder.Descending, -1); - break; - case GridContextMenuItemType.GroupByColumn: - grid.GroupBy(dataColumn.FieldName, grid.GetGroupCount()); - break; - case GridContextMenuItemType.UngroupColumn: - grid.GroupBy(dataColumn.FieldName, -1); - break; - case GridContextMenuItemType.ClearGrouping: - grid.ClearSort(); - break; case GridContextMenuItemType.ShowGroupPanel: grid.ShowGroupPanel = !grid.ShowGroupPanel; break; @@ -129,22 +93,10 @@ public static void ProcessColumnMenuItemClick(ContextMenuItem item, IGridColumn var isFooterVisible = grid.FooterDisplayMode == GridFooterDisplayMode.Always || grid.FooterDisplayMode == GridFooterDisplayMode.Auto && grid.GetTotalSummaryItems().Count > 0; grid.FooterDisplayMode = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; - break; - case GridContextMenuItemType.HideColumn: - column.Visible = false; - break; + break; case GridContextMenuItemType.ShowColumnChooser: grid.ShowColumnChooser(); break; - case GridContextMenuItemType.FixColumnToLeft: - column.FixedPosition = GridColumnFixedPosition.Left; - break; - case GridContextMenuItemType.FixColumnToRight: - column.FixedPosition = GridColumnFixedPosition.Right; - break; - case GridContextMenuItemType.Unfix: - column.FixedPosition = GridColumnFixedPosition.None; - break; case GridContextMenuItemType.ClearFilter: grid.ClearFilter(); break; diff --git a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor index 1756045..e0fed7a 100644 --- a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor +++ b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor @@ -46,7 +46,7 @@ => await GridContextMenuHelper.ProcessRowMenuItemClickAsync((ContextMenuItem)e.ItemInfo.DataItem, ContextMenuRowIndex, Grid); public async Task Grid_ContextMenu(GridCustomizeElementEventArgs e, MouseEventArgs mouseArgs) { - if(GridContextMenuHelper.IsColumnContextMenuElement(e.ElementType)) { + if(GridContextMenuHelper.IsCustomContextMenuElement(e.ElementType)) { ContextMenuColumn = e.Column; ColumnContextMenuData = GridContextMenuHelper.GetColumnItems(e); await ColumnContextMenu.ShowAsync(mouseArgs); diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index d3e86c2..b42a1e6 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -12,7 +12,7 @@ DataItemDeleting="Grid_DataItemDeleting" ContextMenus="GridContextMenus.All" CustomizeContextMenu="Grid_CustomizeContextMenu" - > + @oncontextmenu:preventDefault> @@ -58,44 +58,80 @@ protected override async Task OnInitializedAsync() { GridData = await ForecastService.GetForecastAsync(); } - void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) - { - if (args.Context is GridDataRowCommandContext rowContext) - { - if (rowContext.Grid.IsEditing()) - { - args.Items.AddCustomItem("Save Changes", async () => - { + void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { + if (args.Context is GridDataRowCommandContext rowContext) { + if (rowContext.Grid.IsEditing()) { + args.Items.AddCustomItem("Save", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.SaveChangesAsync(); rowContext.Grid.EndUpdate(); }); - args.Items.AddCustomItem("Cancel Editing", async () => - { + args.Items.AddCustomItem("Cancel", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.CancelEditAsync(); rowContext.Grid.EndUpdate(); }); } - args.Items.AddCustomItem("New Row", async () => - { + args.Items.AddCustomItem("New", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.StartEditNewRowAsync(); rowContext.Grid.EndUpdate(); - }); - args.Items.AddCustomItem("Edit Row", async () => - { + }).BeginGroup = true; + args.Items.AddCustomItem("Edit", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex); rowContext.Grid.EndUpdate(); }); - args.Items.AddCustomItem("Delete Row", async () => - { + args.Items.AddCustomItem("Delete", async () => { rowContext.Grid.BeginUpdate(); rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex); rowContext.Grid.EndUpdate(); }); } + + if (args.Context is GridHeaderCommandContext headerContext) { + var isFilterRowVisible = headerContext.Grid.ShowFilterRow != false; + string filterRowItemText = isFilterRowVisible ? "Hide Filter Row" : "Show Filter Row"; + var newFilterRowState = isFilterRowVisible ? false : true; + args.Items.AddCustomItem(filterRowItemText, () => { + headerContext.Grid.BeginUpdate(); + headerContext.Grid.ShowFilterRow = newFilterRowState; + headerContext.Grid.EndUpdate(); + }); + + var isFiltered = headerContext.Grid.GetFilterCriteria() != null; + args.Items.AddCustomItem("Clear Filter", () => { + headerContext.Grid.BeginUpdate(); + headerContext.Grid.ClearFilter(); + headerContext.Grid.EndUpdate(); + }).Enabled = isFiltered ? true : false; + + args.Items.AddCustomItem("Fix Column to the Left", () => { + headerContext.Grid.BeginUpdate(); + headerContext.Column.FixedPosition = GridColumnFixedPosition.Left; + headerContext.Grid.EndUpdate(); + }).BeginGroup = true; + args.Items.AddCustomItem("Fix Column to the Right", () => { + headerContext.Grid.BeginUpdate(); + headerContext.Column.FixedPosition = GridColumnFixedPosition.Right; + headerContext.Grid.EndUpdate(); + }); + args.Items.AddCustomItem("Unfix Column", () => { + headerContext.Grid.BeginUpdate(); + headerContext.Column.FixedPosition = GridColumnFixedPosition.None; + headerContext.Grid.EndUpdate(); + }); + + var isFooterVisible = headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always + || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; + string footerItemText = isFooterVisible ? "Hide Footer" : "Show Footer"; + var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; + args.Items.AddCustomItem(footerItemText, () => { + headerContext.Grid.BeginUpdate(); + headerContext.Grid.FooterDisplayMode = newFooterState; + headerContext.Grid.EndUpdate(); + }); + } } @@ -118,28 +154,22 @@ } } - async Task NewItem_Click() - { + async Task NewItem_Click() { await Grid.StartEditNewRowAsync(); } - void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) - { + void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) { Grid.ShowColumnChooser(); } - async Task ExportXlsxItem_Click() - { + async Task ExportXlsxItem_Click() { await Grid.ExportToXlsxAsync(ExportFileName); } - async Task ExportXlsItem_Click() - { + async Task ExportXlsItem_Click() { await Grid.ExportToXlsAsync(ExportFileName); } - async Task ExportCsvItem_Click() - { + async Task ExportCsvItem_Click() { await Grid.ExportToCsvAsync(ExportFileName); } - async Task ExportPdfItem_Click() - { + async Task ExportPdfItem_Click() { await Grid.ExportToPdfAsync(ExportFileName); } } \ No newline at end of file From 45d10940e168766f7d53c595e440811c5a33e6f3 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Mon, 20 Apr 2026 15:47:36 +0800 Subject: [PATCH 04/12] Updated column context menu to custom context menu --- .../Data/GridContextMenuHelper.cs | 105 ++++-------------- .../Pages/GridContextMenuContainer.razor | 20 ++-- CS/GridWithContextMenu/Pages/Index.razor | 2 +- 3 files changed, 30 insertions(+), 97 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 4d8c591..7f6789a 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -3,9 +3,8 @@ namespace GridWithContextMenu.Data { public enum GridContextMenuItemType { FullExpand, FullCollapse, - SortAscending, SortDescending, ClearSorting, - GroupByColumn, UngroupColumn, ClearGrouping, ShowGroupPanel, - HideColumn, ShowColumnChooser, + ShowGroupPanel, + ShowColumnChooser, ClearFilter, ShowFilterRow, ShowFooter, @@ -13,9 +12,9 @@ public enum GridContextMenuItemType { ExpandDetailRow, CollapseDetailRow, NewRow, EditRow, DeleteRow, - FixColumnToRight, FixColumnToLeft, Unfix, + SaveUpdates, CancelUpdates, - SaveUpdates, CancelUpdates + ExportXls, ExportXlsx, ExportPdf, ExportDocx } public class ContextMenuItem { @@ -29,7 +28,7 @@ public class ContextMenuItem { } public class GridContextMenuHelper { - static List CreateColumnContextMenuItems() { + static List CreateCustomContextMenuItems() { return new List { new ContextMenuItem { ItemType = GridContextMenuItemType.FullExpand, Text = "Expand All", IconCssClass="grid-context-menu-item-full-expand" }, new ContextMenuItem { ItemType = GridContextMenuItemType.FullCollapse, Text = "Collapse All", IconCssClass="grid-context-menu-item-full-collapse" }, @@ -59,9 +58,9 @@ public static bool IsContextMenuElement(GridElementType elementType) { } public static bool IsCustomContextMenuElement(GridElementType elementType) { switch(elementType) { - //case GridElementType.ToolbarContainer: - //case GridElementType.PagerContainer: - // return true; + case GridElementType.ToolbarContainer: + case GridElementType.PagerContainer: + return true; } return false; } @@ -73,8 +72,7 @@ public static bool IsRowContextMenuElement(GridElementType elementType) { return false; } - public static void ProcessColumnMenuItemClick(ContextMenuItem item, IGridColumn column, IGrid grid) { - var dataColumn = column as IGridDataColumn; + public static void ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) { grid.BeginUpdate(); switch(item.ItemType) { case GridContextMenuItemType.FullExpand: @@ -134,19 +132,19 @@ public static async Task ProcessRowMenuItemClickAsync(ContextMenuItem item, int break; } } - public static List GetColumnItems(GridCustomizeElementEventArgs e) { - var items = CreateColumnContextMenuItems(); + public static List GetCustomItems(GridCustomizeElementEventArgs e) { + var items = CreateCustomContextMenuItems(); var applyBeginGroupForNextVisibleItem = false; foreach(var item in items) { - item.Visible = IsColumnMenuItemVisible(e, item.ItemType); + item.Visible = IsCustomMenuItemVisible(e, item.ItemType); if(!item.Visible && item.BeginGroup) applyBeginGroupForNextVisibleItem = true; if(item.Visible && applyBeginGroupForNextVisibleItem) { item.BeginGroup = true; applyBeginGroupForNextVisibleItem = false; } - item.Enabled = IsColumnMenuItemEnabled(e, item.ItemType); - var isSelected = IsColumnMenuItemSelected(e, item.ItemType); + item.Enabled = IsCustomMenuItemEnabled(e, item.ItemType); + var isSelected = IsCustomMenuItemSelected(e, item.ItemType); if(item.Enabled && isSelected) item.CssClass = "menu-item-selected"; } @@ -161,58 +159,22 @@ public static List GetRowItems(GridCustomizeElementEventArgs e) return items; } - static bool IsColumnMenuItemVisible(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { - var dataColumn = e.Column as IGridDataColumn; - var allowSort = GetAllowSort(e.Column, e.Grid); - var allowGroup = GetAllowGroup(e.Column, e.Grid); + static bool IsCustomMenuItemVisible(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { + switch(itemType) { case GridContextMenuItemType.FullExpand: - case GridContextMenuItemType.FullCollapse: - return e.ElementType == GridElementType.GroupPanelHeader; - case GridContextMenuItemType.SortAscending: - case GridContextMenuItemType.SortDescending: - return allowSort; - case GridContextMenuItemType.ClearSorting: - return allowSort && dataColumn.GroupIndex < 0; - case GridContextMenuItemType.GroupByColumn: - return allowGroup && dataColumn.GroupIndex < 0; - case GridContextMenuItemType.UngroupColumn: - return allowGroup && dataColumn.GroupIndex > -1; - case GridContextMenuItemType.ClearGrouping: - return e.Grid.AllowGroup; + case GridContextMenuItemType.FullCollapse: case GridContextMenuItemType.ShowGroupPanel: case GridContextMenuItemType.ShowFilterRow: case GridContextMenuItemType.ShowFooter: - case GridContextMenuItemType.HideColumn: case GridContextMenuItemType.ShowColumnChooser: - case GridContextMenuItemType.FixColumnToLeft: - case GridContextMenuItemType.FixColumnToRight: - case GridContextMenuItemType.Unfix: case GridContextMenuItemType.ClearFilter: return true; } return false; } - static bool IsColumnMenuItemSelected(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { - var dataColumn = e.Column as IGridDataColumn; - var isSorted = dataColumn != null && dataColumn.SortIndex > -1; - var isGrouped = dataColumn != null && dataColumn.GroupIndex > -1; - var sortOrder = GridColumnSortOrder.None; - var fixedPosition = dataColumn.FixedPosition; - if (isSorted || isGrouped) { - sortOrder = dataColumn.SortOrder; - if (sortOrder == GridColumnSortOrder.None) - sortOrder = GridColumnSortOrder.Ascending; - } + static bool IsCustomMenuItemSelected(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { switch(itemType) { - case GridContextMenuItemType.SortAscending: - return sortOrder == GridColumnSortOrder.Ascending; - case GridContextMenuItemType.SortDescending: - return sortOrder == GridColumnSortOrder.Descending; - case GridContextMenuItemType.FixColumnToLeft: - return fixedPosition == GridColumnFixedPosition.Left; - case GridContextMenuItemType.FixColumnToRight: - return fixedPosition == GridColumnFixedPosition.Right; case GridContextMenuItemType.ShowGroupPanel: return e.Grid.ShowGroupPanel; case GridContextMenuItemType.ShowFilterRow: @@ -223,33 +185,17 @@ static bool IsColumnMenuItemSelected(GridCustomizeElementEventArgs e, GridContex } return false; } - static bool IsColumnMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { - var dataColumn = e.Column as IGridDataColumn; - var allowSort = GetAllowSort(e.Column, e.Grid); - var allowGroup = GetAllowGroup(e.Column, e.Grid); + static bool IsCustomMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { switch(itemType) { case GridContextMenuItemType.FullExpand: case GridContextMenuItemType.FullCollapse: - case GridContextMenuItemType.SortAscending: - case GridContextMenuItemType.SortDescending: - case GridContextMenuItemType.GroupByColumn: - case GridContextMenuItemType.UngroupColumn: case GridContextMenuItemType.ShowGroupPanel: case GridContextMenuItemType.ShowFilterRow: case GridContextMenuItemType.ShowFooter: - case GridContextMenuItemType.HideColumn: case GridContextMenuItemType.ShowColumnChooser: - case GridContextMenuItemType.FixColumnToLeft: - case GridContextMenuItemType.FixColumnToRight: return true; - case GridContextMenuItemType.ClearSorting: - return allowSort && (dataColumn.SortIndex > -1 || dataColumn.GroupIndex > -1); - case GridContextMenuItemType.ClearGrouping: - return e.Grid.AllowGroup && e.Grid.GetGroupCount() > 1; case GridContextMenuItemType.ClearFilter: - return e.Grid.GetDataColumns().Any(i => i.FilterRowValue != null); - case GridContextMenuItemType.Unfix: - return e.Column.FixedPosition != GridColumnFixedPosition.None; + return e.Grid.GetFilterCriteria() != null ? true : false; } return false; } @@ -305,16 +251,5 @@ static bool IsRowMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMen } return false; } - - static bool GetAllowSort(IGridColumn column, IGrid grid) { - if(column is IGridDataColumn dataColumn) - return dataColumn.AllowSort ?? grid.AllowSort; - return false; - } - static bool GetAllowGroup(IGridColumn column, IGrid grid) { - if(column is IGridDataColumn dataColumn) - return dataColumn.AllowGroup ?? grid.AllowGroup; - return false; - } } } diff --git a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor index e0fed7a..e6ca277 100644 --- a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor +++ b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor @@ -1,9 +1,9 @@ @using GridWithContextMenu.Data @using System.Collections - + @code { - DxContextMenu ColumnContextMenu { get; set; } + DxContextMenu CustomContextMenu { get; set; } DxContextMenu RowContextMenu { get; set; } - IEnumerable ColumnContextMenuData { get; set; } + IEnumerable CustomContextMenuData { get; set; } IEnumerable RowContextMenuData { get; set; } - IGridColumn ContextMenuColumn { get; set; } int ContextMenuRowIndex { get; set; } [Parameter] public IGrid Grid { get; set; } - void ColumnContextMenu_ItemClick(ContextMenuItemClickEventArgs e) - => GridContextMenuHelper.ProcessColumnMenuItemClick((ContextMenuItem)e.ItemInfo.DataItem, ContextMenuColumn, Grid); + void CustomContextMenu_ItemClick(ContextMenuItemClickEventArgs e) + => GridContextMenuHelper.ProcessCustomMenuItemClick((ContextMenuItem)e.ItemInfo.DataItem, Grid); async Task RowContextMenu_ItemClick(ContextMenuItemClickEventArgs e) => await GridContextMenuHelper.ProcessRowMenuItemClickAsync((ContextMenuItem)e.ItemInfo.DataItem, ContextMenuRowIndex, Grid); public async Task Grid_ContextMenu(GridCustomizeElementEventArgs e, MouseEventArgs mouseArgs) { if(GridContextMenuHelper.IsCustomContextMenuElement(e.ElementType)) { - ContextMenuColumn = e.Column; - ColumnContextMenuData = GridContextMenuHelper.GetColumnItems(e); - await ColumnContextMenu.ShowAsync(mouseArgs); + CustomContextMenuData = GridContextMenuHelper.GetCustomItems(e); + await CustomContextMenu.ShowAsync(mouseArgs); } if(GridContextMenuHelper.IsRowContextMenuElement(e.ElementType)) { ContextMenuRowIndex = e.VisibleIndex; diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index b42a1e6..1baf46d 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -61,7 +61,7 @@ void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { if (args.Context is GridDataRowCommandContext rowContext) { if (rowContext.Grid.IsEditing()) { - args.Items.AddCustomItem("Save", async () => { + var save = args.Items.AddCustomItem("Save", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.SaveChangesAsync(); rowContext.Grid.EndUpdate(); From f8d29aaf1d94644f46854c462c3987f5d4298b8b Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Mon, 20 Apr 2026 16:44:32 +0800 Subject: [PATCH 05/12] Implement Export option in context menu --- .../Data/GridContextMenuHelper.cs | 38 ++++++++++++++++--- CS/GridWithContextMenu/wwwroot/css/icons.css | 13 +++++-- .../wwwroot/images/icons/export.svg | 4 ++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 CS/GridWithContextMenu/wwwroot/images/icons/export.svg diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 7f6789a..a1c20fa 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -14,7 +14,7 @@ public enum GridContextMenuItemType { SaveUpdates, CancelUpdates, - ExportXls, ExportXlsx, ExportPdf, ExportDocx + ExportXls, ExportXlsx, ExportPdf, ExportCsv } public class ContextMenuItem { @@ -36,8 +36,12 @@ static List CreateCustomContextMenuItems() { new ContextMenuItem { ItemType = GridContextMenuItemType.ShowColumnChooser, Text = "Column Chooser", IconCssClass="grid-context-menu-item-column-chooser" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ClearFilter, Text = "Clear Filter", BeginGroup = true, IconCssClass="grid-context-menu-item-clear-filter" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFilterRow, Text = "Filter Row", IconCssClass="grid-context-menu-item-filter-row" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFooter, Text = "Footer", IconCssClass="grid-context-menu-item-footer" } - }; + new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFooter, Text = "Footer", IconCssClass="grid-context-menu-item-footer" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ExportCsv, Text = "Export to CSV", BeginGroup = true, IconCssClass="grid-context-menu-item-export" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ExportXlsx, Text = "Export to XLSX", IconCssClass="grid-context-menu-item-export" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ExportXls, Text = "Export to XLS", IconCssClass="grid-context-menu-item-export" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ExportPdf, Text = "Export to PDF", IconCssClass="grid-context-menu-item-export" }, + }; } static List CreateRowContextMenuItems() { return new List { @@ -73,7 +77,8 @@ public static bool IsRowContextMenuElement(GridElementType elementType) { } public static void ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) { - grid.BeginUpdate(); + const string ExportFileName = "ExportResult"; + grid.BeginUpdate(); switch(item.ItemType) { case GridContextMenuItemType.FullExpand: grid.ExpandAllGroupRows(); @@ -98,7 +103,19 @@ public static void ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) case GridContextMenuItemType.ClearFilter: grid.ClearFilter(); break; - } + case GridContextMenuItemType.ExportCsv: + grid.ExportToCsvAsync(ExportFileName); + break; + case GridContextMenuItemType.ExportXlsx: + grid.ExportToXlsxAsync(ExportFileName); + break; + case GridContextMenuItemType.ExportXls: + grid.ExportToXlsAsync(ExportFileName); + break; + case GridContextMenuItemType.ExportPdf: + grid.ExportToPdfAsync(ExportFileName); + break; + } grid.EndUpdate(); } public static async Task ProcessRowMenuItemClickAsync(ContextMenuItem item, int visibleIndex, IGrid grid) { @@ -170,6 +187,11 @@ static bool IsCustomMenuItemVisible(GridCustomizeElementEventArgs e, GridContext case GridContextMenuItemType.ShowColumnChooser: case GridContextMenuItemType.ClearFilter: return true; + case GridContextMenuItemType.ExportCsv: + case GridContextMenuItemType.ExportXlsx: + case GridContextMenuItemType.ExportXls: + case GridContextMenuItemType.ExportPdf: + return e.ElementType == GridElementType.ToolbarContainer; } return false; } @@ -193,7 +215,11 @@ static bool IsCustomMenuItemEnabled(GridCustomizeElementEventArgs e, GridContext case GridContextMenuItemType.ShowFilterRow: case GridContextMenuItemType.ShowFooter: case GridContextMenuItemType.ShowColumnChooser: - return true; + case GridContextMenuItemType.ExportCsv: + case GridContextMenuItemType.ExportXlsx: + case GridContextMenuItemType.ExportXls: + case GridContextMenuItemType.ExportPdf: + return true; case GridContextMenuItemType.ClearFilter: return e.Grid.GetFilterCriteria() != null ? true : false; } diff --git a/CS/GridWithContextMenu/wwwroot/css/icons.css b/CS/GridWithContextMenu/wwwroot/css/icons.css index 9b65aef..1204308 100644 --- a/CS/GridWithContextMenu/wwwroot/css/icons.css +++ b/CS/GridWithContextMenu/wwwroot/css/icons.css @@ -31,7 +31,8 @@ .grid-context-menu-item-collapse-detail-row, .grid-context-menu-item-new-row, .grid-context-menu-item-edit-row, -.grid-context-menu-item-delete-row { +.grid-context-menu-item-delete-row, +.grid-context-menu-item-export { width: 16px; height: 16px; background-repeat: no-repeat; @@ -58,7 +59,8 @@ .dropdown-item:active .grid-context-menu-item-collapse-detail-row, .dropdown-item:active .grid-context-menu-item-new-row, .dropdown-item:active .grid-context-menu-item-edit-row, -.dropdown-item:active .grid-context-menu-item-delete-row { +.dropdown-item:active .grid-context-menu-item-delete-row, +.dropdown-item:active .grid-context-menu-item-export { background-color: white; } @@ -78,7 +80,8 @@ .dropdown-item.disabled .grid-context-menu-item-collapse-detail-row, .dropdown-item.disabled .grid-context-menu-item-new-row, .dropdown-item.disabled .grid-context-menu-item-edit-row, -.dropdown-item.disabled .grid-context-menu-item-delete-row { +.dropdown-item.disabled .grid-context-menu-item-delete-row, +.dropdown-item.disabled .grid-context-menu-item-export { background-color: #c3c2c2; } @@ -166,6 +169,10 @@ mask-image: url(../images/icons/delete-row.svg); -webkit-mask-image: url(../images/icons/delete-row.svg); } +.grid-context-menu-item-export { + mask-image: url(../images/icons/export.svg); + -webkit-mask-image: url(../images/icons/export.svg); +} .grid-context-menu-item-filter-row, diff --git a/CS/GridWithContextMenu/wwwroot/images/icons/export.svg b/CS/GridWithContextMenu/wwwroot/images/icons/export.svg new file mode 100644 index 0000000..d8292c2 --- /dev/null +++ b/CS/GridWithContextMenu/wwwroot/images/icons/export.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 1c9fb2f2fb43a58d560474ae79620b62597c45b7 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Thu, 23 Apr 2026 11:33:52 +0800 Subject: [PATCH 06/12] convert custom click to async function --- .../Data/GridContextMenuHelper.cs | 20 ++++++++++++------- .../Pages/GridContextMenuContainer.razor | 4 ++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index a1c20fa..79b8ef1 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -30,7 +30,8 @@ public class ContextMenuItem { public class GridContextMenuHelper { static List CreateCustomContextMenuItems() { return new List { - new ContextMenuItem { ItemType = GridContextMenuItemType.FullExpand, Text = "Expand All", IconCssClass="grid-context-menu-item-full-expand" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.NewRow, Text = "New", BeginGroup = true, IconCssClass="grid-context-menu-item-new-row" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.FullExpand, Text = "Expand All", IconCssClass="grid-context-menu-item-full-expand" }, new ContextMenuItem { ItemType = GridContextMenuItemType.FullCollapse, Text = "Collapse All", IconCssClass="grid-context-menu-item-full-collapse" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowGroupPanel, Text = "Group Panel", IconCssClass="grid-context-menu-item-show-group-panel" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowColumnChooser, Text = "Column Chooser", IconCssClass="grid-context-menu-item-column-chooser" }, @@ -76,11 +77,14 @@ public static bool IsRowContextMenuElement(GridElementType elementType) { return false; } - public static void ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) { + public static async Task ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) { const string ExportFileName = "ExportResult"; grid.BeginUpdate(); switch(item.ItemType) { - case GridContextMenuItemType.FullExpand: + case GridContextMenuItemType.NewRow: + await grid.StartEditNewRowAsync(); + break; + case GridContextMenuItemType.FullExpand: grid.ExpandAllGroupRows(); break; case GridContextMenuItemType.FullCollapse: @@ -104,16 +108,16 @@ public static void ProcessCustomMenuItemClick(ContextMenuItem item, IGrid grid) grid.ClearFilter(); break; case GridContextMenuItemType.ExportCsv: - grid.ExportToCsvAsync(ExportFileName); + await grid.ExportToCsvAsync(ExportFileName); break; case GridContextMenuItemType.ExportXlsx: - grid.ExportToXlsxAsync(ExportFileName); + await grid.ExportToXlsxAsync(ExportFileName); break; case GridContextMenuItemType.ExportXls: - grid.ExportToXlsAsync(ExportFileName); + await grid.ExportToXlsAsync(ExportFileName); break; case GridContextMenuItemType.ExportPdf: - grid.ExportToPdfAsync(ExportFileName); + await grid.ExportToPdfAsync(ExportFileName); break; } grid.EndUpdate(); @@ -187,6 +191,7 @@ static bool IsCustomMenuItemVisible(GridCustomizeElementEventArgs e, GridContext case GridContextMenuItemType.ShowColumnChooser: case GridContextMenuItemType.ClearFilter: return true; + case GridContextMenuItemType.NewRow: case GridContextMenuItemType.ExportCsv: case GridContextMenuItemType.ExportXlsx: case GridContextMenuItemType.ExportXls: @@ -209,6 +214,7 @@ static bool IsCustomMenuItemSelected(GridCustomizeElementEventArgs e, GridContex } static bool IsCustomMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMenuItemType itemType) { switch(itemType) { + case GridContextMenuItemType.NewRow: case GridContextMenuItemType.FullExpand: case GridContextMenuItemType.FullCollapse: case GridContextMenuItemType.ShowGroupPanel: diff --git a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor index e6ca277..7c69bfc 100644 --- a/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor +++ b/CS/GridWithContextMenu/Pages/GridContextMenuContainer.razor @@ -38,8 +38,8 @@ [Parameter] public IGrid Grid { get; set; } - void CustomContextMenu_ItemClick(ContextMenuItemClickEventArgs e) - => GridContextMenuHelper.ProcessCustomMenuItemClick((ContextMenuItem)e.ItemInfo.DataItem, Grid); + async Task CustomContextMenu_ItemClick(ContextMenuItemClickEventArgs e) + => await GridContextMenuHelper.ProcessCustomMenuItemClick((ContextMenuItem)e.ItemInfo.DataItem, Grid); async Task RowContextMenu_ItemClick(ContextMenuItemClickEventArgs e) => await GridContextMenuHelper.ProcessRowMenuItemClickAsync((ContextMenuItem)e.ItemInfo.DataItem, ContextMenuRowIndex, Grid); From 932cd7df587140f9222bb504ec873ee75a3df035 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Mon, 27 Apr 2026 15:50:59 +0800 Subject: [PATCH 07/12] add icons and footer menu --- .../Data/GridContextMenuHelper.cs | 4 +- CS/GridWithContextMenu/Pages/Index.razor | 76 ++++++++++++++----- CS/GridWithContextMenu/wwwroot/css/icons.css | 4 +- .../wwwroot/images/icons/export-grid.svg | 8 ++ .../wwwroot/images/icons/export.svg | 4 - 5 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 CS/GridWithContextMenu/wwwroot/images/icons/export-grid.svg delete mode 100644 CS/GridWithContextMenu/wwwroot/images/icons/export.svg diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 79b8ef1..1cb7eed 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -35,8 +35,8 @@ static List CreateCustomContextMenuItems() { new ContextMenuItem { ItemType = GridContextMenuItemType.FullCollapse, Text = "Collapse All", IconCssClass="grid-context-menu-item-full-collapse" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowGroupPanel, Text = "Group Panel", IconCssClass="grid-context-menu-item-show-group-panel" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowColumnChooser, Text = "Column Chooser", IconCssClass="grid-context-menu-item-column-chooser" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.ClearFilter, Text = "Clear Filter", BeginGroup = true, IconCssClass="grid-context-menu-item-clear-filter" }, - new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFilterRow, Text = "Filter Row", IconCssClass="grid-context-menu-item-filter-row" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFilterRow, Text = "Filter Row", BeginGroup = true, IconCssClass="grid-context-menu-item-filter-row" }, + new ContextMenuItem { ItemType = GridContextMenuItemType.ClearFilter, Text = "Clear Filter", IconCssClass="grid-context-menu-item-clear-filter" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ShowFooter, Text = "Footer", IconCssClass="grid-context-menu-item-footer" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ExportCsv, Text = "Export to CSV", BeginGroup = true, IconCssClass="grid-context-menu-item-export" }, new ContextMenuItem { ItemType = GridContextMenuItemType.ExportXlsx, Text = "Export to XLSX", IconCssClass="grid-context-menu-item-export" }, diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index 1baf46d..68c589a 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -61,76 +61,110 @@ void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { if (args.Context is GridDataRowCommandContext rowContext) { if (rowContext.Grid.IsEditing()) { - var save = args.Items.AddCustomItem("Save", async () => { + var saveEdit = args.Items.AddCustomItem("Save", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.SaveChangesAsync(); rowContext.Grid.EndUpdate(); }); - args.Items.AddCustomItem("Cancel", async () => { + saveEdit.IconCssClass = "grid-context-menu-item-edit-row"; + + var cancelEdit = args.Items.AddCustomItem("Cancel", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.CancelEditAsync(); rowContext.Grid.EndUpdate(); }); + cancelEdit.IconCssClass = "grid-context-menu-item-delete-row"; } - args.Items.AddCustomItem("New", async () => { + var newRow = args.Items.AddCustomItem("New", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.StartEditNewRowAsync(); rowContext.Grid.EndUpdate(); - }).BeginGroup = true; - args.Items.AddCustomItem("Edit", async () => { + }); + newRow.IconCssClass = "grid-context-menu-item-new-row"; + newRow.BeginGroup = true; + + var editRow = args.Items.AddCustomItem("Edit", async () => { rowContext.Grid.BeginUpdate(); await rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex); rowContext.Grid.EndUpdate(); }); - args.Items.AddCustomItem("Delete", async () => { + editRow.IconCssClass = "grid-context-menu-item-edit-row"; + + var deleteRow = args.Items.AddCustomItem("Delete", () => { rowContext.Grid.BeginUpdate(); rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex); rowContext.Grid.EndUpdate(); }); + deleteRow.IconCssClass = "grid-context-menu-item-delete-row"; } if (args.Context is GridHeaderCommandContext headerContext) { var isFilterRowVisible = headerContext.Grid.ShowFilterRow != false; - string filterRowItemText = isFilterRowVisible ? "Hide Filter Row" : "Show Filter Row"; var newFilterRowState = isFilterRowVisible ? false : true; - args.Items.AddCustomItem(filterRowItemText, () => { + var filterRow = args.Items.AddCustomItem("Filter Row", () => { headerContext.Grid.BeginUpdate(); headerContext.Grid.ShowFilterRow = newFilterRowState; headerContext.Grid.EndUpdate(); }); + filterRow.IconCssClass = "grid-context-menu-item-filter-row"; + filterRow.CssClass = isFilterRowVisible ? "menu-item-selected" : ""; var isFiltered = headerContext.Grid.GetFilterCriteria() != null; - args.Items.AddCustomItem("Clear Filter", () => { + var clearFilter = args.Items.AddCustomItem("Clear Filter", () => { headerContext.Grid.BeginUpdate(); headerContext.Grid.ClearFilter(); headerContext.Grid.EndUpdate(); - }).Enabled = isFiltered ? true : false; + }); + clearFilter.IconCssClass = "grid-context-menu-item-clear-filter"; + clearFilter.Enabled = isFiltered ? true : false; + + var isFooterVisible = headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always + || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; + var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; + var footer = args.Items.AddCustomItem("Footer", () => + { + headerContext.Grid.BeginUpdate(); + headerContext.Grid.FooterDisplayMode = newFooterState; + headerContext.Grid.EndUpdate(); + }); + footer.IconCssClass = "grid-context-menu-item-footer"; + footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; - args.Items.AddCustomItem("Fix Column to the Left", () => { + var fixLeft = args.Items.AddCustomItem("Fix Column to the Left", () => { headerContext.Grid.BeginUpdate(); headerContext.Column.FixedPosition = GridColumnFixedPosition.Left; headerContext.Grid.EndUpdate(); - }).BeginGroup = true; - args.Items.AddCustomItem("Fix Column to the Right", () => { + }); + fixLeft.IconCssClass = "grid-context-menu-item-fix-column-left"; + fixLeft.BeginGroup = true; + + var fixRight = args.Items.AddCustomItem("Fix Column to the Right", () => { headerContext.Grid.BeginUpdate(); headerContext.Column.FixedPosition = GridColumnFixedPosition.Right; headerContext.Grid.EndUpdate(); }); - args.Items.AddCustomItem("Unfix Column", () => { + fixRight.IconCssClass = "grid-context-menu-item-fix-column-right"; + + var unfix = args.Items.AddCustomItem("Unfix Column", () => { headerContext.Grid.BeginUpdate(); headerContext.Column.FixedPosition = GridColumnFixedPosition.None; headerContext.Grid.EndUpdate(); }); + unfix.IconCssClass = "grid-context-menu-item-unfix-column"; + } - var isFooterVisible = headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always - || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; - string footerItemText = isFooterVisible ? "Hide Footer" : "Show Footer"; + if (args.Context is GridFooterCommandContext footerContext) { + var isFooterVisible = footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always + || footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && footerContext.Grid.GetTotalSummaryItems().Count > 0; var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; - args.Items.AddCustomItem(footerItemText, () => { - headerContext.Grid.BeginUpdate(); - headerContext.Grid.FooterDisplayMode = newFooterState; - headerContext.Grid.EndUpdate(); + var footer = args.Items.AddCustomItem("Footer", () => + { + footerContext.Grid.BeginUpdate(); + footerContext.Grid.FooterDisplayMode = newFooterState; + footerContext.Grid.EndUpdate(); }); + footer.IconCssClass = "grid-context-menu-item-footer"; + footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; } } diff --git a/CS/GridWithContextMenu/wwwroot/css/icons.css b/CS/GridWithContextMenu/wwwroot/css/icons.css index 1204308..c313445 100644 --- a/CS/GridWithContextMenu/wwwroot/css/icons.css +++ b/CS/GridWithContextMenu/wwwroot/css/icons.css @@ -170,8 +170,8 @@ -webkit-mask-image: url(../images/icons/delete-row.svg); } .grid-context-menu-item-export { - mask-image: url(../images/icons/export.svg); - -webkit-mask-image: url(../images/icons/export.svg); + mask-image: url(../images/icons/export-grid.svg); + -webkit-mask-image: url(../images/icons/export-grid.svg); } diff --git a/CS/GridWithContextMenu/wwwroot/images/icons/export-grid.svg b/CS/GridWithContextMenu/wwwroot/images/icons/export-grid.svg new file mode 100644 index 0000000..13c6494 --- /dev/null +++ b/CS/GridWithContextMenu/wwwroot/images/icons/export-grid.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/CS/GridWithContextMenu/wwwroot/images/icons/export.svg b/CS/GridWithContextMenu/wwwroot/images/icons/export.svg deleted file mode 100644 index d8292c2..0000000 --- a/CS/GridWithContextMenu/wwwroot/images/icons/export.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From b8d114e939733e6f7bda597517991767a2e9992d Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Mon, 27 Apr 2026 18:07:54 +0400 Subject: [PATCH 08/12] README auto update [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a89a5c4..0ec1054 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/520791644/25.1.3%2B) +![](https://img.shields.io/endpoint?url=https://codecentral.devexpress.com/api/v1/VersionRange/520791644/25.2.5%2B) [![](https://img.shields.io/badge/Open_in_DevExpress_Support_Center-FF7200?style=flat-square&logo=DevExpress&logoColor=white)](https://supportcenter.devexpress.com/ticket/details/T1106945) [![](https://img.shields.io/badge/📖_How_to_use_DevExpress_Examples-e9f6fc?style=flat-square)](https://docs.devexpress.com/GeneralInformation/403183) [![](https://img.shields.io/badge/💬_Leave_Feedback-feecdd?style=flat-square)](#does-this-example-address-your-development-requirementsobjectives) From 53c6d9b92f9427a0994484838322da66bbb882df Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Thu, 30 Apr 2026 15:34:17 +0800 Subject: [PATCH 09/12] refactored Grid_CustomizeContextMenu() --- .../Data/GridContextMenuHelper.cs | 109 ++++++++++++++ CS/GridWithContextMenu/Pages/Index.razor | 134 +----------------- 2 files changed, 116 insertions(+), 127 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 1cb7eed..7e69d8e 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -283,5 +283,114 @@ static bool IsRowMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMen } return false; } + + public static void CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { + if (args.Context is GridDataRowCommandContext rowContext) AddRowItems(args, rowContext); + + if (args.Context is GridHeaderCommandContext headerContext) AddHeaderItems(args, headerContext); + + if (args.Context is GridFooterCommandContext footerContext) AddFooterItems(args, footerContext); + } + + private static void AddRowItems(GridCustomizeContextMenuEventArgs args, GridDataRowCommandContext rowContext) { + var update = (IGridCommandContext c, Action t) => { + c.Grid.BeginUpdate(); + t(); + c.Grid.EndUpdate(); + }; + var updateAsync = async (IGridCommandContext c, Task t) => { + c.Grid.BeginUpdate(); + await t; + c.Grid.EndUpdate(); + }; + + if (rowContext.Grid.IsEditing()) { + var saveEdit = args.Items.AddCustomItem("Save", async () => + await updateAsync(rowContext, rowContext.Grid.SaveChangesAsync())); + saveEdit.IconCssClass = "grid-context-menu-item-edit-row"; + + var cancelEdit = args.Items.AddCustomItem("Cancel", async () => + await updateAsync(rowContext, rowContext.Grid.CancelEditAsync())); + cancelEdit.IconCssClass = "grid-context-menu-item-delete-row"; + } + var newRow = args.Items.AddCustomItem("New", async () => + await updateAsync(rowContext, rowContext.Grid.StartEditNewRowAsync())); + newRow.IconCssClass = "grid-context-menu-item-new-row"; + newRow.BeginGroup = true; + + var editRow = args.Items.AddCustomItem("Edit", async () => + await updateAsync(rowContext, rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex))); + editRow.IconCssClass = "grid-context-menu-item-edit-row"; + + var deleteRow = args.Items.AddCustomItem("Delete", () => + update(rowContext, () => rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex))); + deleteRow.IconCssClass = "grid-context-menu-item-delete-row"; + } + private static void AddHeaderItems(GridCustomizeContextMenuEventArgs args, GridHeaderCommandContext headerContext) { + var update = (IGridCommandContext c, Action t) => { + c.Grid.BeginUpdate(); + t(); + c.Grid.EndUpdate(); + }; + var updateAsync = async (IGridCommandContext c, Task t) => { + c.Grid.BeginUpdate(); + await t; + c.Grid.EndUpdate(); + }; + + var isFilterRowVisible = headerContext.Grid.ShowFilterRow != false; + var newFilterRowState = isFilterRowVisible ? false : true; + var filterRow = args.Items.AddCustomItem("Filter Row", () => + update(headerContext, () => headerContext.Grid.ShowFilterRow = newFilterRowState)); + filterRow.IconCssClass = "grid-context-menu-item-filter-row"; + filterRow.CssClass = isFilterRowVisible ? "menu-item-selected" : ""; + + var isFiltered = headerContext.Grid.GetFilterCriteria() != null; + var clearFilter = args.Items.AddCustomItem("Clear Filter", () => + update(headerContext, () => headerContext.Grid.ClearFilter())); + clearFilter.IconCssClass = "grid-context-menu-item-clear-filter"; + clearFilter.Enabled = isFiltered ? true : false; + + var isFooterVisible = headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always + || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; + var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; + var footer = args.Items.AddCustomItem("Footer", () => + update(headerContext, () => headerContext.Grid.FooterDisplayMode = newFooterState)); + footer.IconCssClass = "grid-context-menu-item-footer"; + footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; + + var fixLeft = args.Items.AddCustomItem("Fix Column to the Left", () => + update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Left)); + fixLeft.IconCssClass = "grid-context-menu-item-fix-column-left"; + fixLeft.BeginGroup = true; + + var fixRight = args.Items.AddCustomItem("Fix Column to the Right", () => + update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Right)); + fixRight.IconCssClass = "grid-context-menu-item-fix-column-right"; + + var unfix = args.Items.AddCustomItem("Unfix Column", () => + update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.None)); + unfix.IconCssClass = "grid-context-menu-item-unfix-column"; + } + private static void AddFooterItems(GridCustomizeContextMenuEventArgs args, GridFooterCommandContext footerContext) { + var update = (IGridCommandContext c, Action t) => { + c.Grid.BeginUpdate(); + t(); + c.Grid.EndUpdate(); + }; + var updateAsync = async (IGridCommandContext c, Task t) => { + c.Grid.BeginUpdate(); + await t; + c.Grid.EndUpdate(); + }; + + var isFooterVisible = footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always + || footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && footerContext.Grid.GetTotalSummaryItems().Count > 0; + var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; + var footer = args.Items.AddCustomItem("Footer", () => + update(footerContext, () => footerContext.Grid.FooterDisplayMode = newFooterState)); + footer.IconCssClass = "grid-context-menu-item-footer"; + footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; + } } } diff --git a/CS/GridWithContextMenu/Pages/Index.razor b/CS/GridWithContextMenu/Pages/Index.razor index 68c589a..dbf22bb 100644 --- a/CS/GridWithContextMenu/Pages/Index.razor +++ b/CS/GridWithContextMenu/Pages/Index.razor @@ -58,116 +58,8 @@ protected override async Task OnInitializedAsync() { GridData = await ForecastService.GetForecastAsync(); } - void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { - if (args.Context is GridDataRowCommandContext rowContext) { - if (rowContext.Grid.IsEditing()) { - var saveEdit = args.Items.AddCustomItem("Save", async () => { - rowContext.Grid.BeginUpdate(); - await rowContext.Grid.SaveChangesAsync(); - rowContext.Grid.EndUpdate(); - }); - saveEdit.IconCssClass = "grid-context-menu-item-edit-row"; - - var cancelEdit = args.Items.AddCustomItem("Cancel", async () => { - rowContext.Grid.BeginUpdate(); - await rowContext.Grid.CancelEditAsync(); - rowContext.Grid.EndUpdate(); - }); - cancelEdit.IconCssClass = "grid-context-menu-item-delete-row"; - } - var newRow = args.Items.AddCustomItem("New", async () => { - rowContext.Grid.BeginUpdate(); - await rowContext.Grid.StartEditNewRowAsync(); - rowContext.Grid.EndUpdate(); - }); - newRow.IconCssClass = "grid-context-menu-item-new-row"; - newRow.BeginGroup = true; - - var editRow = args.Items.AddCustomItem("Edit", async () => { - rowContext.Grid.BeginUpdate(); - await rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex); - rowContext.Grid.EndUpdate(); - }); - editRow.IconCssClass = "grid-context-menu-item-edit-row"; - - var deleteRow = args.Items.AddCustomItem("Delete", () => { - rowContext.Grid.BeginUpdate(); - rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex); - rowContext.Grid.EndUpdate(); - }); - deleteRow.IconCssClass = "grid-context-menu-item-delete-row"; - } - - if (args.Context is GridHeaderCommandContext headerContext) { - var isFilterRowVisible = headerContext.Grid.ShowFilterRow != false; - var newFilterRowState = isFilterRowVisible ? false : true; - var filterRow = args.Items.AddCustomItem("Filter Row", () => { - headerContext.Grid.BeginUpdate(); - headerContext.Grid.ShowFilterRow = newFilterRowState; - headerContext.Grid.EndUpdate(); - }); - filterRow.IconCssClass = "grid-context-menu-item-filter-row"; - filterRow.CssClass = isFilterRowVisible ? "menu-item-selected" : ""; - - var isFiltered = headerContext.Grid.GetFilterCriteria() != null; - var clearFilter = args.Items.AddCustomItem("Clear Filter", () => { - headerContext.Grid.BeginUpdate(); - headerContext.Grid.ClearFilter(); - headerContext.Grid.EndUpdate(); - }); - clearFilter.IconCssClass = "grid-context-menu-item-clear-filter"; - clearFilter.Enabled = isFiltered ? true : false; - - var isFooterVisible = headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always - || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; - var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; - var footer = args.Items.AddCustomItem("Footer", () => - { - headerContext.Grid.BeginUpdate(); - headerContext.Grid.FooterDisplayMode = newFooterState; - headerContext.Grid.EndUpdate(); - }); - footer.IconCssClass = "grid-context-menu-item-footer"; - footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; - - var fixLeft = args.Items.AddCustomItem("Fix Column to the Left", () => { - headerContext.Grid.BeginUpdate(); - headerContext.Column.FixedPosition = GridColumnFixedPosition.Left; - headerContext.Grid.EndUpdate(); - }); - fixLeft.IconCssClass = "grid-context-menu-item-fix-column-left"; - fixLeft.BeginGroup = true; - - var fixRight = args.Items.AddCustomItem("Fix Column to the Right", () => { - headerContext.Grid.BeginUpdate(); - headerContext.Column.FixedPosition = GridColumnFixedPosition.Right; - headerContext.Grid.EndUpdate(); - }); - fixRight.IconCssClass = "grid-context-menu-item-fix-column-right"; - - var unfix = args.Items.AddCustomItem("Unfix Column", () => { - headerContext.Grid.BeginUpdate(); - headerContext.Column.FixedPosition = GridColumnFixedPosition.None; - headerContext.Grid.EndUpdate(); - }); - unfix.IconCssClass = "grid-context-menu-item-unfix-column"; - } - - if (args.Context is GridFooterCommandContext footerContext) { - var isFooterVisible = footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always - || footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && footerContext.Grid.GetTotalSummaryItems().Count > 0; - var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; - var footer = args.Items.AddCustomItem("Footer", () => - { - footerContext.Grid.BeginUpdate(); - footerContext.Grid.FooterDisplayMode = newFooterState; - footerContext.Grid.EndUpdate(); - }); - footer.IconCssClass = "grid-context-menu-item-footer"; - footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; - } - } + void Grid_CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) => GridContextMenuHelper.CustomizeContextMenu(args); void Grid_CustomizeElement(GridCustomizeElementEventArgs e) { if(GridContextMenuHelper.IsContextMenuElement(e.ElementType)) { @@ -188,22 +80,10 @@ } } - async Task NewItem_Click() { - await Grid.StartEditNewRowAsync(); - } - void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) { - Grid.ShowColumnChooser(); - } - async Task ExportXlsxItem_Click() { - await Grid.ExportToXlsxAsync(ExportFileName); - } - async Task ExportXlsItem_Click() { - await Grid.ExportToXlsAsync(ExportFileName); - } - async Task ExportCsvItem_Click() { - await Grid.ExportToCsvAsync(ExportFileName); - } - async Task ExportPdfItem_Click() { - await Grid.ExportToPdfAsync(ExportFileName); - } + async Task NewItem_Click() => await Grid.StartEditNewRowAsync(); + void ColumnChooserItem_Click(ToolbarItemClickEventArgs e) => Grid.ShowColumnChooser(); + async Task ExportXlsxItem_Click() => await Grid.ExportToXlsxAsync(ExportFileName); + async Task ExportXlsItem_Click() => await Grid.ExportToXlsAsync(ExportFileName); + async Task ExportCsvItem_Click() => await Grid.ExportToCsvAsync(ExportFileName); + async Task ExportPdfItem_Click() => await Grid.ExportToPdfAsync(ExportFileName); } \ No newline at end of file From dc92ce6dae969ed778353860959708d95388ca0d Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Tue, 12 May 2026 15:24:11 +0400 Subject: [PATCH 10/12] Add launchSettings.json [skip ci] --- .../Properties/launchSettings.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 CS/GridWithContextMenu/Properties/launchSettings.json diff --git a/CS/GridWithContextMenu/Properties/launchSettings.json b/CS/GridWithContextMenu/Properties/launchSettings.json new file mode 100644 index 0000000..e962168 --- /dev/null +++ b/CS/GridWithContextMenu/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "GridWithContextMenu": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:2159;http://localhost:2160" + } + } +} \ No newline at end of file From e02ffd2a5a6d48df5943969607fb74299b1b7a53 Mon Sep 17 00:00:00 2001 From: iancaringal-devexpress Date: Wed, 13 May 2026 17:46:43 +0800 Subject: [PATCH 11/12] refactored code --- .../Data/GridContextMenuHelper.cs | 96 +++++++++---------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs index 7e69d8e..d9cb9b9 100644 --- a/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs +++ b/CS/GridWithContextMenu/Data/GridContextMenuHelper.cs @@ -285,69 +285,50 @@ static bool IsRowMenuItemEnabled(GridCustomizeElementEventArgs e, GridContextMen } public static void CustomizeContextMenu(GridCustomizeContextMenuEventArgs args) { - if (args.Context is GridDataRowCommandContext rowContext) AddRowItems(args, rowContext); + if (args.Context is GridDataRowCommandContext rowContext) + AddRowItems(args, rowContext); - if (args.Context is GridHeaderCommandContext headerContext) AddHeaderItems(args, headerContext); + if (args.Context is GridHeaderCommandContext headerContext) + AddHeaderItems(args, headerContext); - if (args.Context is GridFooterCommandContext footerContext) AddFooterItems(args, footerContext); + if (args.Context is GridFooterCommandContext footerContext) + AddFooterItems(args, footerContext); } private static void AddRowItems(GridCustomizeContextMenuEventArgs args, GridDataRowCommandContext rowContext) { - var update = (IGridCommandContext c, Action t) => { - c.Grid.BeginUpdate(); - t(); - c.Grid.EndUpdate(); - }; - var updateAsync = async (IGridCommandContext c, Task t) => { - c.Grid.BeginUpdate(); - await t; - c.Grid.EndUpdate(); - }; - if (rowContext.Grid.IsEditing()) { var saveEdit = args.Items.AddCustomItem("Save", async () => - await updateAsync(rowContext, rowContext.Grid.SaveChangesAsync())); + await UpdateAsync(rowContext, rowContext.Grid.SaveChangesAsync())); saveEdit.IconCssClass = "grid-context-menu-item-edit-row"; var cancelEdit = args.Items.AddCustomItem("Cancel", async () => - await updateAsync(rowContext, rowContext.Grid.CancelEditAsync())); + await UpdateAsync(rowContext, rowContext.Grid.CancelEditAsync())); cancelEdit.IconCssClass = "grid-context-menu-item-delete-row"; } var newRow = args.Items.AddCustomItem("New", async () => - await updateAsync(rowContext, rowContext.Grid.StartEditNewRowAsync())); + await UpdateAsync(rowContext, rowContext.Grid.StartEditNewRowAsync())); newRow.IconCssClass = "grid-context-menu-item-new-row"; newRow.BeginGroup = true; var editRow = args.Items.AddCustomItem("Edit", async () => - await updateAsync(rowContext, rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex))); + await UpdateAsync(rowContext, rowContext.Grid.StartEditRowAsync(rowContext.RowVisibleIndex))); editRow.IconCssClass = "grid-context-menu-item-edit-row"; - var deleteRow = args.Items.AddCustomItem("Delete", () => - update(rowContext, () => rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex))); + var deleteRow = args.Items.AddCustomItem("Delete", () => + Update(rowContext, () => rowContext.Grid.ShowRowDeleteConfirmation(rowContext.RowVisibleIndex))); deleteRow.IconCssClass = "grid-context-menu-item-delete-row"; } private static void AddHeaderItems(GridCustomizeContextMenuEventArgs args, GridHeaderCommandContext headerContext) { - var update = (IGridCommandContext c, Action t) => { - c.Grid.BeginUpdate(); - t(); - c.Grid.EndUpdate(); - }; - var updateAsync = async (IGridCommandContext c, Task t) => { - c.Grid.BeginUpdate(); - await t; - c.Grid.EndUpdate(); - }; - var isFilterRowVisible = headerContext.Grid.ShowFilterRow != false; var newFilterRowState = isFilterRowVisible ? false : true; var filterRow = args.Items.AddCustomItem("Filter Row", () => - update(headerContext, () => headerContext.Grid.ShowFilterRow = newFilterRowState)); + Update(headerContext, () => headerContext.Grid.ShowFilterRow = newFilterRowState)); filterRow.IconCssClass = "grid-context-menu-item-filter-row"; filterRow.CssClass = isFilterRowVisible ? "menu-item-selected" : ""; var isFiltered = headerContext.Grid.GetFilterCriteria() != null; var clearFilter = args.Items.AddCustomItem("Clear Filter", () => - update(headerContext, () => headerContext.Grid.ClearFilter())); + Update(headerContext, () => headerContext.Grid.ClearFilter())); clearFilter.IconCssClass = "grid-context-menu-item-clear-filter"; clearFilter.Enabled = isFiltered ? true : false; @@ -355,42 +336,57 @@ private static void AddHeaderItems(GridCustomizeContextMenuEventArgs args, GridH || headerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && headerContext.Grid.GetTotalSummaryItems().Count > 0; var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; var footer = args.Items.AddCustomItem("Footer", () => - update(headerContext, () => headerContext.Grid.FooterDisplayMode = newFooterState)); + Update(headerContext, () => headerContext.Grid.FooterDisplayMode = newFooterState)); footer.IconCssClass = "grid-context-menu-item-footer"; footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; var fixLeft = args.Items.AddCustomItem("Fix Column to the Left", () => - update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Left)); + Update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Left)); fixLeft.IconCssClass = "grid-context-menu-item-fix-column-left"; fixLeft.BeginGroup = true; var fixRight = args.Items.AddCustomItem("Fix Column to the Right", () => - update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Right)); + Update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.Right)); fixRight.IconCssClass = "grid-context-menu-item-fix-column-right"; var unfix = args.Items.AddCustomItem("Unfix Column", () => - update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.None)); + Update(headerContext, () => headerContext.Column.FixedPosition = GridColumnFixedPosition.None)); unfix.IconCssClass = "grid-context-menu-item-unfix-column"; } private static void AddFooterItems(GridCustomizeContextMenuEventArgs args, GridFooterCommandContext footerContext) { - var update = (IGridCommandContext c, Action t) => { - c.Grid.BeginUpdate(); - t(); - c.Grid.EndUpdate(); - }; - var updateAsync = async (IGridCommandContext c, Task t) => { - c.Grid.BeginUpdate(); - await t; - c.Grid.EndUpdate(); - }; - var isFooterVisible = footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Always || footerContext.Grid.FooterDisplayMode == GridFooterDisplayMode.Auto && footerContext.Grid.GetTotalSummaryItems().Count > 0; var newFooterState = isFooterVisible ? GridFooterDisplayMode.Never : GridFooterDisplayMode.Always; var footer = args.Items.AddCustomItem("Footer", () => - update(footerContext, () => footerContext.Grid.FooterDisplayMode = newFooterState)); + Update(footerContext, () => footerContext.Grid.FooterDisplayMode = newFooterState)); footer.IconCssClass = "grid-context-menu-item-footer"; footer.CssClass = isFooterVisible ? "menu-item-selected" : ""; } - } + + public static void Update(IGridCommandContext context, Action t) + { + context.Grid.BeginUpdate(); + try + { + t(); + } + finally + { + context.Grid.EndUpdate(); + } + } + + public static async Task UpdateAsync(IGridCommandContext context, Task t) + { + context.Grid.BeginUpdate(); + try + { + await t; + } + finally + { + context.Grid.EndUpdate(); + } + } + } } From ad2cd287c3e06cdb37f4ab63f49694dfb5678da5 Mon Sep 17 00:00:00 2001 From: DevExpressExampleBot Date: Thu, 14 May 2026 19:05:30 +0400 Subject: [PATCH 12/12] Change LICENSE [skip ci] --- LICENSE | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index cd4c9a2..7536adc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ -This code example is provided "as is" without warranty of any kind. Developer Express Inc ("DevExpress") disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. +This code example is provided "as is" without warranty of any kind. Developer Express Inc ("DevExpress") disclaims all warranties, +either express or implied, including the warranties of merchantability and fitness for a particular purpose. -For licensing terms and conditions of DevExpress product(s) required for, or associated with the use of this code example, please refer to the applicable End-User License Agreement at https://www.devexpress.com/Support/licensingfaq.xml \ No newline at end of file +For terms and conditions governing use of DevExpress product libraries, including product libraries used in this sample, +please review the applicable DevExpress End-User License Agreement at the following address: https://www.devexpress.com/Support/licensingfaq.xml \ No newline at end of file