Tuesday, May 4, 2010

Making Telerik RAD Grid Hierarchy at your ease

Hello,
Continuing with the topic on Telerik RAD Controls, I am herewith presenting few of my experiences on RAD Grid Hierarchy.This will greatly help reduce your time on the same.
  1. Telerik RAD Grid Hierarchy.
    It’s very simple to create a hierarchy using Telerik RAD Grid but there are few properties, events that we need to understand to make it work as desired.
    RAD Grid contains MasterTableView and Detail Tables using which hierarchy can be created by just providing a parent table relationship. It is obvious that the hierarchy can only be created with a relation between two tables.
    Master Table View:
    For e.g.: Consider the following snippet:
    AllowSorting="True" AutoGenerateColumns="False" PageSize="5" BorderWidth="0px"
    OnNeedDataSource="rgd_DataSource" OnItemDataBound ="rgd_ItemDataBound" OnDetailTableDataBind="rgd_DetailDataSource"
    OnItemCommand="rgd_ItemCommand"
    MasterTableView-ExpandCollapseColumn-CollapseImageUrl="~/images/Content/arrow_expanded.gif"
    MasterTableView-ExpandCollapseColumn-ExpandImageUrl="~/images/Content/arrow_collapsed.gif">



    PagerStyle-AlwaysVisible="true" CommandItemSettings-AddNewRecordText="Add New "
    AutoGenerateColumns="false" CommandItemDisplay="Bottom" Name="" DataKeyNames="Id,MemoryId,Title,Name,BusinessTitle,Purpose,Fax,Email,Phone">

    Master table View is bound at higher level of hierarchy. Data source of any kind can be bound here. It’s mandatory to define the DataKeyNames array. This holds all the fields or columns of the data source that is bound in this mastertableview.
    The RadGrid control has a MasterTableView property that represents the top table in the grid. The instances of RadGrid and MasterTableView are almost identical, although they are of different types (RadGrid and GridTableView, respectively).
    The main difference between RadGrid and MasterTableView is that the properties of RadGrid specify the defaults for every GridTableView in the grid (the MasterTableView and any DetailTables). The properties of MasterTableView apply only to the top-level table in the grid. In other words, property settings on MasterTableView are not inherited by any DetailTables nested inside it. The properties of MasterTableView, as with the properties of any DetailTable in the grid, act as overrides to the defaults set on the RadGrid object.
    For example, if we set blue border for the RadGrid object, the MasterTableView and any DetailTables will also have blue border (assuming they do not override the property setting), while if we set blue border for the MasterTableView, this border will appear only on the top-level table, and not on any detail tables.
    ?Always define Common attributes that need to apply to entire Grid including master and detail tables, Define them in Radgrid. We can also apply mastertable attributes in RAD Grid using prefix of mastertableview-commandItemDisplay etc. But it’s always better to define attributes of mastertableview and radgrid or any detailtables in their respective places which makes more readable and understandable.
    Grid Table View: There is one Grid Table View for each Detail Table in the grid. Grid Table View need to be bound to a datasource as we need to ensure that each Grid Table View in the grid has its own table within parent table.
    All the detail tables are added to Detail table collection as shown in following snippet:

    CommandItemDisplay="Bottom" CommandItemSettings-AddNewRecordText="Add New A"
    AllowPaging="false" HierarchyDefaultExpanded="false" DataKeyNames="TemporaryId,TempId,APurpose,A1,A2,PostalCode,City,CountryName">




    As we see in the above example, Details Tables contain a Grid Table View; each table is defined as a level in hierarchy.
    ?Make a thumb rule to define the columns of inner most table of hierarchy first in detailtables collection..Parent table relation must be defined as the hierarchy occurs.
    Ensure that each table in the grid has its own data source
    If we are using declarative data sources, set the DataSourceID property of each table to the ID of a DataSource object. For each parent table, set the DataKeyNames property to an array containing the names of each field in the table's data source that is used to link to detail tables. For each detail table, add GridRelationFields objects to the ParentTableRelation collection, each of which specifies a link between a field in the detail table to a field in the DataKeyNames array of the parent table.
    If we are not using declarative data sources, add event handlers for the NeedDataSource and DetailTableDataBind events. In the DetailTableDataBind event handler, we can determine which data source should be related to the current table view by checking whether the GetDataKeyValue method of the detail table's ParentItem returns a value.

    ?Hierarchical structure is not supported with simple data-binding (calling DataBind()).
    We cab dynamically bind the Grid properties by just declaring the RAD Grid statically. We can define the structure of a RadGrid control that is declared in the ASPX page, by using the Page_Load event handler. Columns and detail table should be added to the corresponding collection first, before the values for their properties are set. This is important because no ViewState is managed for the object before it has been added to the corresponding collection.

    ? Be sure to check that IsPostBack is false. Otherwise, we will end up adding the same objects to the RadGrid multiple times.
    We can also create the RAD Grid with its hierarchy dynamically. To generate RadGrid instance entirely in code, we should use the Page_Init event handler. Once the grid is created, add it to the Controls collection of a PlaceHolder control that is declared in the ASPX file. When generating a grid in the Page_Init event handler, grid columns should be added to the Controls collection of the MasterTableView after their attributes are set. No ViewState is required for grid structure to be persisted as it is recreated on each page initialization.
    Parent Table Relations: We need to define the ParentTableRelations and DataKeyNames for the MasterTableView and GridTableViews according to the database relations conventions. And here are the exact conventions:
    the primary key column name for each table in the grid source (used for master/detail table population) should be added to the DataKeyNames collection of the respective master/detail table
    the MasterKeyField in the GridRelationFields should match the primary key of the parent table in the corresponding relation
    the DetailKeyField in the GridRelationFields should match the foreign key of the child table in the corresponding relation
    There is one more detail if we use declarative binding using DataSource controls.We should have WHERE clause in the SelectCommand of the data source controls for the nested tables which to filter the records for them. The WHERE clause should include the field from the ParentTableRelation definition between the master/child table. Furthermore, that same field has to be included in the SelectParameters of the "inner" data source controls (with exactly the same Name and SessionField value). An important detail is that every GridRelationFields should have only one field name for DetailKeyField and MasterKeyField. For multi-hierarchy relations we can use multiple relation fields as in the example below:


    ?ParentTableRelation must be properly and correctly defined else the detail table or inner level data will not be loaded and there would be no proper error message or exception thrown.
    Events: Inorder to bind the Mastertable and Detailtable, the following two events need to be handled.

    OnNeedDataSource: This event is handled to populate the RadGrid MasterTableview with data. It gets fired when the grid is about to be bound with the data source that is assigned (is null/Nothing).

    Using this event eliminates the need for calling
    RadGrid.DataBind when the grid content should be refreshed, due to a structural change.For example if Edit command bubbles, grid will automatically rebind and display the item in edit mode, with no additional code.
    When we use NeedDataSource we need to assign manually the DataSource property only once in the event handler!

    We should never call Rebind() method in NeedDataSource event handler or DataBind() for the grid at any stage of the page lifecycle!
    Following are the cases when this event is fired inorder to help us understand when the grid is fetched for data:
    . Right after OnLoad - if the grid has not been data-bound yet and there is no ViewState data.
    This also means that if MasterTableView.DataSourcePersistenceMode has been set to NoPersistence, grid would bind each time the page loads (not only the first time);
    When paging operation occurs.
    When sorting operation occurs.
    When Edit command is fired.
    Right after Update/Delete/Insert command event handlers finish execution. You can cancel these operations handling ItemCommand event and assigning false value to the e event argument's Canceled property.
    When grouping / ungrouping - right after the RadGrid.GroupsChanging event is raised;
    When filtering (choosing an option from a column filter menu);
    When resorting a group.
    When a call to the Rebind() grid method takes place.
    Prior to any detail table binding.

    DetailTableDataBind: Fires when a detail-table in the hierarchy is about to be bound. You should only assign the DataSource property of the detail table to a data-source properly filtered to display only child records related to the parent item. We can find the instance of the detail table in the event argument (e). We can find the parent item using e.DetailTable.ParentItem property.

    Eg:
    protected void rgd_DetailDataSource(object source, GridDetailTableDataBindEventArgs e)
    {

    GridDataItem dataItem = (GridDataItem)e.DetailTableView.ParentItem;
    int selectedMemoryId = Convert.ToInt16(dataItem.GetDataKeyValue("MemoryId").ToString());
    int selectedId = Convert.ToInt16(dataItem.GetDataKeyValue("Id").ToString());
    if (ViewState[ViewStateConstants.OBJECT] != null)
    {
    try
    {
    Entity = (IList)ViewState[ViewStateConstants.];
    ViewState[ViewStateConstants.] = Entity;
    if (selectedId == 0)
    {
    foreach (Entity tempEntity in Entity)
    {
    IList aEntity = new List();

    if (tempEntity.MemoryId == selectedMemoryId)
    {
    aEntity = tempEntity.A;
    e.DetailTableView.DataSource = aEntity;
    break;

    }

    }
    }
    }

    catch
    {
    lblMsg.Text = "Unable to load";
    }
    }
    }

    Handy Properties / Methods :
    It is always better to name each table like MasterTableView or Detail Grid Table view to identify in code behind to handle the logic, e.Item.OwnerTableView.Name can be used to compare the name of the table that is provided.
    GridEditableItem editedItem = e.Item as GridEditableItem;
    editedItem.GetDataKeyValue("MemoryId");
    The above lines are used to retrieve the column value of Column “MemoryId” provided this key is defined in DataKeyNames array while creating the MasterTableView.

    GridDataItem dataItem = (GridDataItem)e.DetailTableView.ParentItem;
    int selectedMemoryId = Convert.ToInt16(dataItem.GetDataKeyValue("MemoryId").ToString());

    The above lines are used to retrieve the value of column of “MemoryId” while loading detail table and parent or mastertable parentkey is required to filter data. This can be used in the event DetailTableDataBind.
    To hide the commandItemDisplay in the grid this needs to be handled in ItemDataBound event. Following lines hides them in both MasterTable and Child table:
    rgd.MasterTableView.CommandItemDisplay = GridCommandItemDisplay.None;
    rgd.MasterTableView.DetailTables[0].CommandItemDisplay = GridCommandItemDisplay.None;

    We can hide the edit or delete columns which again needs to be done in ItemDataBound event. Following lines of code demonstrates them for detail table:
    GridColumn editColumn = rgd.MasterTableView.DetailTables[0].GetColumn("TemporaryId");
    editColumn.Visible = false;

    GridColumn deleteColumn = rgd.MasterTableView.DetailTables[0].GetColumn("Deletecolumn");
    deleteColumn.Visible = false;

    Key string provided in GetColumn method is the uniquename provided as shown below in html code:



    Refresh and Rebind buttons can be hidden by using the following snippet:
    GridCommandItem cmditm = (GridCommandItem)e.Item;
    cmditm.FindControl("RefreshButton").Visible = false; cmditm.FindControl("RebindGridButton").Visible = false;
    Conclusion: There are many more properties and methods which can be explored on the same lines as explained above. This documents can be used handy to create a hierarchy using Telerik RAD Grid. Self Referencing Hierarchy works in a different way in Telerik RAD Grid which I would document soon to help you better.
    Happy Coding & Exploring !

4 comments:

  1. Excellent article, I have a better understanding of the Telerik grid now. I'm getting started with heirarchy grids and am having trouble determining which grid raised the edit event. It's a simple master detail grid but I had code written to handle the master grid edit event which crashed when I added a detail grid and tried to edit it. Any ideas?

    ReplyDelete
  2. Hi Alan,
    Thank you for reading the article.

    I hope you are written itemCommand handler and On needDataSource handler

    If yes, please let me know if you are explicitly calling the DataBind() method anywhere.

    ReplyDelete
  3. how to create detail table view dynamically

    ReplyDelete