Wednesday, June 16, 2010

Formatting Exports from Telerik RadGrid (PDF,Word, Excel)

I have been spending a bit of time trying to get a simple and elegant way to (re-)format an export from a RadGrid. The Css Styles (especially if you do not use the built-in skins) are not carried through to the export.

 

The export creates XHTML which is then passed to the appropriate engine to produce the download file. The solution (once you find it) is pretty clean.

 

First, I created a Css.Resx file which contains the following items:

  • GridItem
  • GridItemCell
  • GridHeaderItem
  • GridHeaderItemCell
  • GridItem
  • GridItemCell

Each of these items contain a definition in classic style sheet format.

color:#0000FF;
font-size:1.2em;

So you can now define the appearance of each of these six regions (you can do more regions using the same pattern).

 

Setting up to detect the Export

Telerik examples typically shows the use of an independent button which then sets a variable. We use a similar pattern but make use of the built-in commands and a property on the RadGrid. This is done by adding two event handlers (with supporting code) as shown below. We use the CssClass as a flag variable.

 

The routines below may be put into a utility library and made universally available.

grid.ItemCommand += Grid_ItemCommand;
grid.ItemCreated += Grid_ItemCreatedAddRowScope;
....            
 
/// <summary>
/// Handles the ItemCommand event of the Grid control and applies export settings
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="e">The <see cref="Telerik.Web.UI.GridCommandEventArgs"/> instance containing the event data.</param>
private static void Grid_ItemCommand(object source, GridCommandEventArgs e)
{
    RadGrid grid = source as RadGrid;
    switch (e.CommandName)
    {
        case RadGrid.ExportToPdfCommandName:
            grid.CssClass = "Export";
            break;
        case RadGrid.ExportToWordCommandName:
            grid.CssClass = "Export";
            break;
        case RadGrid.ExportToExcelCommandName:
            grid.CssClass = "Export";
            break;
    }
}

Now when the XHTML starts being built,  we see if we should apply our settings, and if so, we do as shown below.

private static void Grid_ItemCreated(object sender, GridItemEventArgs e)
{
RadGrid grid = sender as RadGrid;
if (grid.CssClass == "Export")
{
    if ( e.Item is GridHeaderItem)
    {
        grid.ShowFooter = true;
        grid.MasterTableView.ShowFooter = true;
        GridHeaderItem headerItem = (GridHeaderItem)e.Item;
        var items = GetStyleArray(Resources.Css.GridHeaderItem);
        foreach (string key in items.Keys)
        {
            headerItem.Style[key] = items[key];
        }
        items = GetStyleArray(Resources.Css.GridHeaderItemCell);
        foreach (TableCell cell in headerItem.Cells)
        {
            foreach (string key in items.Keys)
            {
                cell.Style[key] = items[key];
            }
        }
    }
    else if (e.Item is GridItem)
    {
        GridItem headerItem = (GridItem)e.Item;                    
        var items = GetStyleArray(Resources.Css.GridItem);
        foreach (string key in items.Keys)
        {
            headerItem.Style[key] = items[key];
        }
        items = GetStyleArray(Resources.Css.GridItemCell);
        foreach (TableCell cell in headerItem.Cells)
        {
            foreach (string key in items.Keys)
            {
                cell.Style[key] = items[key];
            }
        }
    }
    else if (e.Item is GridFooterItem)
    {
        GridFooterItem headerItem = (GridFooterItem)e.Item;
        var items = GetStyleArray(Resources.Css.GridFooterItem);
        foreach (string key in items.Keys)
        {
            headerItem.Style[key] = items[key];
        }
        items = GetStyleArray(Resources.Css.GridFooterItemCell);
        foreach (TableCell cell in headerItem.Cells)
        {
            foreach (string key in items.Keys)
            {
                cell.Style[key] = items[key];
            }
        }
    }
}

This uses a function that breaks apart the CSS  into a sorted list that is shown below. The function is (reasonably) tolerant of format.  You could use

.GridItemCell { whatever }

instead of just the content shown above.

 

/// <summary>
/// Gets a style array suitable for a griditem from a string.
/// </summary>
/// <param name="css">The CSS.</param>
/// <returns></returns>
private static SortedList<string,string>  GetStyleArray(string css)
{
    char[] sep1={';','{','}',' '};
    char[] sep2={':'};
    var ret=new SortedList<string,string>();
    string[] elements = css.Split(sep1, StringSplitOptions.RemoveEmptyEntries);
    foreach (string element in elements)
    {
        var parts = element.Split(sep2, StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length == 2)
        {
            if (!ret.ContainsKey(parts[0]))
            {
                ret.Add(parts[0], parts[1]);
            }
        }
    }
    return ret;
}

There are a few got-cha, for example: 

  • color:Red; does not work.
  • color: #FF0000; does work.

Those are discovered quickly (if it does not work, try an alternative expression!).  I suspect that the CSS level is not comprehensive – so don’t be surprise if some items do not work!

 

With the formatting being in a RESX file, it is easy to modify as needed for different customers. You can modify as few or as many formatting attributes as you wish.

3 comments:

  1. Wouldn't happen to have a VB version of this?

    ReplyDelete
  2. do you have an example project that you can post?

    ReplyDelete
  3. GetStyleArray is a user defined function?

    ReplyDelete