Overview
DataGrid displays data in a tabular format with support for:
- Columns — customizable column definitions with abstract cell rendering
- Index column — optional row-number column
- Header row — clickable header buttons excluded from selection
- Data binding — reflection-based property binding from any
IList - Column resizing — interactive drag-to-resize on header boundaries
- Vertical scrolling — automatic scrollbar with mouse wheel and touch-drag support
- Selection — row, column, or cell selection modes
- Sorting — interactive header-click sorting with ascending/descending indicators
- Filtering — per-column text filter inputs with configurable string comparison
- Styling — fully customizable through
DataGridStyleandStylesheet
Columns
Columns are defined by creating an array of DataGridColumnBase subclasses. The base class is abstract. The built-in DataGridTextColumn renders cells as text.
| Property | Description |
|---|---|
Header |
Text displayed in the column header |
Property |
Name of the public field or property on the data object to display |
Width |
Initial width in pixels |
HeaderHorizontalAlignment |
Horizontal alignment of this column's header text; when null, the grid-wide DataGrid.HeaderHorizontalAlignment is used |
CellHorizontalAlignment |
Horizontal alignment of this column's cells; when null, the grid-wide DataGrid.CellHorizontalAlignment is used |
DataGridTextColumn adds:
| Property | Description |
|---|---|
Format |
Optional format string applied to the value (e.g. "{0:C2}"). When null, ToString() is used. |
DataGridImageColumn adds:
| Property | Description |
|---|---|
ImageWidth |
Width of the rendered image thumbnail in pixels (default 32) |
ImageHeight |
Height of the rendered image thumbnail in pixels (default 32) |
Additional built-in column types are provided:
| Type | Description | Flags |
|---|---|---|
DataGridTextColumn |
Renders cell values as text | DataGridColumnFlags.All |
DataGridCheckBoxColumn |
Renders boolean values as read-only check boxes; throws when bound to a non-boolean value | DataGridColumnFlags.CanSort |
DataGridImageColumn |
Renders IImage values as image thumbnails; throws when bound to a non-image value |
DataGridColumnFlags.None |
Both text and check box columns provide convenience constructors:
// Using object initializer
var col = new DataGridTextColumn { Header = "Name", Property = "Name", Width = 120 };
// Using constructor with property, header, width, and optional format
var col2 = new DataGridTextColumn("Price", "Price", 100, "{0:C2}");
// Using constructor with property and width only (no header)
var col3 = new DataGridTextColumn("Name", 120);
// Check box column bound to an Active boolean property
var col4 = new DataGridCheckBoxColumn("Active", "Active", 60);
Index Column
DataGrid optionally displays a row-number index column as the first column (enabled by default). Set IndexColumnWidth to null to hide it.
| Property | Description |
|---|---|
IndexColumnWidth |
Width of the index column in pixels (default 50); set to null to hide the index column |
IndexColumnHeaderHorizontalAlignment |
Horizontal alignment of the index column header text; when null, HeaderHorizontalAlignment is used |
IndexColumnCellHorizontalAlignment |
Horizontal alignment of the index column cell numbers; when null, CellHorizontalAlignment is used |
var dataGrid = new DataGrid
{
IndexColumnWidth = null // Hide the index column
};
Custom Columns
Subclass DataGridColumnBase, override CreateWidget to control how each cell is rendered, and override Flags to declare which grid capabilities the column supports:
public class DataGridBoolColumn : DataGridColumnBase
{
public override DataGridColumnFlags Flags => DataGridColumnFlags.None;
public override Widget CreateWidget(object value, DataGridStyle style)
{
if (value == null)
return null;
return new CheckButton
{
IsChecked = (bool)value,
Enabled = false
};
}
}
CreateWidget receives the current DataGridStyle so custom cells can respect the active stylesheet. Use the style-driven helpers (Widget.ApplyWidgetStyle, ButtonBase.ApplyButtonStyle, LabelWidget.ApplyLabelStyle, ImageButton.ApplyImageButtonStyle) with the style's TextCellStyle, CheckCellStyle, ImageCellStyle, and HeaderStyle members:
public override Widget CreateWidget(object value, DataGridStyle style)
{
if (value == null)
return null;
var label = new Label { Text = value.ToString() };
label.ApplyLabelStyle(style.TextCellStyle);
return label;
}
Binding Data
Assign a collection to the Data property. Each object becomes a row and its property values are resolved by name via reflection.
var dataGrid = new DataGrid
{
Columns = new DataGridColumnBase[]
{
new DataGridTextColumn { Header = "First Name", Property = "FirstName", Width = 120 },
new DataGridTextColumn { Header = "Last Name", Property = "LastName", Width = 120 },
new DataGridTextColumn { Header = "Company", Property = "Company", Width = 200 },
},
Data = GetCustomers() // IList of objects
};
Note. Property must match a public instance field or property name on the object exactly.
After modifying a bound property on an existing data row, call InvalidateDataRow to re-read the property values and refresh the display for that row:
// Modify a row in-place
var customer = (Customer)dataGrid.Data[3];
customer.Company = "New Company Name";
dataGrid.InvalidateDataRow(3);
Column Resizing
When ResizableColumns is true (default), columns can be resized interactively by dragging the boundary between two header cells.
By default a fill column is appended after all data columns and takes up any remaining horizontal space. Setting FillColumnIndex to a column index makes that column fill the remaining space instead. When dragging the boundary adjacent to the fill column, the neighbouring fixed-width column also resizes so the fill column remains flexible.
The width of the hit-test area around a column boundary is controlled by ColumnResizeHandleWidth (default 4 pixels), and the minimum column width during resizing is MinColumnWidth (default 20 pixels).
Scrolling
DataGrid provides a vertical scrollbar when the data exceeds the visible area. Mouse wheel and touch-drag scrolling are supported.
| Property | Description |
|---|---|
ScrollMultiplier |
Rows scrolled per mouse wheel tick (default 10) |
The scrollbar appearance is controlled through DataGridStyle.VerticalScrollBackground and DataGridStyle.VerticalScrollKnob (see Styling).
Filtering
When HasFilter is true (default), a filter row is displayed below the header. Each column that supports filtering (e.g. DataGridTextColumn) shows a text input where you can type to filter rows. Set HasFilter = false to hide filter inputs.
| Property | Description |
|---|---|
HasFilter |
Whether filter inputs are displayed below the header (default true) |
FilterStringComparison |
Default string comparison used when matching filter text against cell values (default CurrentCultureIgnoreCase) |
A column type must declare the DataGridColumnFlags.CanFilter flag in its Flags override to support filtering. DataGridTextColumn does this by default. Custom columns that don't support filtering return DataGridColumnFlags.None:
public class DataGridBoolColumn : DataGridColumnBase
{
public override DataGridColumnFlags Flags => DataGridColumnFlags.None;
...
}
Per-column filtering is controlled by the Filter property on each column:
// Programmatically filter the "Country" column to show only rows containing "US"
countryColumn.Filter = "US";
// Clear the filter
countryColumn.Filter = null;
Each column can be individually enabled or disabled for filtering via HasFilter, and can override the DataGrid-level string comparison via FilterStringComparison:
// Disable filter input for this column even though DataGrid.HasFilter is true
countryColumn.HasFilter = false;
// Use case-sensitive matching for this column only
countryColumn.FilterStringComparison = StringComparison.Ordinal;
Selection
Set GridSelectionMode to control selection behavior.
| Value | Description |
|---|---|
None |
Selection disabled |
Row |
Entire rows can be selected |
Column |
Entire columns can be selected |
Cell |
Individual cells can be selected |
| Property | Description |
|---|---|
SelectedRowIndex |
Zero-based index of the selected data row, or null when no row is selected |
SelectedItem |
The data object of the selected row, or null when no row is selected |
SelectedIndexChanged |
Event raised when the selected row index changes |
HoverIndexCanBeNull |
Whether the hover highlight clears when the pointer leaves the grid (default true) |
CanSelectNothing |
Whether clicking an already-selected row deselects it (default false) |
Sorting
When SortableHeaders is true (default), clicking a header cell sorts the data by that column. Clicking the same header again reverses the sort direction. Set SortableHeaders = false to disable interactive sorting while still allowing programmatic sorting.
| Property | Description |
|---|---|
SortableHeaders |
Whether header cells are clickable and trigger sorting (default true) |
SortColumn |
Zero-based index of the column being sorted, or null when no sort is applied |
SortDirection |
Ascending or Descending (default Ascending) |
The sort indicator images are configured through DataGridStyle.HeaderStyle.SortAscendingImage and DataGridStyle.HeaderStyle.SortDescendingImage (see Styling).
A column only becomes sortable when it declares the DataGridColumnFlags.CanSort flag in its Flags override and HasSorting is true. Each column can be individually disabled for sorting via HasSorting even when sorting is globally enabled:
// Disable interactive sorting for the "Image" column
imageColumn.HasSorting = false;
Sorting can also be applied programmatically:
dataGrid.SortColumn = 2; // Sort by the third column
dataGrid.SortDirection = ListSortDirection.Descending;
Header Row
The first row is always treated as a header. It is excluded from hover highlighting and selection and is always visible regardless of the scroll position. Header cells are rendered as buttons; when SortableHeaders is enabled and the column supports sorting (declared via Flags and HasSorting), clicking a header cell sorts the data by that column.
Styling
DataGrid look and feel is controlled through a DataGridStyle, which inherits from GridStyle, assigned via the active Stylesheet.
| Property | Description |
|---|---|
HeaderStyle |
DataGridHeaderStyle applied to header cells, including the label style, sort indicator images and spacing, and content padding |
FilterStyle |
TextBoxStyle applied to the filter row text inputs |
TextCellStyle |
LabelStyle applied to text cell content |
CheckCellStyle |
ImageButtonStyle applied to check box cell content |
ImageCellStyle |
WidgetStyle applied to image cell content |
ShowGridLines |
Whether grid lines are drawn between cells |
GridLinesColor |
Color of the grid lines |
ColumnSpacing |
Horizontal padding inside each cell |
RowSpacing |
Vertical padding inside each cell |
SelectionBackground |
Brush used for the selected row/column/cell |
SelectionHoverBackground |
Brush used for the hovered row/column/cell |
VerticalScrollBackground |
Image for the scrollbar track |
VerticalScrollKnob |
Image for the scrollbar thumb |
Apply a custom style in code (before creating any DataGrid instances):
var dataGridStyle = Stylesheet.Current.DataGridStyle;
dataGridStyle.ShowGridLines = true;
dataGridStyle.GridLinesColor = Color.Gray;
dataGridStyle.SelectionHoverBackground = new SolidBrush(Color.FromArgb(40, Color.White));
dataGridStyle.HeaderStyle.SortAscendingImage = mySortUpImage;
dataGridStyle.TextCellStyle.TextColor = Color.White;
Example
The following example displays a list of customer records:
using Myra.Graphics2D.UI;
using Myra.Graphics2D.UI.Data;
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Company { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
// Build data
var customers = new List<Customer>
{
new Customer { FirstName = "Alice", LastName = "Smith", Company = "Acme", City = "Portland", Country = "US" },
new Customer { FirstName = "Bob", LastName = "Jones", Company = "Globex", City = "Seattle", Country = "US" },
};
// Create the DataGrid
var dataGrid = new DataGrid
{
Columns = new DataGridColumnBase[]
{
new DataGridTextColumn { Header = "First Name", Property = "FirstName", Width = 120 },
new DataGridTextColumn { Header = "Last Name", Property = "LastName", Width = 120 },
new DataGridTextColumn { Header = "Company", Property = "Company", Width = 200 },
new DataGridTextColumn { Header = "City", Property = "City", Width = 150 },
new DataGridTextColumn { Header = "Country", Property = "Country", Width = 100 },
},
Data = customers
};
_desktop = new Desktop { Root = dataGrid };
Full sample is available here: https://github.com/rds1983/Myra/tree/master/samples/Myra.Samples.DataGrid