| layout | post |
|---|---|
| title | Unbound Rows in WPF DataGrid control | Syncfusion® |
| description | Learn here all about Unbound Rows support in Syncfusion® WPF DataGrid (SfDataGrid) control, its elements and more. |
| platform | wpf |
| control | SfDataGrid |
| documentation | ug |
WPF DataGrid allows you to add additional rows at top and also bottom of the SfDataGrid which are not bound with data object from underlying data source. You can add unbound rows using SfDataGrid.UnBoundRows collection property. You can add any no of unbound rows to SfDataGrid. Unbound rows can be exported and printed.
{% tabs %} {% highlight xaml %} <syncfusion:SfDataGrid x:Name="dataGrid" ItemsSource="{Binding Orders}"> syncfusion:SfDataGrid.UnBoundRows <syncfusion:GridUnBoundRow Position="Top"/> </syncfusion:SfDataGrid.UnBoundRows> </syncfusion:SfDataGrid> {% endhighlight %} {% highlight c# %} this.dataGrid.UnBoundRows.Add(new GridUnBoundRow() {Position = UnBoundRowsPosition.Top}); {% endhighlight %} {% endtabs %}
N> AllowFrozenGroupHeaders is not supported with unbound rows.
Unbound row can be placed in top or bottom of the SfDataGrid. Unbound row positioned based on GridUnBoundRow.Position and GridUnBoundRow.ShowBelowSummary properties.
Below table shows the unbound row positioning based on property settings of Position and ShowBelowSummary.
| UnBoundRowPosition | ShowBelowSummary | Position in DataGrid |
|---|---|---|
| Top | True | Unbound row placed at top, right above the record rows. In this position, unbound row is selectable and editable. |
| Top | False | Unbound row placed at top, right next to Header row. In this position, unbound row is not selectable, not editable and frozen when scrolling. |
| Bottom | True | Unbound row placed at bottom of SfDataGrid. In this position, unbound row is not selectable, not editable and frozen when scrolling. |
| Bottom | False | Unbound row placed at bottom, right below record rows. In this position, unbound row is selectable and editable. |
Below screen shot shows different unbound rows placed in all possible positions.
You can populate data for the unbound row by handling QueryUnBoundRow event of SfDataGrid. This event occurs for each cell in unbound row whenever the row gets refreshed.
GridUnBoundRowEventsArgs of the QueryUnBoundRow event provides information about the cell triggered this event. GridUnBoundRowEventsArgs.OriginalSender returns the DataGrid fired this event for DetailsView.
You can get or set the GridUnBoundRowEventsArgs.Value property based on the UnBoundAction. If UnBoundAction is QueryData then you can set the value for display. If the UnBoundAction is CommitData then you can get the edited value.
{% tabs %}
{% highlight xaml %}
<syncfusion:SfDataGrid x:Name="dataGrid"
ItemsSource="{Binding Orders}"
SelectionMode="Multiple" >
syncfusion:SfDataGrid.UnBoundRows
<syncfusion:GridUnBoundRow Position="Top"/>
</syncfusion:SfDataGrid.UnBoundRows>
</syncfusion:SfDataGrid>
{% endhighlight %}
{% endtabs %}
For example, now unbound row populated based on selected items in SfDataGrid.
{% tabs %} {% highlight c# %} this.dataGrid.SelectedItems.Add(collection[4]); this.dataGrid.SelectedItems.Add(collection[5]); this.dataGrid.SelectedItems.Add(collection[7]); dataGrid.QueryUnBoundRow += dataGrid_QueryUnBoundRow;
void dataGrid_QueryUnBoundRow(object sender, GridUnBoundRowEventsArgs e) {
if (e.UnBoundAction == UnBoundActions.QueryData)
{
if(e.RowColumnIndex.ColumnIndex == 0)
{
e.Value = (dataGrid.SelectedItems.OrderBy(item => (item as OrderInfo).OrderID).Last() as OrderInfo).OrderID;
e.Handled = true;
}
else if(e.RowColumnIndex.ColumnIndex == 2)
{
e.Value = (dataGrid.SelectedItems.First(item => (item as OrderInfo).CustomerName.Contains("g")) as OrderInfo).CustomerName;
e.Handled = true;
}
}
} {% endhighlight %} {% endtabs %}
You can add or remove unbound rows using SfDataGrid.UnBoundRows property which reflects in UI immediately.
You can trigger the QueryUnBoundRow event for the unbound row cells at runtime by invalidating the unbound row by calling SfDataGrid.InValidateUnBoundRow method and invalidating the VisualContainer by calling InvalidateMeasureInfo method.
{% tabs %}
{% highlight c# %}
using Syncfusion.UI.Xaml.Grid.Helpers;
dataGrid.InValidateUnBoundRow(dataGrid.UnBoundRows[0]);
dataGrid.GetVisualContainer().InvalidateMeasureInfo();
{% endhighlight %}
{% endtabs %}
You can cancel the editing of unbound row cell by handling the SfDataGrid.CurrentCellBeginEdit event with the help of SfDataGrid.GetUnBoundRow method and row index.
{% tabs %} {% highlight c# %} using Syncfusion.UI.Xaml.Grid; dataGrid.CurrentCellBeginEdit += dataGrid_CurrentCellBeginEdit;
void dataGrid_CurrentCellBeginEdit(object sender, CurrentCellBeginEditEventArgs args)
{
var unboundRow = dataGrid.GetUnBoundRow(args.RowColumnIndex.RowIndex);
if (unboundRow == null)
return;
args.Cancel = true;
} {% endhighlight %} {% endtabs %}
You can get the edited value of unbound row cell from GridUnBoundRowEventsArgs.Value property of QueryUnBoundRow event when UnBoundAction is CommitData.
{% tabs %} {% highlight c# %} void dataGrid_QueryUnBoundRow(object sender, GridUnBoundRowEventsArgs e) {
if (e.UnBoundAction == UnBoundActions.CommitData)
{
var editedValue = e.Value;
}
} {% endhighlight %} {% endtabs %}
You can customize the style of unbound row by writing style of TargetType UnBoundRowControl or setting SfDataGrid.UnBoundRowStyle property.
{% tabs %}
{% highlight xaml %}
<Window.Resources>
<Style TargetType="syncfusion:UnBoundRowControl">
</Style>
</Window.Resources>
<syncfusion:SfDataGrid x:Name="dataGrid"
ItemsSource="{Binding Orders}"
SelectionMode="Multiple">
syncfusion:SfDataGrid.UnBoundRows
<syncfusion:GridUnBoundRow Position="Top"/>
</syncfusion:SfDataGrid.UnBoundRows>
</syncfusion:SfDataGrid>
{% endhighlight %}
{% endtabs %}
You can customize the style of unbound row cell by writing style of TargetType GridUnBoundRowCell or setting SfDataGrid.UnBoundRowCellStyle property.
{% tabs %}
{% highlight xaml %}
<Window.Resources>
<local:UnboundCellStyleConverter x:Key="unboundRowCellStyleConverter"/>
<Style TargetType="syncfusion:GridUnBoundRowCell">
</Style>
</Window.Resources>
<syncfusion:SfDataGrid x:Name="dataGrid"
ItemsSource="{Binding Orders}"
SelectionMode="Multiple">
syncfusion:SfDataGrid.UnBoundRows
<syncfusion:GridUnBoundRow Position="Top"/>
</syncfusion:SfDataGrid.UnBoundRows>
</syncfusion:SfDataGrid>
{% endhighlight %}
{% highlight c# %}
public class UnboundCellStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var unboundRowCell = value as GridUnBoundRowCell;
if (unboundRowCell == null || unboundRowCell.ColumnBase.GridUnBoundRowEventsArgs.Value == null) return null;
if(unboundRowCell.ColumnBase.GridUnBoundRowEventsArgs.Value.ToString().Contains("g"))
return new SolidColorBrush(Colors.Red);
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
} {% endhighlight %} {% endtabs %}
SfDataGrid allows you to customize the operations like key navigation and UI related interactions by overriding the corresponding renderer associated with the unbound row cell. Each renderer have set of virtual methods for handling the behaviors. Creating new renderers also supported.
Below table lists the available cell types for unbound row and its renderers.
| Cell Type | Renderer |
|---|---|
| UnBoundTemplateColumn | {{'[GridUnBoundRowCellTemplateRenderer](https://help.syncfusion.com/cr/wpf/Syncfusion.UI.Xaml.Grid.Cells.GridUnBoundRowCellTemplateRenderer.html)'| markdownify }} |
| UnBoundTextColumn | {{'[GridUnBoundRowCellTextBoxRenderer](https://help.syncfusion.com/cr/wpf/Syncfusion.UI.Xaml.Grid.Cells.GridUnBoundRowCellTextBoxRenderer.html)'| markdownify }} |
The renderer of unbound row cell defined by GridUnBoundRowEventsArgs.CellType property in the QueryUnBoundRow event. If the GridUnBoundRowEventsArgs.CellType not defined then the UnboundTextColumn set as default cell type of GridUnBoundRowCell.
If GridUnBoundRowEventsArgs.CellTemplate and GridUnBoundRowEventsArgs.EditTemplate properties defined then UnBoundTemplateColumn set as cell type of GridUnBoundRowCell.
You can customize the unbound row cell behavior by overriding existing renderer and replace the default one in SfDataGrid.UnBoundRowCellRenderers.
Below GridUnBoundRowCellTextBoxRenderer is customized to change the foreground.
{% tabs %} {% highlight c# %} public class GridUnBoundRowCellTextBoxRendererExt : GridUnBoundRowCellTextBoxRenderer {
public override void OnInitializeDisplayElement(DataColumnBase dataColumn, TextBlock uiElement, object dataContext)
{
base.OnInitializeDisplayElement(dataColumn, uiElement, dataContext);
var cellValue = dataColumn.GridUnBoundRowEventsArgs != null && dataColumn.GridUnBoundRowEventsArgs.Value != null ?
dataColumn.GridUnBoundRowEventsArgs.Value.ToString() :
string.Empty;
uiElement.Text = cellValue;
uiElement.Foreground = new SolidColorBrush(Colors.Orange);
}
public override void OnInitializeEditElement(DataColumnBase dataColumn, TextBox uiElement, object dataContext)
{
base.OnInitializeEditElement(dataColumn, uiElement, dataContext);
var cellValue = (dataColumn.GridUnBoundRowEventsArgs != null && dataColumn.GridUnBoundRowEventsArgs.Value != null) ?
dataColumn.GridUnBoundRowEventsArgs.Value.ToString() :
string.Empty;
uiElement.Text = cellValue.ToString();
}
} {% endhighlight %} {% endtabs %}
In the below code default renderer replaced using the above custom renderer in SfDataGrid.UnBoundRowCellRenderers.
{% tabs %} {% highlight c# %} dataGrid.UnBoundRowCellRenderers.Remove("UnBoundTextColumn"); dataGrid.UnBoundRowCellRenderers.Add("UnBoundTextColumn", new GridUnBoundRowCellTextBoxRendererExt()); {% endhighlight %} {% endtabs %}
You can customize the unbound row cell by creating new renderer, deriving from GridUnBoundRowCellRenderer and setting the GridUnBoundRowEventsArgs.CellType property.
Below code creates DatePickerRenderer to load the DatePicker as editor element in the first cell of unbound row.
{% tabs %} {% highlight c# %} public class DatePickerRenderer : GridUnBoundRowCellRenderer<TextBlock, DatePicker> { ///
public DatePickerRenderer()
{
}
/// <summary>
/// Display element creation.
/// </summary>
/// <returns></returns>
protected override TextBlock OnCreateDisplayUIElement()
{
return new TextBlock();
}
/// <summary>
/// Edit Element creation.
/// </summary>
/// <returns></returns>
protected override DatePicker OnCreateEditUIElement()
{
return new DatePicker();
}
/// <summary>
/// Initialize the value for display element.
/// </summary>
/// <param name="dataColumn"></param>
/// <param name="uiElement"></param>
/// <param name="dataContext"></param>
public override void OnInitializeDisplayElement(DataColumnBase dataColumn, TextBlock uiElement, object dataContext)
{
uiElement.Text = dataColumn.GridUnBoundRowEventsArgs.Value.ToString();
}
/// <summary>
/// Updates the value for display element.
/// </summary>
/// <param name="dataColumn"></param>
/// <param name="uiElement"></param>
/// <param name="dataContext"></param>
public override void OnUpdateDisplayBinding(DataColumnBase dataColumn, TextBlock uiElement, object dataContext)
{
uiElement.Text = dataColumn.GridUnBoundRowEventsArgs.Value.ToString();
}
#region Edit Element
/// <summary>
/// Initialize the value for edit element.
/// </summary>
/// <param name="dataColumn"></param>
/// <param name="uiElement"></param>
/// <param name="dataContext"></param>
public override void OnInitializeEditElement(DataColumnBase dataColumn, DatePicker uiElement, object dataContext)
{
uiElement.SelectedDate = (DateTime?)dataColumn.GridUnBoundRowEventsArgs.Value;
uiElement.Tag = dataColumn;
}
/// <summary>
/// Updates the value for edit element.
/// </summary>
/// <param name="dataColumn"></param>
/// <param name="element"></param>
/// <param name="dataContext"></param>
public override void OnUpdateEditBinding(DataColumnBase dataColumn, DatePicker element, object dataContext)
{
element.SelectedDate = (DateTime?)dataColumn.GridUnBoundRowEventsArgs.Value;
element.Tag = dataColumn;
}
/// <summary>
/// Method to wire the Selection Changed event
/// </summary>
/// <param name="uiElement"></param>
protected override void OnWireEditUIElement(DatePicker uiElement)
{
base.OnWireEditUIElement(uiElement);
uiElement.SelectedDateChanged += uiElement_SelectedDateChanged;
}
/// <summary>
/// Method to un wire the Selection Changed event.
/// </summary>
/// <param name="uiElement"></param>
protected override void OnUnwireEditUIElement(DatePicker uiElement)
{
base.OnUnwireEditUIElement(uiElement);
uiElement.SelectedDateChanged -= uiElement_SelectedDateChanged;
}
/// <summary>
/// Method to raise the CurrentCellValueChangedEvent.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void uiElement_SelectedDateChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
var datePicker = sender as DatePicker;
if (datePicker.Tag is DataColumn)
{
var dataColumn = datePicker.Tag as DataColumn;
dataColumn.GridUnBoundRowEventsArgs.Value = (sender as DatePicker).SelectedDate;
DataGrid.RaiseQueryUnBoundRow(dataColumn.GridUnBoundRowEventsArgs.GridUnboundRow, UnBoundActions.CommitData, dataColumn.GridUnBoundRowEventsArgs.Value, dataColumn.GridColumn, dataColumn.GridUnBoundRowEventsArgs.CellType, new Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex(dataColumn.RowIndex, dataColumn.ColumnIndex));
}
}
#endregion
#region Update
/// <summary>
/// Update display value and raise event
/// </summary>
/// <param name="dataColumn"></param>
/// <param name="currentRendererElement"></param>
protected override void OnEditingComplete(DataColumnBase dataColumn, FrameworkElement currentRendererElement)
{
dataColumn.GridUnBoundRowEventsArgs.Value = (currentRendererElement as DatePicker).Text;
DataGrid.RaiseQueryUnBoundRow(dataColumn.GridUnBoundRowEventsArgs.GridUnboundRow, UnBoundActions.CommitData, dataColumn.GridUnBoundRowEventsArgs.Value, dataColumn.GridColumn, dataColumn.GridUnBoundRowEventsArgs.CellType, new Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex(dataColumn.RowIndex, dataColumn.ColumnIndex));
}
#endregion
} {% endhighlight %} {% endtabs %}
In the below code newly created renderer added in SfDataGrid.UnBoundRowCellRenderers.
{% tabs %} {% highlight c# %} dataGrid.UnBoundRowCellRenderers.Add("DatePickerRenderer", new DatePickerRenderer()); {% endhighlight %} {% endtabs %}
Below code sets the CellType as DatePickerRenderer.
{% tabs %} {% highlight c# %} void dataGrid_QueryUnBoundRow(object sender, GridUnBoundRowEventsArgs e) {
if (e.UnBoundAction == UnBoundActions.QueryData)
{
if(e.RowColumnIndex.ColumnIndex == 0)
{
e.CellType = "DatePickerRenderer";
e.Value = DateTime.Now;
e.Handled = true;
}
else if(e.RowColumnIndex.ColumnIndex == 2)
{
e.Value = (dataGrid.SelectedItems.First(item => (item as OrderInfo).CustomerName.Contains("g")) as OrderInfo).CustomerName;
e.Handled = true;
}
}
if (e.UnBoundAction == UnBoundActions.CommitData)
date = (DateTime)e.Value;
} {% endhighlight %} {% endtabs %}
You can customize the unbound row cells using GridUnBoundRowEventsArgs.CellTemplate property.
{% tabs %}
{% highlight xaml %}
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
{% endhighlight %}
{% endtabs %}
{% tabs %} {% highlight c# %} void dataGrid_QueryUnBoundRow(object sender, GridUnBoundRowEventsArgs e) {
if (e.UnBoundAction == UnBoundActions.QueryData)
{
if(e.RowColumnIndex.ColumnIndex == 0)
{
e.CellType = "UnBoundTemplateColumn";
e.CellTemplate = App.Current.Resources["UnBoundRowCellTemplate"] as DataTemplate;
e.Value = (dataGrid.SelectedItems.OrderBy(item => (item as OrderInfo).OrderID).Last() as OrderInfo).OrderID;
e.Handled = true;
}
else if(e.RowColumnIndex.ColumnIndex == 2)
{
e.Value = (dataGrid.SelectedItems.First(item => (item as OrderInfo).CustomerName.Contains("g")) as OrderInfo).CustomerName;
e.Handled = true;
}
}
} {% endhighlight %} {% endtabs %}
You can change the height of unbound row using SfDataGrid.QueryRowHeight event.
{% tabs %} {% highlight c# %} using Syncfusion.UI.Xaml.Grid; dataGrid.QueryRowHeight += dataGrid_QueryRowHeight;
void dataGrid_QueryRowHeight(object sender, QueryRowHeightEventArgs e) {
if(dataGrid.IsUnBoundRow(e.RowIndex))
{
e.Height = 40;
e.Handled = true;
}
} {% endhighlight %} {% endtabs %}
You can export the unbound rows to excel by setting the ExcelExportingOptions.ExportUnBoundRows property.
{% tabs %} {% highlight c# %} ExcelExportingOptions excelExportingOption = new ExcelExportingOptions(); excelExportingOption.ExportUnBoundRows = true; {% endhighlight %} {% endtabs %}
You can export the unbound rows to PDF by setting the PdfExportingOptions.ExportUnBoundRows property.
{% tabs %} {% highlight c# %} PdfExportingOptions pdfExportingOption = new PdfExportingOptions(); pdfExportingOption.ExportUnBoundRows = true; {% endhighlight %} {% endtabs %}
You can get the unbound row of specified row index using GetUnBoundRow method.
{% tabs %} {% highlight c# %} using Syncfusion.UI.Xaml.Grid; var unboundRow = dataGrid.GetUnBoundRow(1); {% endhighlight %} {% endtabs %}
You can merge the unbound row cell by setting the Left, Right, Top and Bottom properties of CoveredCellInfo with the help of GetUnBoundRow method and RowIndex.
{% tabs %} {% highlight c# %} using Syncfusion.UI.Xaml.Grid; dataGrid.QueryCoveredRange += dataGrid_QueryCoveredRange;
void dataGrid_QueryCoveredRange(object sender, GridQueryCoveredRangeEventArgs e) { var unboundRow = this.dataGrid.GetUnBoundRow(e.RowColumnIndex.RowIndex);
if(unboundRow == null)
return;
if(e.RowColumnIndex.ColumnIndex == 1)
{
e.Range = new CoveredCellInfo(e.RowColumnIndex.ColumnIndex, (e.OriginalSender as SfDataGrid).Columns.Count, e.RowColumnIndex.RowIndex, e.RowColumnIndex.RowIndex);
e.Handled = true;
}
}
{% endhighlight %}
{% endtabs %}
Master-details view also allows you to add additional rows to ViewDefinition.DataGrid which are not bound with data object from underlying data source.
You can get the DetailsViewDataGrid using GridUnBoundRowEventsArgs.OriginalSender of the QueryUnBoundRow event, which fired the event and rendered in UI.
{% tabs %}
{% highlight xaml %}
<syncfusion:SfDataGrid x:Name="dataGrid"
NavigationMode="Cell"
AutoGenerateColumns="True"
ItemsSource="{Binding Orders}">
syncfusion:SfDataGrid.DetailsViewDefinition
<syncfusion:GridViewDefinition RelationalColumn="ProductDetails">
syncfusion:GridViewDefinition.DataGrid
<syncfusion:SfDataGrid x:Name="FirstLevelNestedGrid"
AutoGenerateColumns="True">
syncfusion:SfDataGrid.UnBoundRows
<syncfusion:GridUnBoundRow Position="Top"
ShowBelowSummary="True"/>
</syncfusion:SfDataGrid.UnBoundRows>
</syncfusion:SfDataGrid>
</syncfusion:GridViewDefinition.DataGrid>
</syncfusion:GridViewDefinition>
</syncfusion:SfDataGrid.DetailsViewDefinition>
</syncfusion:SfDataGrid>
{% endhighlight %}
{% endtabs %}
{% tabs %} {% highlight c# %} this.FirstLevelNestedGrid.QueryUnBoundRow += FirstLevelNestedGrid_QueryUnBoundRow;
void FirstLevelNestedGrid_QueryUnBoundRow(object sender, GridUnBoundRowEventsArgs e) {
if (e.UnBoundAction == UnBoundActions.QueryData)
{
if (e.RowColumnIndex.ColumnIndex == 0)
{
e.Value = "Total Items";
e.Handled = true;
}
else if(e.RowColumnIndex.ColumnIndex == 1)
{
e.Value = (e.OriginalSender as SfDataGrid).View.Records.Count();
e.Handled = true;
}
}
} {% endhighlight %} {% endtabs %}










