I have a web page with a RadGrid in Batch Edit mode. The number and width of the columns makes the grid wider than the browser window, so it scrolls horizontally. I followed this example to allow me to TAB from cell to cell in the table and to scroll the grid horizontally as the cursor moves from column to column. When I get to the last cell in a row, pressing TAB moves me to the next row, but it moves me to the first visible column on the row rather than scrolling the page all the way back to the left edge and moving me to the first column.
Is there a way to reset the horizontal scrolling when I TAB from the last cell so it goes back to the left most column?
3 Answers, 1 is accepted
Attila,
Here is the re-work of the testPage.zip example from the post I referenced in the problem description. It is inelegant but it gets scrolling working using TAB and SHIFT-TAB when using frozen columns and static headers.
Note: It relies on every column having a UNIQUE NAME.
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server" />
</head>
<body>
<form id="form1" runat="server">
<telerik:RadScriptManager ID="RadScriptManager1" runat="server">
<Scripts>
<asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.Core.js" />
<asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQuery.js" />
<asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQueryInclude.js" />
</Scripts>
</telerik:RadScriptManager>
<script type="text/javascript">
function OnGridCreated(sender, args)
{
// Resize columns to fit data
var grid = $find("RadGrid1");
var columns = grid.get_masterTableView().get_columns();
for (var i = 0; i < columns.length; i++) {
columns[i].resizeToFit();
}
// Assign function to recalculate scrolling
var frozenScroll = $get(grid.get_id() + "_Frozen");
var allColumns = grid.get_masterTableView().get_columns();
var scrollLeftOffset = 0;
var allColumnsWidth = new Array;
var lastWidth = 0;
for(var i = 0; i < allColumns.length; i++)
{
allColumnsWidth[i] = allColumns[i].get_element().offsetWidth;
}
$get(grid.get_id() + "_GridData").onscroll = function (e)
{
for(var i = 0; i < allColumns.length; i++)
{
if(!allColumns[i].get_visible())
{
scrollLeftOffset += allColumnsWidth[i];
lastWidth = allColumnsWidth[i];
}
}
var thisScrollLeft = this.scrollLeft;
this.scrollLeft = 0;
if (thisScrollLeft > 0)
{
// To insure the current cell is not on the right edge, add the last column's width
frozenScroll.scrollLeft = thisScrollLeft + scrollLeftOffset + lastWidth;
}
scrollLeftOffset = 0;
}
}
function OnKeyDown(event)
{
// Look for TAB
if (event.which == 9)
{
var grid = $find("RadGrid1");
var columnName = event.target.name.split("$TB_")[1];
var masterTableView = grid.get_masterTableView();
var column = masterTableView.getColumnByUniqueName(columnName);
var element = column.get_element();
var index = element.cellIndex;
var numColumns = grid.get_masterTableView().get_columns().length - 1;
var frozenScroll = $get(grid.get_id() + "_Frozen");
if (event.shiftKey == false)
{
if (index == numColumns)
{
// Reset scroll position to left edge
frozenScroll.scrollLeft = 0;
}
}
else
{
// Reduce the scroll position by the current cell's width
frozenScroll.scrollLeft = frozenScroll.scrollLeft - column.get_element().offsetWidth;
}
}
}
</script>
<telerik:RadAjaxManager ID="RadAjaxManager1" runat="server">
</telerik:RadAjaxManager>
<div>
<telerik:RadGrid ID="RadGrid1" runat="server"
OnNeedDataSource="RadGrid1_NeedDataSource"
AutoGenerateColumns="False" onkeydown="OnKeyDown(event);">
<ClientSettings AllowKeyboardNavigation="True">
<Scrolling FrozenColumnsCount="2" AllowScroll="True" ScrollHeight="600px" UseStaticHeaders="True"></Scrolling>
<Selecting CellSelectionMode="None" AllowRowSelect="True"></Selecting>
<ClientEvents OnGridCreated="OnGridCreated" />
<Resizing AllowColumnResize="True" AllowResizeToFit="True" ClipCellContentOnResize="False" />
</ClientSettings>
<AlternatingItemStyle Wrap="True" BackColor="Gainsboro"></AlternatingItemStyle>
<MasterTableView CommandItemDisplay="TopAndBottom" DataKeyNames="PeopleID" EditMode="Batch" TableLayout="Fixed">
<BatchEditingSettings EditType="Row" />
<Columns>
</Columns>
<CommandItemSettings ShowAddNewRecordButton="False" />
</MasterTableView>
<HeaderStyle VerticalAlign="Bottom" Font-Bold="True" BackColor="DarkSeaGreen" Wrap="False"></HeaderStyle>
<ActiveItemStyle Wrap="True"></ActiveItemStyle>
</telerik:RadGrid>
</div>
</form>
</body>
</html>
using System;
using Telerik.Web.UI;
using System.Data;
public partial class Default : System.Web.UI.Page
{
protected void RadGrid1_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
DataTable dt = new DataTable();
DataRow dr;
int rowsNum = 130;
string[][] colsInfo = {
new string[] { "PeopleID", "number" },
new string[] { "Custom1", "string" },
new string[] { "Custom2", "string" },
new string[] { "Custom3", "string" },
new string[] { "Custom4", "string" },
new string[] { "Custom5", "string" },
new string[] { "Custom6", "string" },
new string[] { "Custom7", "string" },
new string[] { "Custom8", "string" },
new string[] { "Custom9", "string" },
new string[] { "Custom10", "string" },
new string[] { "Custom11", "string" },
new string[] { "Custom12", "string" },
new string[] { "Custom13", "string" },
new string[] { "Custom14", "string" },
new string[] { "Custom15", "string" },
new string[] { "Custom16", "string" },
new string[] { "Custom17", "string" },
new string[] { "Custom18", "string" },
new string[] { "Custom19", "string" },
new string[] { "Custom20", "string" },
};
for (int i = 0; i < colsInfo.Length; i++)
{
GridBoundColumn newColumn = new GridBoundColumn();
newColumn.DataField = colsInfo[i][0];
newColumn.UniqueName = colsInfo[i][0];
newColumn.HeaderText = "*" + colsInfo[i][0] + "*";
RadGrid1.MasterTableView.Columns.Add(newColumn);
switch (colsInfo[i][1])
{
case "string":
dt.Columns.Add(new DataColumn(colsInfo[i][0], typeof(String)));
break;
case "number":
dt.Columns.Add(new DataColumn(colsInfo[i][0], typeof(Int32)));
break;
case "date":
dt.Columns.Add(new DataColumn(colsInfo[i][0], typeof(DateTime)));
break;
case "bool":
dt.Columns.Add(new DataColumn(colsInfo[i][0], typeof(Boolean)));
break;
default:
break;
}
}
for (int j = 1; j <= rowsNum; j++)
{
dr = dt.NewRow();
for (int k = 0; k < colsInfo.Length; k++)
{
switch (colsInfo[k][1])
{
case "string":
dr[colsInfo[k][0]] = String.Format("{0} Row{1}", colsInfo[k][0], j);
break;
case "number":
dr[colsInfo[k][0]] = j;
break;
case "date":
dr[colsInfo[k][0]] = DateTime.Now;
break;
case "bool":
dr[colsInfo[k][0]] = j % 2 == 1 ? true : false;
break;
default:
break;
}
}
dt.Rows.Add(dr);
}
(sender as RadGrid).DataSource = dt;
}
}
Hi Thomas,
The solution in the articles you shared was provided for a specific scenario. You might be using an entirely different scenario, hence, that code will not be compatible.
RadGrid by default can handle the Scrolling when Tabbing and there is no need to implement it additionally. You can check out the attached GIF image to see it in action.
Here is the Grid setup
<telerik:RadGrid ID="RadGrid1" runat="server" AllowPaging="True" Width="800px" OnNeedDataSource="RadGrid1_NeedDataSource">
<ClientSettings AllowKeyboardNavigation="true">
<Scrolling AllowScroll="true" UseStaticHeaders="true" />
</ClientSettings>
<MasterTableView AutoGenerateColumns="False" DataKeyNames="OrderID" EditMode="Batch" TableLayout="Fixed">
<HeaderStyle Width="300px" />
<Columns>
<telerik:GridBoundColumn DataField="OrderID" DataType="System.Int32"
FilterControlAltText="Filter OrderID column" HeaderText="OrderID"
ReadOnly="True" SortExpression="OrderID" UniqueName="OrderID">
<HeaderStyle Width="100px" />
</telerik:GridBoundColumn>
<telerik:GridDateTimeColumn DataField="OrderDate" DataType="System.DateTime"
FilterControlAltText="Filter OrderDate column" HeaderText="OrderDate"
SortExpression="OrderDate" UniqueName="OrderDate">
</telerik:GridDateTimeColumn>
<telerik:GridNumericColumn DataField="Freight" DataType="System.Decimal"
FilterControlAltText="Filter Freight column" HeaderText="Freight"
SortExpression="Freight" UniqueName="Freight">
</telerik:GridNumericColumn>
<telerik:GridBoundColumn DataField="ShipName"
FilterControlAltText="Filter ShipName column" HeaderText="ShipName"
SortExpression="ShipName" UniqueName="ShipName">
</telerik:GridBoundColumn>
<telerik:GridBoundColumn DataField="ShipCountry"
FilterControlAltText="Filter ShipCountry column" HeaderText="ShipCountry"
SortExpression="ShipCountry" UniqueName="ShipCountry">
</telerik:GridBoundColumn>
</Columns>
</MasterTableView>
</telerik:RadGrid>
<script runat="server">
protected void RadGrid1_NeedDataSource(object sender, GridNeedDataSourceEventArgs e)
{
(sender as RadGrid).DataSource = Enumerable.Range(1, 10).Select(x => new
{
OrderID = x,
OrderDate = DateTime.Now.Date.AddDays(x),
Freight = x * .1,
ShipName = "Name " + x,
ShipCountry = "Country " + x
});
}
</script>
If this doesn't work for you, then you likely have a scenario specific to your requirements and will require a different approach.
Can you share the reason you had to implement the JavaScript from the other forum post? We might be able to give you some advice.
Regards,
Attila Antal
Progress Telerik
Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.
Hello Attila,
I want to have frozen columns AND static headers, so no matter which way I scroll, they will stay on the screen. This link seems to say that what I am trying to do is unsupported.
When frozen columns are used, tabbing between the textboxes in an inline
edit form is not supported out-of-the-box, because the frozen columns
will be scrolled together with the non-frozen. In selected scenarios,
this functionality can be achieved if you subscribe to the textboxes'
focus events and scroll a specific <div>
with Javascript. This <div>
has a client ID of <RadGridInstance.ClientID>_Frozen
.
When doing this, you should take into account the current scroll
position, and the width of the column that should be hidden/shown.
However, when I implemented the JavaScript from the other post it did not scroll left when I used SHIFT TAB and when the TAB reaches the right end of the row, it doesn't go to the first record of the next line - it goes to the first visible column of the next line.
Hello Thomas,
Good findings! Thanks for sharing it with the community!
As a token of gratitude, I have awarded you 1000 Telerik points.
The code you wrote is elegant enough. If you wanted to make it more elegant, you could improve that with a little bit of jQuery.
Below you will see the improvements I made. I have left comments to show the details for the things that I did differently. Of course, even this can be further improved if really needed.
RadGrid markup
<telerik:RadGrid ID="RadGrid1" runat="server"
OnNeedDataSource="RadGrid1_NeedDataSource"
AutoGenerateColumns="False">
<ClientSettings AllowKeyboardNavigation="True">
<Scrolling FrozenColumnsCount="2" AllowScroll="True" ScrollHeight="600px" UseStaticHeaders="True"></Scrolling>
<Selecting CellSelectionMode="None" AllowRowSelect="True"></Selecting>
<ClientEvents OnGridCreated="OnGridCreated" />
<Resizing AllowColumnResize="True" AllowResizeToFit="True" ClipCellContentOnResize="False" />
</ClientSettings>
<AlternatingItemStyle Wrap="True" BackColor="Gainsboro"></AlternatingItemStyle>
<MasterTableView CommandItemDisplay="TopAndBottom" DataKeyNames="PeopleID" EditMode="Batch" TableLayout="Fixed">
<BatchEditingSettings EditType="Row" />
<Columns>
</Columns>
<CommandItemSettings ShowAddNewRecordButton="False" />
</MasterTableView>
<HeaderStyle VerticalAlign="Bottom" Font-Bold="True" BackColor="DarkSeaGreen" Wrap="False"></HeaderStyle>
<ActiveItemStyle Wrap="True"></ActiveItemStyle>
</telerik:RadGrid>
JavaScript
function OnGridCreated(sender, args) {
var grid = sender; // Telerik.Web.UI.RadGrid
var gridElement = grid.get_element(); // <div class="RadGrid"></div>
// Attach the keydown event
// https://api.jquery.com/on/
$telerik.$(gridElement).on('keydown', OnKeyDown);
var masterTable = grid.get_masterTableView();
var columns = masterTable.get_columns();
// Resize all Columns
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
columns.forEach(col => col.resizeToFit());
// Collect the width of all columns
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
var allColumnsWidth = columns.map(col => col.get_element().offsetWidth);
// Get the Frozen Scroll
// https://api.jquery.com/attribute-ends-with-selector/#attributevalue
var frozenScroll = $telerik.$(gridElement).find('div[id$=_Frozen]').get(0); // <div id="RadGrid1_Frozen"></div>
var scrollLeftOffset = 0;
var lastWidth = 0;
// grid.GridDataDiv = <div id="RadGrid1_GridData" class="rgDataDiv">
// https://docs.telerik.com/devtools/aspnet-ajax/controls/grid/how-to/Scrolling/changing-grid-scrollheight-at-runtime-to-fill-its-container-height
$telerik.$(grid.GridDataDiv).on('scroll', function (e) {
for (var i = 0; i < columns.length; i++) {
if (!columns[i].get_visible()) {
scrollLeftOffset += allColumnsWidth[i];
lastWidth = allColumnsWidth[i];
}
}
var thisScrollLeft = this.scrollLeft;
this.scrollLeft = 0;
if (thisScrollLeft > 0) {
// To insure the current cell is not on the right edge, add the last column's width
frozenScroll.scrollLeft = thisScrollLeft + scrollLeftOffset + lastWidth;
}
scrollLeftOffset = 0;
})
}
function OnKeyDown(event) {
// Look for TAB
if (event.which == 9) {
// https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
var gridElement = event.currentTarget; // <div class="RadGrid"></div>
// https://docs.telerik.com/devtools/aspnet-ajax/general-information/get-client-side-reference#using-plain-javascript-methods
var grid = gridElement.control; // Telerik.Web.UI.RadGrid
var masterTableView = grid.get_masterTableView();
// https://developer.mozilla.org/en-US/docs/Web/API/Event/target
var target = event.target;
// https://api.jquery.com/closest/
var currentCell = $telerik.$(target).closest('td.rgBatchCurrent').get(0); // <td class="rgBatchCurrent"></td>
if (!currentCell) return;
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement
var cellIndex = currentCell.cellIndex;
var numColumns = masterTableView.get_columns().length - 1;
var frozenScroll = $telerik.$(gridElement).find('div[id$=_Frozen]').get(0); // <div id="RadGrid1_Frozen"></div>
if (!event.shiftKey) {
if (cellIndex == numColumns) {
// Reset scroll position to left edge
frozenScroll.scrollLeft = 0;
}
}
else {
// Reduce the scroll position by the current cell's width
frozenScroll.scrollLeft = frozenScroll.scrollLeft - currentCell.offsetWidth;
}
}
}
Regards,
Attila Antal
Progress Telerik
Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.