The solution below illustrates how to implement treeview/combobox hybrid editor and dependent load-on-demand combobox editors in RadGrid for ASP.NET AJAX. The demo files can be downloaded from here.
The first type editor is useful when you would like to visualize hierarchical treeview structure for easier navigation among the combobox options when editing cell values (if applicable).
The second scenario is preferred when one wants to reduce the options in a combobox editor based on the selection in another dropdown editor (using the integrated callback mechanism of the filtered combo), thus receiving some performance gains since only the combobox is refreshed as opposed to the entire grid.
Examine the code implementation and the comments in the source for more details.
JavaScript
<style type="text/css">
div.RadComboBoxDropDown.rcbItem{margin:0;padding:0;}</style><telerik:RadScriptBlockID="RadScriptBlock1" runat="server"><script type="text/javascript">//stop the event bubblingfunctionStopPropagation(e){if(!e){
e =window.event;}
e.cancelBubble=true;}//find the selected node in the treeview inside the combobox and scroll it into viewfunctionOnClientDropDownOpenedHandler(sender, eventArgs){var tree = sender.get_items().getItem(0).findControl("RadTreeView1");var selectedNode = tree.get_selectedNode();if(selectedNode){
selectedNode.scrollIntoView();}}//when tree node is clicked, set the text and value for the item in the combobox and commit the changesfunctionnodeClicking(sender, args){//get the id of the employeesCombo in the edited row (passed from the server in the ItemDataBound event handler)var comboBox =$find(window['comboId']);var node = args.get_node();
comboBox.set_text(node.get_text());
comboBox.trackChanges();
comboBox.get_items().getItem(0).set_text(node.get_text());
comboBox.get_items().getItem(0).set_value(node.get_value());
comboBox.commitChanges();
comboBox.hideDropDown();// Call comboBox.attachDropDown if:// 1) The RadComboBox is inside an AJAX panel.// 2) The RadTreeView has a server-side event handler for the NodeClick event, i.e. it initiates a postback when clicking on a Node.// Otherwise the AJAX postback becomes a normal postback regardless of the outer AJAX panel.
comboBox.attachDropDown();}functionfreightComboClientSelectedIndexChangedHandler(sender, eventArgs){//get reference to the grid row DOM elementvar gridRow = sender.get_element().parentNode.parentNode;//locate the customers combobox in the same row using the $telerik.findControl method from the Telerik Client Static Library//note that the id of the combobox concatenates RCB_ + UniqueName value for the column, i.e. RCB_CustomerName in this particular casevar customersCombo = $telerik.findControl(gridRow,"RCB_CustomerName");// this will fire the ItemsRequested server event and hook the OnClientItemsRequested client event of the// customers combobox passing the freight as a parameter to the first event
customersCombo.add_itemsRequested(customersComboItemsRequested);
customersCombo.requestItems(eventArgs.get_item().get_value(),false);}functioncustomersComboItemsRequested(sender, eventArgs){if(sender.get_items().get_count()>0){// pre-select the first item
sender.findItemByText(sender.get_items().getItem(0).get_text()).select();}//detach the client items requested event as it not needed any more
sender.remove_itemsRequested(customersComboItemsRequested);}</script></telerik:RadScriptBlock>
ASP.NET
<telerik:RadScriptManagerID="RadScriptManager1"runat="server"/><telerik:RadAjaxManagerrunat="server"ID="RadAjaxManager1"EnableAJAX="true"><AjaxSettings><telerik:AjaxSettingAjaxControlID="RadGrid1"><UpdatedControls><telerik:AjaxUpdatedControlControlID="RadGrid1"LoadingPanelID="RadAjaxLoadingPanel1"/><telerik:AjaxUpdatedControlControlID="lblMessage"/></UpdatedControls></telerik:AjaxSetting></AjaxSettings></telerik:RadAjaxManager><telerik:RadAjaxLoadingPanelID="RadAjaxLoadingPanel1"runat="server"/><asp:LabelID="Label1"runat="server"EnableViewState="false"Font-Bold="true"Text="Orders List"/><telerik:RadGridRenderMode="Lightweight"ID="RadGrid1"AutoGenerateColumns="false"PageSize="15"AllowPaging="true"AllowSorting="true"runat="server"DataSourceID="OrdersDataSource"AllowAutomaticUpdates="true"AllowAutomaticInserts="True"ShowStatusBar="true"OnItemDataBound="RadGrid1_ItemDataBound"OnUpdateCommand="RadGrid1_UpdateCommand"OnInsertCommand="RadGrid1_InsertCommand"OnItemCreated="RadGrid1_ItemCreated"onitemcommand="RadGrid1_ItemCommand"><MasterTableViewDataKeyNames="OrderID"EditMode="InPlace"CommandItemDisplay="TopAndBottom"TableLayout="Fixed"><Columns><telerik:GridBoundColumnUniqueName="OrderID"DataField="OrderID"HeaderText="OrderID"ReadOnly="true"/><telerik:GridDropDownColumnListTextField="Freight"ListValueField="Freight"DataField="Freight"DataSourceID="ComboOrdersDataSource"HeaderText="Freight"UniqueName="Freight"AllowAutomaticLoadOnDemand="true"/><telerik:GridTemplateColumnUniqueName="LastName"HeaderText="Employee Name"SortExpression="LastName"ItemStyle-Width="400px"><ItemTemplate><%#DataBinder.Eval(Container.DataItem,"LastName")%></ItemTemplate><EditItemTemplate><!-- Show employees in the treeview --><telerik:RadComboBoxRenderMode="Lightweight"ID="RadComboBox1"runat="server"Width="180px"ShowToggleImage="True"Style="vertical-align: middle;"OnClientDropDownOpened="OnClientDropDownOpenedHandler"ExpandAnimation-Type="None"CollapseAnimation-Type="None"><ItemTemplate><divid="div1"onclick="StopPropagation(event);"><telerik:RadTreeViewRenderMode="Lightweight"runat="server"ID="RadTreeView1"OnClientNodeClicking="nodeClicking"Height="140px"DataTextField="LastName"DataValueField="EmployeeID"DataFieldID="EmployeeID"DataFieldParentID="ReportsTo"DataSourceID="EmployeesDataSource"/></div></ItemTemplate><Items><telerik:RadComboBoxItemText=""/></Items></telerik:RadComboBox></EditItemTemplate></telerik:GridTemplateColumn><telerik:GridDropDownColumnListTextField="ContactName"ListValueField="CustomerID"DataField="CustomerID"DataSourceID="CustomersDataSource"HeaderText="Customer Name"UniqueName="CustomerName"AllowAutomaticLoadOnDemand="true"/><telerik:GridEditCommandColumnButtonType="ImageButton"HeaderStyle-Width="60px"/></Columns></MasterTableView></telerik:RadGrid><asp:LabelID="lblMessage"runat="server"EnableViewState="false"Font-Bold="true"/><asp:SqlDataSourceID="OrdersDataSource"runat="server"ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"SelectCommand="SELECT orders.[OrderID], orders.[EmployeeID], orders.[CustomerID], orders.[Freight],
employees.[EmployeeID], employees.[LastName]
FROM [Orders] AS orders
INNER JOIN [Employees] AS employees
ON orders.EmployeeID = employees.EmployeeID"InsertCommand="INSERT INTO [Orders] ([EmployeeID], [CustomerID],[Freight]) VALUES (@EmployeeID, @CustomerID, @Freight)"UpdateCommand="UPDATE [Orders] SET [EmployeeID] = @EmployeeID, [CustomerID] = @CustomerID, [Freight] = @Freight WHERE [OrderID] = @OrderID"><InsertParameters><asp:ParameterName="EmployeeID"Type="Int16"/><asp:ParameterName="CustomerID"Type="String"/><asp:ParameterName="Freight"Type="String"/></InsertParameters><UpdateParameters><asp:ParameterName="EmployeeID"Type="Int16"/><asp:ParameterName="CustomerID"Type="String"/><asp:ParameterName="Freight"Type="String"/></UpdateParameters></asp:SqlDataSource><asp:SqlDataSourceID="CustomersDataSource"runat="server"ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"SelectCommand="SELECT [CustomerID], [ContactName] FROM [Customers] Order By ContactName"/><asp:SqlDataSourceID="ComboOrdersDataSource"runat="server"ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"SelectCommand="SELECT DISTINCT [Freight] FROM [Orders] Order By Freight"/><asp:SqlDataSourceID="EmployeesDataSource"runat="server"ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"SelectCommand="SELECT [EmployeeID], [LastName], [ReportsTo] FROM [Employees]"/>
C#
protectedvoidRadGrid1_ItemCreated(object sender,GridItemEventArgs e){//if a grid item is in edit mode, wire the ItemsRequested event of the customersCombo to filter the items in it based on the selection in the freight comboif(e.Item isGridEditableItem&& e.Item.IsInEditMode){RadComboBox freightCombo =(e.Item asGridEditableItem)["Freight"].Controls[0]asRadComboBox;if(Page.IsCallback){RadComboBox customersCombo =(e.Item asGridEditableItem)["CustomerName"].Controls[0]asRadComboBox;
customersCombo.ItemsRequested +=newRadComboBoxItemsRequestedEventHandler(customersCombo_ItemsRequested);}}}protectedvoidRadGrid1_ItemDataBound(object sender,GridItemEventArgs e){if(e.Item isGridEditableItem&& e.Item.IsInEditMode &&(!e.Item.OwnerTableView.IsItemInserted)){//wire the OnClientSelectedIndexChanged event of the first LOD combo editor to initiate callback on item selectionRadComboBox freightCombo =(e.Item asGridEditableItem)["Freight"].Controls[0]asRadComboBox;
freightCombo.OnClientSelectedIndexChanged ="freightComboClientSelectedIndexChangedHandler";//set the selected value for the Employees combobox to match the value in the edited cellRadComboBox employeesCombo =(e.Item asGridEditableItem).FindControl("RadComboBox1")asRadComboBox;
employeesCombo.Items[0].Text = DataBinder.Eval(e.Item.DataItem,"LastName").ToString();//set the selection in the treeview inside the combobox to match the value in the edited cellRadTreeView employeesTreeView = employeesCombo.Items[0].FindControl("RadTreeView1")asRadTreeView;
employeesTreeView.FindNodeByText(DataBinder.Eval(e.Item.DataItem,"LastName").ToString()).Selected =true;//Expand all nodes to scroll the selected into view. Have in mind that load-on-demand is recommended with very large number of nodes in the treeview
employeesTreeView.ExpandAllNodes();//register script block which holds the client id of the employeesComboBox. Will be used to reference the client object of the combobox in the RadTreeView's OnClientNodeClicking event handler
RadScriptManager.RegisterClientScriptBlock(this.Page,typeof(Page),"employeesComboScript","<script type='text/javascript'>window['comboId'] = '"+ e.Item.FindControl("RadComboBox1").ClientID +"';</script>",false);}elseif(e.Item isGridDataInsertItem){//wire the OnClientSelectedIndexChanged event of the first LOD combo editor to initiate callback on item selectionRadComboBox freightCombo =(e.Item asGridDataInsertItem)["Freight"].Controls[0]asRadComboBox;
freightCombo.OnClientSelectedIndexChanged ="freightComboClientSelectedIndexChangedHandler";//register script block which holds the client id of the employeesComboBox. Will be used to reference the client object of the combobox in the RadTreeView's OnClientNodeClicking event handler
RadScriptManager.RegisterClientScriptBlock(this.Page,typeof(Page),"employeesComboScript","<script type='text/javascript'>window['comboId'] = '"+ e.Item.FindControl("RadComboBox1").ClientID +"';</script>",false);}}protectedvoidcustomersCombo_ItemsRequested(object sender,RadComboBoxItemsRequestedEventArgs e){LoadFilteredCustomersManually(e.Text, sender asRadComboBox);}protectedvoidLoadFilteredCustomersManually(string freight,RadComboBox customersCombo){//if the orderID value cannot be parsed as integer(i.e. auto LOD is triggered), exit the handlertry{
System.Double.Parse(freight);}catch{return;}SqlConnection connection =newSqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString);
connection.Open();//select customer name based on the orderIDSqlCommand comm =newSqlCommand("SELECT CustomerID FROM [Orders] WHERE Freight=@Freight", connection);
comm.Parameters.AddWithValue("@Freight", freight);string cid = comm.ExecuteScalar().ToString();
comm =newSqlCommand("SELECT CustomerID, ContactName from [Customers] WHERE CustomerID = '"+ cid +"'", connection);SqlDataAdapter adapter =newSqlDataAdapter(comm);DataTable dt =newDataTable();
adapter.Fill(dt);//populate the customers combo with the new set of items
customersCombo.Items.Clear();foreach(DataRow row in dt.Rows){
customersCombo.Items.Add(newRadComboBoxItem(row["ContactName"].ToString(), row["CustomerID"].ToString()));}
connection.Close();}protectedvoidRadGrid1_UpdateCommand(object sender,GridCommandEventArgs e){string empId =(((e.Item asGridEditableItem).FindControl("RadComboBox1")asRadComboBox).Items[0].FindControl("RadTreeView1")asRadTreeView).SelectedNode.Value;//if the EmployeeID value is empty, cancel the update operationif(empId ==string.Empty){
e.Canceled =true;SetMessage("Order cannot be updated. You must select an employee from the provided list");}else{
OrdersDataSource.UpdateParameters["EmployeeID"].DefaultValue = empId;}}protectedvoidRadGrid1_InsertCommand(object sender,GridCommandEventArgs e){//if the EmployeeID value is empty, cancel the insert operationif((((e.Item asGridEditableItem).FindControl("RadComboBox1")asRadComboBox).Items[0].FindControl("RadTreeView1")asRadTreeView).SelectedNode ==null){
e.Canceled =true;SetMessage("Order cannot be inserted. You must select an employee from the provided list");}else{string empId =(((e.Item asGridEditableItem).FindControl("RadComboBox1")asRadComboBox).Items[0].FindControl("RadTreeView1")asRadTreeView).SelectedNode.Value;
OrdersDataSource.InsertParameters["EmployeeID"].DefaultValue = empId;}}protectedvoidRadGrid1_ItemCommand(object sender,GridCommandEventArgs e){//switch the edit/insert modes to ensure that only one operation will be processed at given timeif(e.CommandName == RadGrid.EditCommandName){
e.Item.OwnerTableView.IsItemInserted =false;}elseif(e.CommandName == RadGrid.InitInsertCommandName){
RadGrid1.EditIndexes.Clear();}}publicvoidSetMessage(string message){
lblMessage.Text = message;}
VB
ProtectedSub RadGrid1_ItemCreated(ByVal sender AsObject,ByVal e As GridItemEventArgs)Handles RadGrid1.ItemCreated
'if a grid item is in edit mode, wire the ItemsRequested event of the customersCombo to filter the items in it based on the selection in the freight comboIfTypeOf e.Item Is GridEditableItem AndAlso e.Item.IsInEditMode ThenDim freightCombo As RadComboBox =TryCast(TryCast(e.Item, GridEditableItem)("Freight").Controls(0), RadComboBox)If Page.IsCallback ThenDim customersCombo As RadComboBox =TryCast(TryCast(e.Item, GridEditableItem)("CustomerName").Controls(0), RadComboBox)AddHandler customersCombo.ItemsRequested,AddressOf customersCombo_ItemsRequested
EndIfEndIfEndSubProtectedSub RadGrid1_ItemDataBound(ByVal sender AsObject,ByVal e As GridItemEventArgs)Handles RadGrid1.ItemDataBound
IfTypeOf e.Item Is GridEditableItem AndAlso e.Item.IsInEditMode AndAlso(Not e.Item.OwnerTableView.IsItemInserted)Then'wire the OnClientSelectedIndexChanged event of the first LOD combo editor to initiate callback on item selectionDim freightCombo As RadComboBox =TryCast(TryCast(e.Item, GridEditableItem)("Freight").Controls(0), RadComboBox)
freightCombo.OnClientSelectedIndexChanged ="freightComboClientSelectedIndexChangedHandler"'set the selected value for the Employees combobox to match the value in the edited cellDim employeesCombo As RadComboBox =TryCast(TryCast(e.Item, GridEditableItem).FindControl("RadComboBox1"), RadComboBox)
employeesCombo.Items(0).Text= DataBinder.Eval(e.Item.DataItem,"LastName").ToString()'set the selection in the treeview inside the combobox to match the value in the edited cellDim employeesTreeView As RadTreeView =TryCast(employeesCombo.Items(0).FindControl("RadTreeView1"), RadTreeView)
employeesTreeView.FindNodeByText(DataBinder.Eval(e.Item.DataItem,"LastName").ToString()).Selected =True'Expand all nodes to scroll the selected into view. Have in mind that load-on-demand is recommended with very large number of nodes in the treeview
employeesTreeView.ExpandAllNodes()'register script block which holds the client id of the employeesComboBox. Will be used to reference the client object of the combobox in the RadTreeView's OnClientNodeClicking event handler
RadScriptManager.RegisterClientScriptBlock(Me.Page,GetType(Page),"employeesComboScript","<script type='text/javascript'>window['comboId'] = '"+ e.Item.FindControl("RadComboBox1").ClientID +"';</script>",False)ElseIfTypeOf e.Item Is GridDataInsertItem Then'wire the OnClientSelectedIndexChanged event of the first LOD combo editor to initiate callback on item selectionDim freightCombo As RadComboBox =TryCast(TryCast(e.Item, GridDataInsertItem)("Freight").Controls(0), RadComboBox)
freightCombo.OnClientSelectedIndexChanged ="freightComboClientSelectedIndexChangedHandler"'register script block which holds the client id of the employeesComboBox. Will be used to reference the client object of the combobox in the RadTreeView's OnClientNodeClicking event handler
RadScriptManager.RegisterClientScriptBlock(Me.Page,GetType(Page),"employeesComboScript","<script type='text/javascript'>window['comboId'] = '"+ e.Item.FindControl("RadComboBox1").ClientID +"';</script>",False)EndIfEndSubPrivateSub customersCombo_ItemsRequested(ByVal sender AsObject,ByVal e As RadComboBoxItemsRequestedEventArgs)
LoadFilteredCustomersManually(e.Text,TryCast(sender, RadComboBox))EndSubProtectedSub LoadFilteredCustomersManually(ByVal freight AsString,ByVal customersCombo As RadComboBox)'if the orderID value cannot be parsed as integer(i.e. auto LOD is triggered), exit the handlerTryDim fid AsDouble=System.[Double].Parse(freight)CatchReturnEndTryDim connection AsNew SqlConnection(ConfigurationManager.ConnectionStrings("NorthwindConnectionString").ConnectionString)
connection.Open()'select customer name based on the orderIDDim comm AsNew SqlCommand("SELECT CustomerID FROM [Orders] WHERE Freight=@Freight", connection)
comm.Parameters.AddWithValue("@Freight", freight)Dim cid AsString= comm.ExecuteScalar().ToString()
comm =New SqlCommand("SELECT CustomerID, ContactName from [Customers] WHERE CustomerID = '"+ cid +"'", connection)Dim adapter AsNew SqlDataAdapter(comm)Dim dt AsNew DataTable()
adapter.Fill(dt)'populate the customers combo with the new set of items
customersCombo.Items.Clear()For Each row As DataRow In dt.Rows
customersCombo.Items.Add(New RadComboBoxItem(row("ContactName").ToString(), row("CustomerID").ToString()))Next
connection.Close()EndSubProtectedSub RadGrid1_UpdateCommand(ByVal sender AsObject,ByVal e As GridCommandEventArgs)Handles RadGrid1.UpdateCommand
Dim empId AsString=TryCast(TryCast(TryCast(e.Item, GridEditableItem).FindControl("RadComboBox1"), RadComboBox).Items(0).FindControl("RadTreeView1"), RadTreeView).SelectedNode.Value
'if the EmployeeID value is empty, cancel the update operationIf empId =String.Empty Then
e.Canceled =True
SetMessage("Order cannot be updated. You must select an employee from the provided list")Else
OrdersDataSource.UpdateParameters("EmployeeID").DefaultValue = empId
EndIfEndSubProtectedSub RadGrid1_InsertCommand(ByVal sender AsObject,ByVal e As GridCommandEventArgs)Handles RadGrid1.InsertCommand
'if the EmployeeID value is empty, cancel the insert operationIfTryCast(TryCast(TryCast(e.Item, GridEditableItem).FindControl("RadComboBox1"), RadComboBox).Items(0).FindControl("RadTreeView1"), RadTreeView).SelectedNode IsNothingThen
e.Canceled =True
SetMessage("Order cannot be inserted. You must select an employee from the provided list")ElseDim empId AsString=TryCast(TryCast(TryCast(e.Item, GridEditableItem).FindControl("RadComboBox1"), RadComboBox).Items(0).FindControl("RadTreeView1"), RadTreeView).SelectedNode.Value
OrdersDataSource.InsertParameters("EmployeeID").DefaultValue = empId
EndIfEndSubProtectedSub RadGrid1_ItemCommand(ByVal sender AsObject,ByVal e As GridCommandEventArgs)Handles RadGrid1.ItemCommand
'switch the edit/insert modes to ensure that only one operation will be processed at given timeIf e.CommandName = RadGrid.EditCommandName Then
e.Item.OwnerTableView.IsItemInserted =FalseElseIf e.CommandName = RadGrid.InitInsertCommandName Then
RadGrid1.EditIndexes.Clear()EndIfEndSubPublicSub SetMessage(ByVal message AsString)
lblMessage.Text= message
EndSub