licongjie的博客

专心、专注、专业
随笔 - 26, 文章 - 1, 评论 - 207, 引用 - 1
数据加载中……

利用asp.net 2.0的客户端回调功能制作下拉框无限级联动

        记得以前做asp的时候,常会碰到下拉框多级联动,比如说在注册的时候,需要选择省和城市,这就需要二级联动,那个时候一个普遍的做法就是利用javascript脚本来实现,先把数据从数据库中读取出来,放到javascript的数组中,在下拉框的onchange事件触发时,就可以直接从javascript的数组中读取想要的数据。后来在asp.net 1.0中开发时,这种方法也通用,但如果要多级的话,有一个很大的缺点,就是它需要把数据一次性全部读到javascript的数组中,如果数据非常庞大的话,这部分开销是很大的,有时候不把数据放到数据中中,而是直接写在javascript中,这样不易于维护,而且代码也太长,如果要开发可以无限级联的话,那更是比较头痛。自从asp,net 2.0问世后,开始学习新技术,发现asp,net2.0不仅在控件上,功能上都产生了大变化,而且在对javascript客户脚本的调用功能也更加强大了,特别是出现了ajax功能,即客户端回调功能(当然这只是一个小小的ajax功能,如果要实现功能更强大的无刷新可看atlas),利用这个特性可以很方便的开发现无限级联动的下拉框效果。
        1、先在数据库里建立相应的数据表如下:

 2、建立读取数据表的函数,这里就不写出来了,我想每个人都会有不同的方法去实现这一功能。
    3、接下来,我们就可以进行页面设置了,我们先做一个二级联动的效果,在页面上放置二个下拉框控件,上面一个为TestDropDownList,下面一个为Item1,再加入一个objectdatasource控件,用来读取parentId为0的所有记录绑定到TestDropDownList下拉框中,如下图:

  如果要使用客户端回调,必须要实现System.Web.UI.ICallbackEventHandler接口。
1public partial class Test : System.Web.UI.Page , System.Web.UI.ICallbackEventHandler
2{
  该接口下面,有二个方法:
        a)、void System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent(string eventArguent)
        该方法接收客户端传过来的参数enentArguent,这个参数好象只能是string型,并且没有返回值,我们可以根据传过来的参数,来执行相应的功能,在这里我们不能对页面进行重新绑定等操作,但能够跟数据库进行交互操作,如读取、删除、插入等操作,在本文的例子中,这里从客户端传过来的是parentId的值,再从数据库中读取parentId为相应值的所有记录,具体代码如下:
 1void System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent(string eventArguent)
 2    {
 3        DataSet ds = new GetData().TestByParentId(Convert.ToInt32(eventArguent));
 4        foreach(DataRow row in ds.Tables[0].Rows)
 5        {
 6            if (result.Equals(string.Empty))
 7            {
 8                result = row["pkId"+ "@" + row["name"].ToString();
 9            }

10            else 
11            {
12                result = result + "," + row["pkId"+ "@" + row["name"].ToString();
13            }

14        }

15    }

  在这里,通过TestByParentId(Convert.ToInt32(enentArguent))函数读到相应数据,并把得到的数据作了一些处理,这个为什么要这样做,请继续往下看。这里的result为string型变量。
    
        b)、string System.Web.UI.ICallbackEventHandler.GetCallbackResult()
        这是第二个方法,该方法没有参数,返回只能返回string型。这个方法是用来数据回调的,上面那个方法接收客户端传过来的参数,进行处理后,还要把处理后的我们想要的数据传回到客户端去执行,才会有用。这里我们也看到了,由于该方法只能返回string型,而在下拉框必须有二个值,一个是DataValue,另一个是DataText,所以在上面的方法中,通过@符号,把这二个值合在一起,便于在客户端程序中进行分离。这个方法功能很简单,就是把result返回即可,如下:
1string System.Web.UI.ICallbackEventHandler.GetCallbackResult()
2    {
3        return result;
4    }

  现在我们实现了System.Web.UI.ICallbackEventHandler接口下的全部方法,接下来我们就可以来写客户端的程序了,用javascript来实现。这里主要也有二个方法,第一个方法用来接受下拉框控件的onchange事件触发响应,另一个用来接收string System.Web.UI.ICallbackEventHandler.GetCallbackResult()方法传递过来的result值,并做相应处理,把数据绑定到第二个下拉框中去,实现 二级联动。主要代码如下
        a)、第一个方法
       
1 function CallServer(input, context)
2    {
3        context.length=0;
4        context.options[context.length]=new Option("数据读取中","0");
5        
6        arg = input.value;
7        <%=callback %>
8    }

  这里有二个参数,input是用来触发onchange的下拉框控件名称,context是要级联的下拉框名称,这里在数据还没有绑定之前,级联下拉框显示“数据读取中...”字样。arg接收input下拉框的值,这个值以后是传递到上面的eventArguent参数中去,其中的<%=callback%>我们下面再介绍。
        b)、第二个方法
   
 1 function ReceiveServerData(result, context)
 2    {
 3        context.length=0;
 4        var arrData = result.split(",");
 5        for (var i = 0; i < arrData.length; i++)
 6        {
 7            var data = arrData[i].split("@");
 8            context.options[context.length]=new Option(data[1],data[0]);
 9        }

10       
11    }

       这里也有二个参数,其中result,就是接收上面讲到的result值,而context同第一个方法的context。这里通过js的split方法,把数据进行处理,再绑定到context控件中去,通过这个就可以在第二个下拉框中出现相应的值了。
  做到这里基本上已经大功告成了,不过还有最后一步,这也是最重要的一步,我们现在做的只是客户端归客户端,服务器方法归服务器方法,那么怎么样把这二个联系起来呢,这也是实现客户端回调的关键部分,这里需要使用Page.ClientScript.GetCallbackEventReference方法,下面是摘自MSDN2上的对ClientScript.GetCallbackEventReference(……)的详细说明。
  
  public string GetCallbackEventReference (Control control,string argument,string clientCallback,string context)
  
    参数:
  
  参数 作用
  control 处理客户端回调的服务器 Control。该控件必须实现 ICallbackEventHandler 接口并提供 RaiseCallbackEvent 方法。
  argument 从客户端脚本传递一个参数到服务器端的RaiseCallbackEvent 方法。
  clientCallback 一个客户端事件处理程序的名称,该处理程序接收服务器端事件返回的结果。
  context 启动回调之前在客户端的客户端脚本信息。脚本的结果传回给客户端事件处理程序。
  返回值 调用客户端回调的客户端函数的名称。
  
    下面是ClientScriptManager.GetCallbackEventReference 方法的重载列表
  
  名称 说明
  ClientScriptManager.GetCallbackEventReference (Control, String, String, String) 获取一个对客户端函数的引用;调用该函数时,将启动一个对服务器端事件的客户端回调。此重载方法的客户端函数包含指定的控件、参数、客户端脚本和上下文。
  ClientScriptManager.GetCallbackEventReference (Control, String, String, String, Boolean) 获取一个对客户端函数的引用;调用该函数时,将启动一个对服务器端事件的客户端回调。此重载方法的客户端函数包含指定的控件、参数、客户端脚本、上下文和布尔值。
  ClientScriptManager.GetCallbackEventReference (Control, String, String, String, String, Boolean) 获取一个对客户端函数的引用;调用该函数时,将启动一个对服务器端事件的客户端回调。此重载方法的客户端函数包含指定的控件、参数、客户端脚本、上下文、错误处理程序和布尔值。
  ClientScriptManager.GetCallbackEventReference (String, String, String, String, String, Boolean) 获取一个对客户端函数的引用;调用该函数时,将启动一个对服务器端事件的客户端回调。此重载方法的客户端函数包含指定的目标、参数、客户端脚本、上下文、错误处理程序和布尔值。  

        主要实现如下:
    在page_load里加入下面语句
   
1callback = Page.ClientScript.GetCallbackEventReference(this"arg""ReceiveServerData""context");

    其中,arg就是用来从客户端传递参数,最终传到enentArguent中,ReceiveServerData是一个客户端方法,用来接收服务器端回调过来的数据。

    再把callback这个字符串放在第一个客户端方法下面(请看上面的第一个客户端方法)

    这个完成后,我们只要把第一个下拉框的onchange客户端事件加入就可以了
    在page_load里加入
   
1TestDropDownList.Attributes["onchange"= "javascript:CallServer(TestDropDownList , Item1)";



    做完所有这些,程序应该能够正常动行了

  现在二级联动已经成功,那怎么能够实现多级的呢,其实大家只要仔细的看一下,就不难发现,当我触到一个下拉框的onchange事件,通过callServer读取值,再通过<%callback%>来启动 void System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent(string eventArguent)这个方法,把数据处理后,通过string System.Web.UI.ICallbackEventHandler.GetCallbackResult()方法返回,再由ReceiveServerData客户端来把数据绑定到相应原下拉框中,而这里的二个互动的下拉框都是通过参数来传递的, TestDropDownList.Attributes["onchange"] = "javascript:CallServer(TestDropDownList , Item1)"; 这样的话,我们可以改变这里传递的控件参数名,就可以重复利用这四个方法,也就是这样就可以实现,无限级联动。我们再加入一个下拉框,取名Item2,我们只要在page_load中加入如下语句即可:

1Item1.Attributes["onchange"= "javascript:CallServer(Item1 , Item2)";

就就可以三级联动了,如果要四级,五级,只要加入下应的下拉框,并在page_load中加入相应的onchange事件触发,就可以实现了

不过这种方法目前还有一个很大的二个缺点:
    1、那就是当第一个选择后,第二个下拉框会出现相应的值,而第三个以及下面的几个不会变,只有再选择第二个下拉框的值后,第三个才会改变
    2、由于1的问题,导致如果第二个下拉框中只有一个值,那么第三个以及以下几个都不能发生改变了
不知道大家有没有好的解决方法,我相信应该有解决的方法的。
    由于本人水平有限,可能有不当之处,希望大家能够指正,谢谢!

posted on 2006-05-25 11:38 李.net 阅读(3288) 评论(9)  编辑 收藏 所属分类: C# & ASP.NET

评论

#1楼    回复  引用  查看    

使用Atlas,您只需要拖一个控件到页面上就好了:
http://dflying.cnblogs.com/archive/2006/05/08/Atlas_Control_Toolkit_Demo__Using_CascadingDropDown_with_a_Database.html
2006-05-25 21:03 | Dflying Chen      

#2楼    回复  引用  查看    

既然在使用ASP.NET 2.0这个方法太麻烦,并且还存在1和2的问题.
我的做法是用DropDownList组件的AutoPostBack属性进行回调把一级DropDownList选择的ID返回供数据查询并将结果绑定给二级的DropDownList然后将二级DropDownList的第一个默认值的ID作参数供数据查询并将结果绑定给三级的DropDownList多级的以此类推.这样即简单还避免了你例子中存在的1和2的问题.
这里需要注意的只是假设二级或更下级的某些项可能存在空值,那么在数据绑定后取返回默认ID的时候会出现错误,所以在取返回默认ID的时候应用容错的方式来处理如:
Try
ProvinceID.Value = DropDownList.Items(0).Value
Catch ex As Exception
ProvinceID.Value = 0
End Try
2006-07-23 22:58 | ZipLi [未注册用户]

#3楼 [楼主]   回复  引用  查看    

@ZipLi
你的这种方法确实可行,可是你使用autopostback而又不使用atlas
是要刷新整个页面的,如果这样的话,实现起来有好多方法可选择的
我的要求是不刷新页面,当然像Dflying Chen 所说的用atlas用的下拉框,可以更方便的实现,只不过在发布的时候还要把atlas中的dll也一起发布而已
2006-07-24 08:38 | 李.net      

#4楼    回复  引用  查看    

楼主,我的解决办法,把第一行数据设为"请选择"之类,这样就,~~
还有请教一下,如果多级联动,不是一个数据表,列名也不一样,该怎么处理?
2006-08-19 21:10 | Yoka [未注册用户]

#5楼    回复  引用  查看    

sdf
2006-09-07 09:39 | sd [未注册用户]

#6楼    回复  引用  查看    

http://grassland619.blog.163.com/
2007-01-21 16:44 | 陈草原 [未注册用户]

#7楼    回复  引用  查看    

ReceiveServerData 函数后面部分继续回调,判断本次处理的下拉框名字决定哪个回调,是否回调,就会一次性联动。
2007-03-19 10:59 | zhangmy      

#8楼    回复  引用  查看    

dfg
2007-07-20 09:32 | ghf [未注册用户]

#9楼    回复  引用  查看    

好像还有部分代码没有贴出
2007-11-21 16:08 | alan.lin [未注册用户]