|
Rank: Member Groups: Member
Joined: 9/17/2008 Posts: 13
|
I have a TreeView and a CallBackPanel in use on the same page. My goal is to have the CBP display details about the selected TreeView item when it is clicked on, but also allow users to reorder and drag-n-drop items in the TreeView to change sort order and parents. The details views work as expected, however, when I start drag-n-drop operations I start experiencing an issue. If I move Item 1 from Container A to Container B, OnItemMoved fires once. That's great. Problem is that if I move Item 1 from Container B back to Container A, OnItemMoved fires twice (once for the first move and once for the second). Now, if I simply click on an item OnItemMoved fires twice again. If I move another item from one place to another OnItemMoved fires three times, and again if I simply click on an item OnItemMoved fires three times. The number of times the event fires increments up with each successive move--I've tested it up to 8 moves. The problem only occurs if I try to use the TreeView as a trigger for the CallbackPanel AND use it's OnItemClick/OnItemMoved events. But I need to use both. So I need some assistance in keeping the OnItemMoved from firing too often. Thanks!
Code: HTML/ASPX
<div id="divMenuTreeView" style="vertical-align:top;width:205px;height:405px;padding:0px;overflow:visible;border:solid 1px #999999;">
<eo:TreeView ID="trvMenuItems"
runat="server"
Height="400px" Width="200px"
ControlSkinID="None"
RaisesServerEvent="true"
SaveStateCrossPages="true"
StateCookieName="ckiTrvMenuItemsTracking2"
AllowDragDrop="true"
AllowDragReordering="true"
OnItemClick="trvMenuItems_OnItemClick"
OnItemMoved="trvMenuItems_OnItemMoved">
<LookNodes>
<eo:TreeNode CollapsedImageUrl="00030301"
DisabledStyle-CssText="background-color:transparent;border-bottom-style:none;border-left-style:none;border-right-style:none;border-top-style:none;color:Gray;padding-bottom:1px;padding-left:1px;padding-right:1px;padding-top:1px;"
ExpandedImageUrl="00030302" ImageUrl="00030301" ItemID="_Default"
NormalStyle-CssText="PADDING-RIGHT: 1px; PADDING-LEFT: 1px; PADDING-BOTTOM: 1px; COLOR: black; BORDER-TOP-STYLE: none; PADDING-TOP: 1px; BORDER-RIGHT-STYLE: none; BORDER-LEFT-STYLE: none; BACKGROUND-COLOR: transparent; BORDER-BOTTOM-STYLE: none"
SelectedStyle-CssText="background-color:#316ac5;border-bottom-color:#999999;border-bottom-style:solid;border-bottom-width:1px;border-left-color:#999999;border-left-style:solid;border-left-width:1px;border-right-color:#999999;border-right-style:solid;border-right-width:1px;border-top-color:#999999;border-top-style:solid;border-top-width:1px;color:White;padding-bottom:0px;padding-left:0px;padding-right:0px;padding-top:0px;">
</eo:TreeNode>
</LookNodes>
<TopGroup Style-CssText="border-bottom-color:#999999;border-bottom-style:solid;border-bottom-width:1px;border-left-color:#999999;border-left-style:solid;border-left-width:1px;border-right-color:#999999;border-right-style:solid;border-right-width:1px;border-top-color:#999999;border-top-style:solid;border-top-width:1px;color:black;cursor:hand;font-family:Tahoma;font-size:8pt;padding-bottom:2px;padding-left:2px;padding-right:2px;padding-top:2px;">
<Bindings>
<eo:DataBinding Property="Text-Html" DataField="strTextHtml"></eo:DataBinding>
<eo:DataBinding Property="Value" DataField="intMenuID"></eo:DataBinding>
</Bindings>
</TopGroup>
</eo:TreeView>
</div>
</td>
<td style="width:12px;"></td>
<td>
<!--<td style="width:459px;">-->
<eo:CallbackPanel ID="cbpMenuItem"
runat="server"
OnExecute="cbpMenuItem_OnExecute"
Triggers="{ControlID:trvMenuItems;Parameter:}"
Height="150px"
Width="457px">
<div id="divMenuItem">
Filler.
</div>
</eo:CallbackPanel>
Code: C#
public partial class MisEditMenu : System.Web.UI.Page
{
// general variables
// ---- for holding value of the menu item selected in the tree
public int intSelectedValue = 0;
// ---- for holding value of the next available sort order; for when inserting a new menu item
public int intNextSortOrder = 0;
// instantiate the SQL connection
SqlConnection sqlConn = new SqlConnection();
//TreeView trvMainMenu;
DataSet sdsMainMenu = new DataSet();
SqlDataAdapter sdaMainMenu = new SqlDataAdapter();
SqlCommand scmGetMenuData = new SqlCommand();
SqlCommand scmUpdateMenuData = new SqlCommand();
protected void Page_PreLoad(object sender, EventArgs e)
{
//HcDevTool.SendDebugEmail("'Page_PreLoad' fired!");
}
protected void Page_Load(object sender, EventArgs e)
{
// set up the SQL connection
sqlConn.ConnectionString = ConfigurationManager.ConnectionStrings["intranet"].ConnectionString;
sqlConn.Open();
// ---- START BUILDING TREE VIEW
// code to bind the main menu **TREEVIEW** generation to SQL (the actual menu is bound in HCnet.master)
// declare the SQL data adapter
// build the SQL command that the adapter will run to get the data
scmGetMenuData.Connection = sqlConn;
scmGetMenuData.CommandType = CommandType.Text;
scmGetMenuData.CommandText = "SELECT intMenuID, intParentID, intSortOrder, intNewSortOrder, strTextHtml, strNavigateUrl, strTarget FROM tbl_mainmenu ORDER BY intSortOrder";
// ---- Build the SQL update command that the adapter will use to update the data
scmUpdateMenuData.Connection = sqlConn;
scmUpdateMenuData.CommandType = CommandType.Text;
scmUpdateMenuData.Parameters.Add("@intMenuID", SqlDbType.BigInt, 8, "intMenuID");
scmUpdateMenuData.Parameters.Add("@strTextHtml", SqlDbType.VarChar, 64, "strTextHtml");
scmUpdateMenuData.CommandText = "UPDATE [tbl_mainmenu] SET strTextHtml = @strTextHtml WHERE intMenuID = @intMenuID";
// set the data adapter's commands
sdaMainMenu.SelectCommand = scmGetMenuData;
sdaMainMenu.UpdateCommand = scmUpdateMenuData;
// fill the data set with the menu data
sdaMainMenu.Fill(sdsMainMenu, "tbl_mainmenu");
// set the primary key on the dataset's "tbl_mainmenu"
DataColumn[] thisPkColumn = new DataColumn[1];
thisPkColumn[0] = sdsMainMenu.Tables["tbl_mainmenu"].Columns["intMenuID"];
sdsMainMenu.Tables["tbl_mainmenu"].PrimaryKey = thisPkColumn;
// build a relationship between the parent/child keys
DataRelation sdrParentChildMenu = sdsMainMenu.Relations.Add(sdsMainMenu.Tables[0].Columns["intMenuID"], sdsMainMenu.Tables[0].Columns["intParentID"]);
sdrParentChildMenu.Nested = true;
// bind the main menu dataset to the treeview
trvMenuItems.DataSource = sdsMainMenu;
trvMenuItems.DataBind();
// ---- END BUILDING TREE VIEW
// Get the next available sort order number in case we need to insert a menu
try
{
SqlCommand scmGetNextSort = new SqlCommand("SELECT (MAX(intSortOrder) + 1) AS intNextSort FROM tbl_mainmenu", sqlConn);
SqlDataReader sdrGetNextSort = scmGetNextSort.ExecuteReader();
while (sdrGetNextSort.Read())
{
intNextSortOrder = Convert.ToInt32(sdrGetNextSort[0]);
//HcDevTool.SendDebugEmail("Next sort order number: " + intNextSortOrder);
}
sdrGetNextSort.Close();
sdrGetNextSort.Dispose();
sdrGetNextSort = null;
scmGetNextSort.Dispose();
scmGetNextSort = null;
}
catch (Exception ex)
{
HcDevTool.SendDebugEmail("Couldn't retrieve next sort order number.", IsPostBack, ex);
}
}
protected void Page_LoadComplete(object sender, EventArgs e)
{
}
protected void trvMenuItems_OnItemClick(object sender, EO.Web.NavigationItemEventArgs e)
{
// set the session variable to the selected node so we can carry it between refreshes
//Session["intTrvMenuItemsSelectedValue"] = e.TreeNode.Value;
// take the current node value from the tree
// intSelectedValue = int.Parse(e.TreeNode.Value);
}
protected void trvMenuItems_OnItemMoved(object sender, EO.Web.TreeNodeMovedEventArgs e)
{
string strDebugMsg = "";
strDebugMsg = strDebugMsg + "'trvMenuItems_OnItemMoved' fired!" + Environment.NewLine + Environment.NewLine;
//HcDevTool.SendDebugEmail(strDebugMsg);
int intOldParentNodeId = GetNodeID(e.Node);
int intNewParentNodeId = GetNodeID(e.Node.ParentNode);
if (intNewParentNodeId != intOldParentNodeId)
{
// need to update the DB to reflect a change in parent node
UpdateParentID(intOldParentNodeId, intNewParentNodeId);
}
// prevent multiple OnItemMoved events
//---this.Response.Redirect(this.Request.Url.AbsoluteUri, false); -- this doesn't work
//---trvMenuItems.DataBind(); -- this doesn't work
}
// Retrieve node ID (folderID) from a TreeNode object --- copied directly from EO documentation example
private int GetNodeID(EO.Web.TreeNode node)
{
try
{
return int.Parse(node.Value.Split(',')[0]);
}
catch(Exception ex)
{
return -1;
}
}
private void UpdateParentID(int oldParentNodeId, int newParentNodeId)
{
HcDevTool.SendDebugEmail("'UpdateParentID' fired!" + Environment.NewLine + "Old Parent Node ID: " + oldParentNodeId.ToString() + Environment.NewLine + "New Parent Node ID: " + newParentNodeId.ToString());
}
protected void cbpMenuItem_OnExecute(object sender, EO.Web.CallbackEventArgs e)
{
//HcDevTool.SendDebugEmail("'cbpMenuItem_OnExecute' fired." + System.Environment.NewLine + "Data: " + e.Data);
}
}
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,194
|
Hi,
This is a very interesting combination. The short answer is the TreeView is actually working as expected. : )
1. When you move item from A to B, then back to A, ItemMoved fired twice. That's normal. TreeView fires ItemMoved every time you move an item. It does not try to "combine" them into a single move event. So if you moved one item twice, then ItemMoved will be fired twice. Note that the actual event firing is delayed until the page post back (in your case when ItemClick event is fired). It is NOT fired immediately after you move it;
2. Because all ItemMoved events are delay fired, they are maintained in a list on the client side and then posted back to the server together. For a normal full page post back, this list is cleared after the page reload. Thus they will not accumulate forever. However in your case, they will because the TreeView never had a chance to reload;
To avoid problem 2, you must allow the TreeView to reload after you raises a server event. This usually means to place the TreeView inside the CallbackPanel. That way the TreeView will be reloaded after the callback thus clearing the ItemMoved list.
Hope this helps. Please feel free to let us know if you have any more questions.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 9/17/2008 Posts: 13
|
Thanks for the excellent help. I love your products and getting that inside info on how they work is great. Moving the TreeView inside the CallBackPanel did indeed fix the issue, but it introduced another one: now when the CBP reloads, the tree reloads (which is good) and all the branches are collapsed. Is there a way to configure it so the branches remain expanded after the reload?
|
|
Rank: Administration Groups: Administration
Joined: 5/27/2007 Posts: 24,194
|
Hi,
I believe it should keep it's state automatically unless you did something (such as rebind the TreeView). You may want to try it in a separate page and see if it works as expected. If it still does not work, please post a test page and we will be happy to take a look.
Thanks!
|
|
Rank: Member Groups: Member
Joined: 9/17/2008 Posts: 13
|
Thanks! Going from
to
Code: C#
if(!IsPostback)
{
Treeview.DataBind()
}
made all the difference!
|
|