Section 508 and WCAG Complaince for RadGrid

3 posts, 0 answers
  1. Ken Lassesen
    Ken Lassesen avatar
    37 posts
    Member since:
    Nov 2008

    Posted 19 Feb 2010 Link to this post


    RadControls version


    .NET version


    Visual Studio version


    programming language


    browser support

    all browsers supported by RadControls


    A strategy for building a Section 508/WCAG Telerik radGrid page

    Accessibility and translations can often result in a medusa head for code. On one hand you want a feature rich grid but you are constrained by Section 508.  Managing and tracking resource mnemonic across a large project becomes a nightmare (and very very boring code typing for developers which usually impacts quality control).  Another pain with a large site is consistency of presentation.


    What if I claim that all you need to do is toss the code below into a page and be done with coding!


    The Web Page Content:

       1: <telerik:RadGrid runat="server" ID="AccountSummaryGrid" DataSourceID="AccountSummaryDb" AccessKey="X">
       2:     <MasterTableView AutoGenerateColumns="false" DataKeyNames="AccountName">
       3:         <Columns>
       4:             <telerik:GridBoundColumn DataField="AccountName" UniqueName="Grid_AccountName">
       5:             </telerik:GridBoundColumn>
       6:             <telerik:GridBoundColumn DataField="Description" UniqueName="Grid_AccountDescription">
       7:             </telerik:GridBoundColumn>
       8:             <telerik:GridBoundColumn DataField="Balance" UniqueName="Grid_AccountBalance">
       9:             </telerik:GridBoundColumn>
      10:             <telerik:GridBoundColumn DataField="CreditLimit" UniqueName="Grid_AccountCreditLimit">
      11:             </telerik:GridBoundColumn>
      12:             <telerik:GridBoundColumn DataField="OwnershipType" UniqueName="Grid_OwnershipType">
      13:             </telerik:GridBoundColumn>
      14:         </Columns>
      15:     </MasterTableView>
      16: </telerik:RadGrid>

    With the page behind code being a horrible:

       1: protected void Page_Load(object sender, EventArgs e)
       2: { 
       3:     RadGridUtilities.ApplyDefaultSettings(AccountSummaryGrid);
       4: }

    There are many tricks (and enhancement not shown), but the first item is to give the user the option to put the site into Accessibility Mode. If you give that option then only the pages while in Accessibility Mode must comply with Section 508. You can be as feature rich as you want elsewhere!!


    This is what the one C# statement above does.

    • Provide web site defaults to the grid (every grid…), you want the defaults changed – it occurs in one spot only not in 100 pages!
    • Provide translation that is based off of the Control.Id (SO no more “Button1” but descriptive “ButtonToRetrieveStatement”
    • Modify the grid for what you want to do for accessibility.

    There are a few items that I excluded (like automatically creating Resx entries for every phrase dynamically and self-auditing for 508 compliance) so the code is clearer.

    /// <summary>
    /// Applies the default settings to the page. If user customization is implemented
    /// the settings here would come from their preferences stored in a Session object.
    /// </summary>
    /// <param name="grid">a radGrid control</param>
    /// <param name="onGetOnly">Determines if the settings are down on every postback or only on first GET</param>
    public static void ApplyDefaultSettings(RadGrid grid, bool onGetOnly)
        // Uniquenames should have an underscore (_) in it. grid_ is the recommended prefix so that
        //localization may identify items used in grids
        char[] sepUnique = { '_', ' ' };
        // Items requiring     
        grid.ItemCreated += grid_ItemCreatedAddRowScope;
        // Get translations. For items that Telerik may have translated use fallback version. 
        grid.MasterTableView.Caption = ResourceTranslation.TranslateField(string.Format(CultureInfo.CurrentCulture, "%ContentPage%{0}_Caption%", grid.ID));
        grid.MasterTableView.CommandItemSettings.AddNewRecordText = ResourceTranslation.TranslateField("%MasterPage%Grid_AddNewRecordText%", grid.MasterTableView.CommandItemSettings.AddNewRecordText);
        grid.MasterTableView.CommandItemSettings.ExportToCsvText = ResourceTranslation.TranslateField("%MasterPage%Grid_ExportToCsvText%", grid.MasterTableView.CommandItemSettings.ExportToCsvText);
        grid.MasterTableView.CommandItemSettings.ExportToExcelText = ResourceTranslation.TranslateField("%MasterPage%Grid_ExportToExcelText%", grid.MasterTableView.CommandItemSettings.ExportToExcelText);
        grid.MasterTableView.CommandItemSettings.ExportToPdfText = ResourceTranslation.TranslateField("%MasterPage%Grid_ExportToPdfText%", grid.MasterTableView.CommandItemSettings.ExportToPdfText);
        grid.MasterTableView.CommandItemSettings.ExportToWordText = ResourceTranslation.TranslateField("%MasterPage%Grid_ExportToWordText%", grid.MasterTableView.CommandItemSettings.ExportToWordText);
        grid.MasterTableView.CommandItemSettings.RefreshText = ResourceTranslation.TranslateField("%MasterPage%Grid_RefreshText%", grid.MasterTableView.CommandItemSettings.RefreshText);
        grid.MasterTableView.CssClass = ResourceTranslation.TranslateField("%MasterPage%Grid_CssClass%", grid.MasterTableView.CssClass);
        grid.MasterTableView.Summary = ResourceTranslation.TranslateField(string.Format(CultureInfo.CurrentCulture, "%ContentPage%{0}_Summary%", grid.ID));
        // This section would be used if images are to be customized for languages (unlikely)
        //    grid.MasterTableView.CommandItemSettings.AddNewRecordImageUrl = ResourceTranslation.TranslateField(grid.MasterTableView.CommandItemSettings.AddNewRecordImageUrl);
        //    grid.MasterTableView.CommandItemSettings.ExportToCsvImageUrl = ResourceTranslation.TranslateField(grid.MasterTableView.CommandItemSettings.ExportToCsvImageUrl);
        //    grid.MasterTableView.CommandItemSettings.ExportToExcelImageUrl = ResourceTranslation.TranslateField(grid.MasterTableView.CommandItemSettings.ExportToExcelImageUrl);
        //    grid.MasterTableView.CommandItemSettings.ExportToPdfImageUrl = ResourceTranslation.TranslateField(grid.MasterTableView.CommandItemSettings.ExportToPdfImageUrl);
        //    grid.MasterTableView.CommandItemSettings.ExportToWordImageUrl = ResourceTranslation.TranslateField(grid.MasterTableView.CommandItemSettings.ExportToWordImageUrl);
        // Site specific options. 
        // If User Options are allowed, the user's setting would be assigned instead.
        grid.AllowPaging = true;
        grid.AllowSorting = true;
        grid.ExportSettings.FileName = grid.ID;
        grid.ExportSettings.IgnorePaging = true;
        grid.ExportSettings.OpenInNewWindow = true;
        grid.ExportSettings.Pdf.Author = "Lassesen Consulting, LLC";
        grid.ExportSettings.Pdf.PageTitle = grid.MasterTableView.Caption;
        grid.ExportSettings.Pdf.Subject = grid.ID;
        grid.MasterTableView.AllowPaging = true;
        grid.MasterTableView.AllowSorting = true;
        grid.MasterTableView.CommandItemDisplay = GridCommandItemDisplay.TopAndBottom;
        grid.MasterTableView.CommandItemSettings.ShowExportToCsvButton = true;
        grid.MasterTableView.CommandItemSettings.ShowExportToExcelButton = true;
        grid.MasterTableView.CommandItemSettings.ShowExportToPdfButton = true;
        grid.MasterTableView.CommandItemSettings.ShowExportToWordButton = true;
        grid.MasterTableView.PagerStyle.Mode = GridPagerMode.NextPrevNumericAndAdvanced;
        grid.PageSize = 60;
        // Changes for accessibility on a Grid Level
        // Remember hanidcap can be cognitive or physical -- so trim lots
        // of features
        if (Section508.UserSetting)
            grid.AllowCustomPaging = false;
            grid.AllowFilteringByColumn = false;
            grid.AllowMultiRowEdit = false;
            grid.AllowMultiRowSelection = false;
            grid.MasterTableView.AllowPaging = false;
            grid.MasterTableView.AllowSorting = false;
            grid.MasterTableView.CommandItemDisplay = GridCommandItemDisplay.None;
            grid.MasterTableView.CommandItemSettings.ShowExportToCsvButton = false;
            grid.MasterTableView.CommandItemSettings.ShowExportToExcelButton = false;
            grid.MasterTableView.CommandItemSettings.ShowExportToPdfButton = false;
            grid.MasterTableView.CommandItemSettings.ShowExportToWordButton = false;
            grid.MasterTableView.ShowGroupFooter = false;
            grid.ShowGroupPanel = false;
            grid.ShowStatusBar = false;
        int colNo = 0;
        foreach (GridColumn col in grid.MasterTableView.Columns)
            // uniquekeys are assumed to be compounded with an underscore
            //separator. Only last part is used as HeaderAbbr
            var translationkey = col.UniqueName;
            var parts = col.UniqueName.Split(sepUnique, System.StringSplitOptions.RemoveEmptyEntries);
            col.HeaderAbbr = parts[parts.Length - 1];
            // CHANGES for Accessibilty at a Column Level
            if (Section508.UserSetting)
                col.AutoPostBackOnFilter = false;
                col.Groupable = false;
                col.HeaderButtonType = GridHeaderButtonType.PushButton;
                col.Resizable = false;
            string tooltipMnemonic = string.Empty;
            //Determine if sortable and adopt appropriate tooltip
            if (grid.AllowSorting)
                if (! string.IsNullOrEmpty(col.SortExpression))
                    tooltipMnemonic = "Sort";
                tooltipMnemonic =string.Format(CultureInfo.CurrentCulture, "%ContentPage%{0}_{1}Tooltip%", translationkey,tooltipMnemonic);
                col.HeaderTooltip = ResourceTranslation.TranslateField(tooltipMnemonic);
            col.HeaderText = ResourceTranslation.TranslateField(string.Format(CultureInfo.CurrentCulture, "%ContentPage%{0}%", translationkey));


    The first item of importance is the event handler that is added

    grid.ItemCreated += grid_ItemCreatedAddRowScope;


    Grids require at least one <td scope=”row”> for Section 508 which is not natively provided by Telerik, so we must add it while the item is being created. This is done with the code below. The columns are those that are in the DataKeyNames, which makes it almost happens by designed-accident.

       1: /// <summary>
       2: /// Routine to add scope="row" to the grid for 508 compliance. The items are those specified in
       3: /// DataKeyNames
       4: /// </summary>
       5: /// <param name="sender">a telerik radgrid control</param>
       6: /// <param name="e"></param>
       7: static void grid_ItemCreatedAddRowScope(object sender, GridItemEventArgs e)
       8: {
       9:     if (e.Item is GridDataItem)
      10:     {
      11:         GridDataItem dataItem = e.Item as GridDataItem;
      12:         foreach (string key in dataItem.OwnerTableView.DataKeyNames)
      13:         {
      14:             foreach (GridColumn col in dataItem.OwnerTableView.Columns)
      15:             {
      16:                 if (col.IsBoundToFieldName(key))
      17:                 {
      18:                     TableCell cell = dataItem[col.UniqueName];
      19:                     cell.Attributes["scope"] = "row";
      20:                 }
      21:             }
      22:         }
      23:     }
      24: }


    The other item is the ResourceTranslation.TranslateField which is a widget between the code and the usual handling of resources. It allows me to identify if a resource is missing as well as do some fancy stuff like “lookup the term and if you do not find it, then use what Telerik provides”;


    That’s it.

    From one of my blogs,
  2. Yavor
    Yavor avatar
    11 posts

    Posted 24 Feb 2010 Link to this post

    Hello Ken,

    Thank you for posting your implementation to the public. I have transferred the code library entry to a forum post, so that other community members can benefit from it as well. Your Telerik points have been updated for your involvement. Thank you for your contribution.

    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  3. Mark Galbreath
    Mark Galbreath avatar
    171 posts
    Member since:
    Jul 2009

    Posted 24 Feb 2010 Link to this post

    A most excellent post, Ken!  Thanks for sharing!

Back to Top