Custom Cell Type in UWP CellGrid (SfCellGrid)

10 May 202111 minutes to read

SfCellGrid provides support to customize the cell by loading any user control into the cell.

This requires a cell renderer class which creates the custom cell control and handles the UI requirements. The custom cell type can be created by registering the cell renderer to the SfCellGrid. It can be enabled by assigning its name to the CellType property of GridStyleInfo class.

In general, the built-in cell types are also constructed only in this way. Every such cell type has its own renderer classes in the code base which are inherited from GridVirtualizingCellRenderer class. This class defines the basic functionality for a cell type.

To customize the cell, please follow the below steps

  • Create a Custom Cell Renderer class
  • Associating the Custom Cell Renderer to SfCellGrid

Creating a new Custom Cell Type(SfCalendar)

Create a Custom Cell Renderer class

Create a custom class “CalendarCellRenderer” by overriding the GridVirtualizingCellRenderer class to define the custom renderer element. Here the display element is TextBlock whereas the edit element is loaded as SfCalendar.

public class CalendarCellRenderer : GridVirtualizingCellRenderer<TextBlock, SfCalendar>
{
    public CalendarCellRenderer()
    {
           
    }
}

To load the SfCalendar in the edit mode of a cell, create a new SfCalendar control in OnCreateEditUIElement method.
In OnInitializeDisplayElement method, initialize the display UI element TextBlock which is to be loaded in the cell of SfCellGrid and in OnInitializeEditElement method,
initialize the edit UI element SfCalendar which is to be loaded in the cell of SfCellGrid while in edit mode.

When the edit UI element is loaded in the cell, OnEditElementLoaded event is invoked and focus is set to the UI element(SfCalendar). When the selected date in SfCalendar Control is changed, SelectionChanged event is invoked and set the selected date as value.
The GetControlValue and GetFormattedText methods returns/updates the value and formatted text of the current rendered element(SfCalendar/TextBlock) in the cell.

public class CalendarCellRenderer : GridVirtualizingCellRenderer<TextBlock, SfCalendar>
{
    public CalendarCellRenderer()
    {
           
    }
    
    //Occurs when the UIElement(SfCalendar) is loaded in SfCellGrid,
    protected override void OnEditElementLoaded(object sender, RoutedEventArgs e)
    {
        var uiElement = ((SfCalendar)sender);
        if (HasCurrentCellState)
            uiElement.Focus(FocusState.Programmatic);
        uiElement.SelectionChanged += UiElement_SelectionChanged;        
    }

    //Occurs when the selected date in SfCalendar is changed,
    private void UiElement_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        base.CurrentCellValueChanged(e.AddedItems[0].ToString());
    }
    
    //Returns the value of the current rendered element(SfCalendar),
    public override object GetControlValue()
    {
        if (HasCurrentCellState && CurrentCellRendererElement != null && IsInEditing)
            return (CurrentCellRendererElement as SfCalendar).SelectedDate;
        else
            return (CurrentCellRendererElement as TextBlock).Text;
    }

    //Returns the formatted text of the cell,
    public override string GetFormattedText(GridStyleInfo style)
    {
        DateTime dateValue;

        if (style.CellValue == null || style.CellValue.ToString() == string.Empty)
            return string.Empty;

        DateTime.TryParse(style.CellValue.ToString(), out dateValue);

        if (dateValue < DateTime.MinValue)
            dateValue = DateTime.MinValue;
        if (dateValue > DateTime.MaxValue)
            dateValue = DateTime.MaxValue;

        double doubleValue;
        if (DateTime.TryParse(dateValue.ToString(), out dateValue))
            return dateValue.ToString("d");
        else if (double.TryParse(dateValue.ToString(), out doubleValue))
            return doubleValue.ToString(style.Format);
        return dateValue.ToString();
    }
    
    //Creates a UIElement for the edit mode of the cell.
    protected override SfCalendar OnCreateEditUIElement()
    {
        return new SfCalendar();
    }

    //Initializing the display element(TextBlock) while loading in SfCellGrid,
    protected override void OnInitializeDisplayElement(RowColumnIndex rowColumnIndex, TextBlock uiElement, GridStyleInfo style, string text)
    {
        base.OnInitializeDisplayElement(rowColumnIndex, uiElement, style, text);
     
        uiElement.Text = text;
    }

    //Initializing the edit element(SfCalendar) while loading in SfCellGrid,
    protected override void OnInitializeEditElement(RowColumnIndex rowColumnIndex, SfCalendar uiElement, GridStyleInfo style, string text)
    {
        base.OnInitializeEditElement(rowColumnIndex, uiElement, style, text);
        if ((style.CellValue != null && style.CellValue.ToString() != string.Empty))
            uiElement.SelectedDate = style.CellValue;
        else if (uiElement.SelectedDate != null)
            uiElement.SelectedDate = null;

        uiElement.Width = Grid.ColumnWidths[style.ColumnIndex];
        uiElement.Height = Grid.RowHeights[style.RowIndex];

        uiElement.SelectedDateBackground = new SolidColorBrush(Windows.UI.Colors.Red);
        uiElement.ShowNavigationButton = true;

        uiElement.VisibleMaxDate = DateTime.MaxValue;
        uiElement.VisibleMinDate = DateTime.MinValue;
        uiElement.ShowHeader = true;
           
    }
    
    //Unwire the events associated with edit UI Element,
    protected override void OnUnwireEditUIElement(SfCalendar uiElement)
    {
       uiElement.SelectionChanged -= UiElement_SelectionChanged;
       uiElement.Focus(FocusState.Programmatic);
       base.OnUnwireEditUIElement(uiElement);
    }

}

Associating the Custom Cell Renderer to SfCellGrid

To associate the custom cell renderer in SfCellGrid, initialize the CalendarCellRenderer class and add it to the CellRenderers property which is a collection of type GridCellRendererCollection class.

To load the custom cell(SfCalendar) in the required range, assign its name “CalendarCell” to the CellType property of GridStyleInfo class.

public MainPage()
{
  this.InitializeComponent();
  
  //Add the customized renderer
  var renderer = new CalendarCellRenderer();
  cellGrid.CellRenderers.Add("CalendarCell", renderer);
  cellGrid.Model.QueryCellInfo += Model_QueryCellInfo;
}

//To update the cell type
void Model_QueryCellInfo(object sender, GridQueryCellInfoEventArgs e)
{
  if (e.Cell.RowIndex == 5 && e.Cell.ColumnIndex == 5)
  {
    e.CellType = "CalendarCell";
    e.CellValue = new DateTime(2015,3,3);

  }
}

Output

The following output is generated using the code above while the Cell(5,5) is in Edit mode.

SfCalendarCell

For more reference, please find the customization sample.

Modify the Existing Cell Type

Users can also change/modify the behavior of already existing cell type in SfCellGrid by creating a custom renderer class by overriding the existing cell renderer class.

Create a Custom Cell Renderer class

To open the dropdown list of ComboBox cell with single click which is not a default behavior of ComboBox cell type in SfCellGrid, hence the users need to create a custom class CustomComboRenderer by overriding the already existing GridComboBoxCellRenderer class to modify the behavior of the default ComboBox cell type.

In the OnEditElementLoaded event, set the IsDropDownOpen property of ComboBox UI to true and this will open the combo box dropdown list when the edit element is loaded in the cell of SfCellGrid.

public class CustomComboRenderer: GridComboBoxCellRenderer
{
    protected override void OnEditElementLoaded(object sender, RoutedEventArgs e)
    {
       base.OnEditElementLoaded(sender, e);
       var combo = sender as ComboBox;
       combo.IsDropDownOpen = true;
    }
}

Associating the Custom Cell Renderer to SfCellGrid

To associate the custom cell renderer in SfCellGrid, initialize the CustomComboRenderer class and add it to the CellRenderers property which is a collection of type GridCellRendererCollection class and remove the existing cell type ComboBox from the collection.

To load the custom cell in the required range, assign its name ComboCell to the CellType property of GridStyleInfo class.

public MainPage()
{
  this.InitializeComponent();
  
  //Remove the default renderer
  cellGrid.CellRenderers.Remove("ComboBox");

  //Add the customized renderer
  var renderer = new CustomComboRenderer();
  cellGrid.CellRenderers.Add("ComboCell", renderer);
  
  cellGrid.EditTrigger = EditTrigger.OnTap;
  cellGrid.Model.QueryCellInfo += Model_QueryCellInfo;
}

//To update the cell type
void Model_QueryCellInfo(object sender, GridQueryCellInfoEventArgs e)
{
  if (e.Cell.RowIndex == 5 && e.Cell.ColumnIndex == 5)
  {
    e.CellType = "ComboCell";
    e.ComboBoxEdit.ItemSource = new List<String> { "First ", "Second ", "Third " };
  }
}

For more reference, please find the customization sample.