Skip to main content
A newer version of this page is available. .
All docs
V21.2

Use the Custom Draw Events to Customize the Appearance of Spreadsheet Elements

  • 13 minutes to read

The WinForms Spreadsheet control supports custom draw events that allow you to customize the appearance of worksheet cells, column headers, and rows headers. You can display custom text, change background settings, or draw images and geometric primitives (lines, rectangles, callouts, and so on) over these elements.

View Example: Use Custom Draw Events to Customize the Appearance of Spreadsheet Elements

Custom Draw Scenarios

The following custom draw scenarios are supported:

Custom Painting
Custom draw events fire before the Spreadsheet control renders its elements with the default appearance settings. Enable the CustomDrawObjectEventsArgs.Handled property in the event handler to cancel default painting and draw an element manually. If this property is false, the Spreadsheet control can override custom appearance settings.
Combined Painting
You can combine custom and default painting. Call the CustomDrawObjectEventsArgs.DrawDefault method in the custom draw event handler to invoke the default drawing mechanism to display a spreadsheet element. Then set the CustomDrawObjectEventsArgs.Handled property to true and add custom graphics to the element.
Default Painting with Custom Settings
Data classes of the custom draw events contain properties that allow you to customize the default appearance settings for a spreadsheet element (such as background color, foreground color, or font attributes). If you use these properties to change the element’s appearance, you do not need to enable the CustomDrawObjectEventsArgs.Handled property. The default drawing mechanism takes these property values into account when it renders the element.

Important

You can use custom painting to change the Spreadsheet control’s visual appearance only. All your custom appearance settings are ignored when you print or export spreadsheet content to PDF.

Custom Paint Cells

Customize Cell Content

The SpreadsheetControl.CustomDrawCell event fires for each cell in the visible worksheet area and allows you to customize cell content. Use properties of the event data class (CustomDrawCellEventArgs) to access a cell to paint, obtain cell bounds, change cell text, specify font attributes, and display custom graphics over the cell.

The code snippet below uses the SpreadsheetControl.CustomDrawCell event to display images for product categories and add callouts to cells that contain 0 in the “Units In Stock” column. The Spreadsheet control uses the default drawing mechanism to render these cells and then displays custom graphics over cells.

Handle the CustomDrawCell event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawCell(object sender, CustomDrawCellEventArgs e)
{

    // Check whether a cell belongs to the "Category" column.
    if (e.Cell.ColumnIndex == 1) { 

        // Obtain the category name. 
        String categoryName = e.Cell.Value.ToString();

        // Return the image that corresponds to the category name. 
        Image categoryImage = ChooseImage(categoryName);

        // Specify the image location (the upper-right corner of the cell).
        Point point = new Point(e.Bounds.Right - 50, e.Bounds.Top);

        // Draw the category image over the cell.
        if (categoryImage != null)
            e.Graphics.DrawImage(categoryImage, point);
    }

    // If a cell belongs to the "Units In Stock" column and the cell value is "0",
    // draw a callout to the right of this cell.
    if (e.Cell.ColumnIndex == 4 && e.Cell.Value.IsNumeric && e.Cell.Value.NumericValue == 0)
    {
        // Specify callout text.
        string text = "OUT OF STOCK";

        using (Font font = new Font(e.Font.Name, 9f, FontStyle.Bold))
        {
            using (StringFormat format = new StringFormat())
            {
                // Align callout text.
                format.LineAlignment = StringAlignment.Center;
                format.Alignment = StringAlignment.Center;

                // Calculate the callout bounds.
                SizeF size = e.Graphics.MeasureString(text, font, int.MaxValue, format);
                Rectangle textBounds = new Rectangle(
                    e.Bounds.Right + 15,
                    (int)Math.Round(e.Bounds.Top + (e.Bounds.Height - size.Height) / 2),
                    (int)(size.Width + 8),
                    (int)Math.Round(size.Height));

                int middle = (int)Math.Round(textBounds.Height / 2.0);

                Point[] points = new Point[] {
                    new Point(textBounds.Left, textBounds.Top),
                    new Point(textBounds.Left - middle, textBounds.Top + middle),
                    new Point(textBounds.Left, textBounds.Bottom)
                };

                // Draw the callout.
                Brush brush = e.Cache.GetSolidBrush(Color.FromArgb(200, 44, 74));
                e.Graphics.FillPolygon(brush, points);
                e.Cache.FillRectangle(brush, textBounds);
                e.Graphics.DrawString(text, font, e.Cache.GetSolidBrush(Color.White), textBounds, format);
            }
        }
    }
}

private Image ChooseImage(string categoryName)
{
    switch (categoryName)
    {
        case "Beverages":
            return Image.FromFile("images/beverages.png");
        case "Condiments":
            return Image.FromFile("images/condiments.png");
        case "Confections":
            return Image.FromFile("images/confections.png");
        case "Dairy Products":
            return Image.FromFile("images/dairy.png");
        case "Grains/Cereals":
            return Image.FromFile("images/cereals.png");
        case "Meat/Poultry":
            return Image.FromFile("images/meat.png");
        case "Produce":
            return Image.FromFile("images/produce.png");
        case "Seafood":
            return Image.FromFile("images/seafood.png");
    }

    return null;
}

Customize Cell Background

The SpreadsheetControl.CustomDrawCellBackground event fires for each cell in the visible worksheet area and allows you to customize the cell background. This event receives a CustomDrawCellBackgroundEventArgs object as a parameter. The object’s properties allow you to access a cell to paint, obtain cell bounds, and specify appearance settings for the cell background.

The following code example changes background color for the active cell and cells in odd rows. The Spreadsheet control uses the default drawing mechanism to paint cells with the custom background.

Handle the CustomDrawCellBackground event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawCellBackground(object sender, 
    CustomDrawCellBackgroundEventArgs e)
{
    // Specify the background color for the active cell. 
    if (e.Cell == spreadsheetControl1.ActiveCell)
    {
        e.BackColor = Color.FromArgb(50, 200, 44, 74);
        return;
    }

    // Specify the background color for cells that contain data and belong to odd rows.
    var dataRange = e.Cell.Worksheet.GetDataRange();
    if (e.Cell.IsIntersecting(dataRange) && e.Cell.RowIndex % 2 != 0) 
    {             
        e.BackColor = Color.FromArgb(50, 91, 155, 213);
    }
}

Custom Paint Column Headers

Customize Column Header Content

The SpreadsheetControl.CustomDrawColumnHeader event fires before a column header is painted and allows you to customize column header content. Use properties of the event data class (CustomDrawColumnHeaderEventArgs) to access a column header to paint, obtain its bounds, change header text, specify font attributes, or display geometric primitives and images.

The code snippet below handles the SpreadsheetControl.CustomDrawColumnHeader event to change default text (A, B, C, …) in column headers. This example does not change the background color of column headers. Refer to the next section in this topic for information on how to apply a custom color to column headers.

Handle the CustomDrawColumnHeader event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawColumnHeader(object sender, 
    CustomDrawColumnHeaderEventArgs e)
{           
    // Cancel default painting for the column header.
    e.Handled = true;

    if (e.ColumnIndex > 4) 
        return;

    // Specify the list of new column header names.
    string[] headers = new string[] { "Category", "Product Name", 
        "Quantity per Unit", "Unit Price", "Units in Stock" };

    // Specify font attributes for the column header.
    SpreadsheetFont defaultFont = spreadsheetControl1.Document.Styles.DefaultStyle.Font;
    using (Font font = new Font(defaultFont.Name, (float)defaultFont.Size, FontStyle.Bold))
    {
        // Specify layout settings for the column header.
        using (StringFormat stringFormat = new StringFormat())
        {
            stringFormat.LineAlignment = StringAlignment.Center;
            stringFormat.Alignment = e.ColumnIndex < 3 ? StringAlignment.Near : StringAlignment.Far;
            stringFormat.Trimming = StringTrimming.EllipsisCharacter;

            // Draw the column header.
            e.Graphics.DrawString(headers[e.ColumnIndex], font, 
                e.Cache.GetSolidBrush(Color.White), e.Bounds, stringFormat);
        }
    }
}

Customize Column Header Background

The SpreadsheetControl.CustomDrawColumnHeaderBackground event fires before a column header is painted and allows you to customize the column header background. This event receives a CustomDrawColumnHeaderBackgroundEventArgs object as a parameter. The object’s properties allow you to access a column header to paint, obtain its bounds, and specify appearance settings for the header background.

The following code snippet demonstrates how to change the background color for column headers. This example applies a dark red color to a column header under the mouse pointer or if a header belongs to a column with the active cell:

Handle the CustomDrawColumnHeaderBackground event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawColumnHeaderBackground(object sender, 
    CustomDrawColumnHeaderBackgroundEventArgs e)
{
    // Cancel default painting for the column header background.
    e.Handled = true;

    // If the column header is under the mouse pointer or the column contains the active cell,
    // paint the column header background in Color 1.
    // Otherwise, use Color 2.
    if (e.ColumnIndex <= 4) {
        var backColor = e.IsHovered || (e.ColumnIndex == spreadsheetControl1.ActiveCell.ColumnIndex) ?
            //Color 1
            Color.FromArgb(200, 44, 74) :
            //Color 2
            Color.FromArgb(91, 155, 213);
        e.Cache.FillRectangle(e.Cache.GetSolidBrush(backColor), e.Bounds);
    }
}

Custom Paint Row Headers

Customize Row Header Content

The SpreadsheetControl.CustomDrawRowHeader event fires before a row header is painted and allows you to customize row header content. Use properties of the event data class (CustomDrawRowHeaderEventArgs) to access a row header to paint, obtain its bounds, change header text, specify font attributes, or display geometric primitives and images.

The code snippet below uses the SpreadsheetControl.CustomDrawRowHeader event to display a row number for every fifth row.

Handle the CustomDrawRowHeader event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawRowHeader(object sender, CustomDrawRowHeaderEventArgs e)
{
    // Cancel default painting for the row header.
    e.Handled = true;

    // Display a row number for every fifth row.
    if ((e.RowIndex + 1) % 5 == 0)
    {
        // Specify font style.
        e.Appearance.FontStyleDelta = FontStyle.Bold;

        // Return the row header bounds.
        Rectangle textBounds = e.Bounds;

        // Specify row header text.
        string text = (e.RowIndex + 1).ToString();

        // Specify text alignment. 
        StringFormat format = new StringFormat {
            LineAlignment = StringAlignment.Center,
            Alignment = StringAlignment.Center
        };

        // Draw row header text.
        e.Graphics.DrawString(text, e.Font, 
            e.Cache.GetSolidBrush(Color.FromArgb(91, 155, 213)), textBounds, format);
    }
}

Customize Row Header Background

The SpreadsheetControl.CustomDrawRowHeaderBackground event fires before a row header is painted and allows you to customize the row header background. This event receives a CustomDrawRowHeaderBackgroundEventArgs object as a parameter. The object’s properties allow you to access a row header to paint, obtain its bounds, and specify appearance settings for the header background.

The following code snippet demonstrates how to change the background color for row headers. This example applies a dark red color to a row header under the mouse pointer or if a header belongs to a row with the active cell:

Handle the CustomDrawRowHeaderBackground event

using System;
using System.Drawing;
using DevExpress.Spreadsheet;
using DevExpress.XtraSpreadsheet;
// ...
private void spreadsheetControl1_CustomDrawRowHeaderBackground(object sender, 
    CustomDrawRowHeaderBackgroundEventArgs e)
{
    // Cancel default painting for the row header background.
    e.Handled = true;

    // If the row header is under the mouse pointer or the row contains the active cell,
    // paint the row header background in Color 1.
    // Otherwise, paint the row header in white.
    Color backColor = e.IsHovered || (e.RowIndex == spreadsheetControl1.ActiveCell.RowIndex) ? 
        Color.FromArgb(200, 44, 74) : // Color 1
        Color.White;
    e.Cache.FillRectangle(e.Cache.GetSolidBrush(backColor), e.Bounds);
}