محتویات سایت
        برچسب های محبوب 








 
   معرفی کنترل ها و تکنیک های موجود در ASP.NET Ajax - قسمت سیزدهم
  در این مقاله به بررسی کنترل CascadingDropDownExtender که از کنترل های موجود در AjaxControlToolkit می باشد، می پردازم.
   Ajax
   ۳۷۷۲۴
   دریافت فایل ضمیمه
   مرتضی صحراگرد
   ۱۳۸۷/۳/۴
نسخه قابل چاپ نسخه قابل چاپ

مقدمه :

معرفی این کنترل را با یک مثال آغاز می کنم. فرض کنید شما از یک فروشگاه کامپیوتری، یک قطعه سخت افزاری  مانند کیبورد خریداری  کرده اید و اکنون قصد دارید به وب سایت شرکت سازنده کیبور مراجعه و محصول خود را ثبت نموده و درایور آن رو دانلود کنید.

فرض کنید که شرکت سازنده کیبورد، محصولات دیگری را نیز مانند مانیتور، اسپیکر، ماوس و غیره در مدل های مختلف تولید می کند.

در نتیجه در سایت شرکت سازنده، شما باید ابتدا نوع محصول (برای شما کیبورد) و سپس مدل آن و در نهایت رنگ محصول را انتخاب نمایید تا محصول مورد نظر خود را ثبت نموده یا درایور مربوط به آن را دانلود نمایید.

شرکت سازنده محصول نیز قصد دارد که بهترین و راحت ترین روش را برای ثبت کردن محصولات خود به کاربران ارایه دهد.

یکی از بهترین و موثرترین روش های موجود استفاده از کنترل CascadingDropDown (و یا روش های مشابه) می باشد.

اگر به وب سایت  شرکت فراسو مراجعه نمایید، خواهید دید که این شرکت نیز از این تکنیک استفاده نموده است.

در این مقاله من قصد داریم که این تکنیک را با استفاده از CascadingDropDown پیاده سازی کنیم.

همانطور که از نام کامل CascadingDropDownExtender مشخص می باشد، این کنترل یک Extender می باشد .

Extender ها در حقیقت یک کنترل مستقل نمی باشند ولی توانایی این را دارد که قابلیت هایی را به کنترل های موجود در ASP.NET بیفزایند.

CascadingDropDown نیز از این قاعده مستثنا نمی باشد و می تواند قابلیت هایی را به کنترل DropDown اضافه نماید.

در این مقاله قصد داریم مثالی را که ذکر شد، پیاده سازی کنیم.

در صفحه محصولات شرکت 3 عدد کنترل DropDown موجود می باشد که اولین کنترل لیست محصولات شرکت را نگهداری می کند. هنگامی که محصول مورد نظر انتخاب شد، کنترل DropDown پایینی از مدل های محصول انتخاب شده پر می شود.

و در نهایت هنگامی که مدل محصول نیز توسط کاربر انتخاب شد، DropDown پایینی آن، رنگ های مربوط به مدل انتخاب شده را نمایش می دهد. و بدین ترتیب کاربر می تواند در کمترین زمان و با بیشترین سهولت محصول مورد نظر خود را انتخاب نموده و سایر اعمال مورد نظر خود را  انجام دهد.

شروع :

ابتدا بانک اطلاعاتی مربوط به محصولات را ایجاد خواهیم کرد. بانک اطلاعاتی ما شامل سه جدول می باشد.

جدول اول، نام و مشخصات انواع محصولات را نگهداری می کند (Products).

جدول دوم، مدل های مربوط به محصولات را نگهداری می کند (ProductModels).

جدول سوم، رنگ های مدل های محصولات را نگهداری می کند(ModelColors).

قبل از اینکه به تشریح مثال بپردازم ، ابتدا یک نگاه سطح بالا به نحوه انجام کار خواهم داشت .

اگر بخواهیم بدون استفاده از تکنیک های AJAX به پیاده سازی تکنیک فوق بپردازیم، سناریوی انجام کار به شکل زیر می باشد.

  1. ابتدا اولین DropDown هنگام لود صفحه پر می شود که شامل نام محصولات تولید شده توسط شرکت می باشد. (این عمل با استفاده از یک دستور Select  روی جدول Products انجام می شود)

  2.  کاربر از اولین DropDown، محصول مورد نظر خود را انتخاب می کند. پس از اینکه نام محصول مورد نظر کاربر انتخاب شد، یک دستور Select روی جدول حاوی مدل ها (ProductModels) اجرا می شود و DropDown دوم از لیست مدل های مربوط به محصول انتخاب شده، پر می شود.

  3. کاربر از دومین DropDown ، مدل مربوط به محصول خود را انتخاب می کند و سپس یک دستور Select روی جدول حاوی رنگ ها (ModelColors) با توجه به مدل انتخاب شده زده می شود و DropDown سوم از رنگ های موجود پر می شود.

  4.  کاربر از سومین DropDown ، رنگ محصول خریداری شده توسط خود را انتخاب می کند و کار تمام است.

در CascadingDropDown نیز این مراحل اتفاق می افتد و فقط نحوه انجام کار تفاوت دارد که در ادامه مقاله به بررسی آن می پردازم.

اکنون به پیاده سازی تکنیک فوق می پردازم.

سه عدد کنترل DropDown به صفحه اضافه نموده و شکل ظاهری آن ها را مرتب می کنیم.

اولین کنترل DropDown ، مشخصات محصولات را نگهداری می کند. دومین کنترل مشخصات مدل های محصول انتخاب شده  و سومین کنترل، مشخصات رنگ مدل انتخاب شده را نگهداری  می کند.

قطعه کد مربوط  به DropDown  به شکل زیر می باشد.


<asp:DropDownList ID="ddlProducts" runat="server" Width="240px" />
<asp:DropDownList ID="ddlModels" runat="server" Width="240px" />
<asp:DropDownList ID="ddlColors" runat="server" Width="240px" />

خوب، اکنون نوبت به اضافه کرده کنترل های CascadingDropDown می باشد. با توجه به اینکه ما سه عدد DropDown در صفحه داریم که قرار است تکنیک را روی آن ها اجرا کنیم، باید سه عدد CascadingDropDown به صفحه اضافه کنیم.

اولین CascadingDropDown را به صفحه اضافه نموده و به شکل زیر تغییر می دهیم.


<ajaxToolkit:CascadingDropDown ID="cddProducts"
      runat="server"
      TargetControlID="ddlProducts"
      Category="Product"
      PromptText="Choose a Product ...."
      LoadingText="Please wait ..."
      ServicePath="ProductsService.asmx"
      ServiceMethod="GetProducts">
</ajaxToolkit:CascadingDropDown>

این کنترل CascadingDropDown  مربوط به ddlProducts می باشد.

اکنون به معرفی صفات مهم CascadingDropDown می پردازم.

  • TargetControlID : نام کنترل DropDown هدف را که قرار است قابلیت های این CascadingDropDown به آن اعمال شود را نگهداری می کند ( در اینجا ddlProducts) .

  • Category: کاربرد این صفت را در ادامه مقاله خواهیم دید. هنگام فراخوانی متدی که عملیات بازیابی اطلاعات از بانک اطلاعاتی را انجام می دهد، مقدار این صفت به عنوان یکی از پارامتر ها ارسال می شود.

  • PromptText : قبل از اینکه کنترل های DropDown با داده های مربوط به خود پر شوند، متن این صفت در آن ها نمایش داده می شود.

  • LoadingText : با توجه به اینکه بازیابی اطلاعات از بانک اطلاعاتی، عملی زمانبر می باشد، در این مدت زمان، متنی که در این صفت وارد نموده ایم در DropDown مربوطه نمایش داده می شود.

  • ServicePath : با توجه به اینکه ما قصد داریم از سمت کلاینت، متدهای مربوط به پر کردن DropDown ها را فراخوانی کنیم، یک وب سرویس به نام ProductsService ایجاد نموده ایم که در ادامه برنامه بیشتر مورد بررسی قرار خواهد گرفت. این صفت مسیر مربوط به وب سرویس را نگهداری می کند.

  • ServiceMethod : این صفت نام متدی را که قرار است از وب سرویس مذکور فراخوانی شود را نگهداری می کند.

  • اکنون CascadingDropDown های مربوط به DropDown های دوم و سوم را اضافه می کنیم.


    <ajaxToolkit:CascadingDropDown ID="cddModels"
             runat="server"
             TargetControlID="ddlModels"
             ParentControlID="ddlProducts"
             Category="Model"
             PromptText="Choose a Product Model...."
             LoadingText="Please wait ..."
             ServicePath="ProductsService.asmx"
             ServiceMethod="GetModels">
    </ajaxToolkit:CascadingDropDown>
    <ajaxToolkit:CascadingDropDown ID="cddColors"
             runat="server"
             TargetControlID="ddlColors"
             ParentControlID="ddlModels"
             Category="Color"
             PromptText="Choose a Color...."
             LoadingText="Please wait ..."
             ServicePath="ProductsService.asmx"
             ServiceMethod="GetColors">
    </ajaxToolkit:CascadingDropDown>

    همانطور که ملاحظه می کنید، این CascadingDropDown ها دارای یک صفت جدید می باشند که در کنترل CascadingDropDown قبلی که به صفحه اضافه کردیم، وجود نداشت.

    ParentControlID : همانطور که قبلا ذکر شد، DropDown پایینی، با توجه به مقداری که در DropDown بالایی انتخاب شده است پر می شود. در نتیجه DropDown بالایی والد DropDown پایینی محسوب می شود. در حقیقت شما با مقدار دادن به این صفت مشخص می کنید که مقادیر این DropDown باید وابسته به کدام DropDown باشند. به طور مثال مقادیر ddlModels وابسته به مقدار انتخاب شده از ddlProducts توسط کاربر می باشد.

    خوب، حالا نوبت به ایجاد یک وب سرویس و پیاده سازی متدهای مربوطه می باشد.

    یک وب سرویس به نام ProductsService به پروژه اضافه می کنیم.

    نکته :

    برای اینکه به وب سرویس این امکان را بدهیم تا از سمت کلاینت فراخوانی شود، باید یک Attribute به نام [System.Web.Script.Services.ScriptService] را مطابق شکل زیر به ابتدای کلاس اضافه کنیم.

    هنگامی که صفحه برای اولین بار لود می شود، اولین DropDown باید با لیست نام محصولات پر شود.


    public CascadingDropDownNameValue[] GetProducts(string knownCategoryValues, string category)
    {
    string Query = "SELECT ProductId,ProductName FROM Products";
    List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();
    using (SqlConnection con = new SqlConnection(connectionString))
       {
          SqlCommand cmd = new SqlCommand(Query, con);
          con.Open();
          SqlDataReader dr = cmd.ExecuteReader();
          if (dr.HasRows)
             {
                      while (dr.Read())
                         {
                                  string productName = (string)dr["ProductName"];
                                  int productId = (int)dr["ProductID"];
                                  values.Add(
    new CascadingDropDownNameValue(productName, productId.ToString()));
                      }
                }
     return values.ToArray();
          }
    }

    قبلا از اینکه به تشریح عملکرد قطعه کد بالا بپردازم، ذکر چند نکته را ضروری می دانم.

    1. هر کدام از CascadingDropDown ها هنگام فراخوانی متد مربوط به خود، دو پارامتر را به متد ارسال می کنند. پارامتر اول را ما knownCategoryValues نام نهادیم که شامل اطلاعاتی در مورد گزینه انتخاب شده از DropDown مربوطه می باشد. همانطور که در ابتدای مقاله ذکر کردم، در اینجا نیز باید دستور Select مناسب روی بانک اطلاعاتی اجرا شود که مشخصات آن توسط این پارامتر ارسال می شود. پارامتر دوم همان Category می باشد که قبلا هم از آن یاد کردیم، و مشخص می کند که DropDown انتخاب شده، مربوط به کدام Category می باشد.

    2. مقدار برگشتی این متد، شامل آرایه ای از اشیاء CascadingDropDownNameValue می باشد که کنترل DropDown مربوطه به آن بایند می شود.

    3. چون CascadingDropDown مربوط به این DropDown دارای ParentControlID نمی باشد، این کنترل پس از لود صفحه پر می شود و اگر دارای این صفت بود، تنها پس از اینکه مقدار والد آن توسط کاربر انتخاب می شد، این DropDown پر می شد(یعنی متد GetProducts فراخوانی می شد).

    قطعه کد بالا دارای نکته خاص دیگری نمی باشد، به جز اینکه ما یک لیست از نوع CascadingDropDownNameValue اینجا نموده ایم و آن را با اطلاعات جدول Products پر کرده ایم و در نهایت لیست پر شده را به شکل آرایه برگردانده ایم.

    ما از پارامتر های ارسال شده به این متد استفاده نکرده ایم.

    مقدار پارامتر knownCategoryValues  خالی می باشد. زیرا این متد پس از لود صفحه فراخوانی می شود و مقداری از DropDown هنوز انتخاب نشده است و دارای والدی هم نیست که مشخصات گزینه انتخاب شده والد، در آن نگهداری شده باشد.

    پارامتر category نیز حاوی نام طبقه بندی DropDown که در نمای Markup مشخص نمودیم یعنی "Product" می باشد.

    اکنون به قطعه کد زیر توجه فرمایید. این متد هنگام انتخاب یک گزینه از گزینه های ddlProducts فراخوانی می شود.


      [WebMethod]
      public CascadingDropDownNameValue[] GetModels(string knownCategoryValues, string category)
      {
         StringDictionary kv = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
         int productId = int.Parse(kv["Product"]);
         string Query = "SELECT ModelId, ProductId, ModelName FROM ProductModels WHERE ProductId = @ProductId";
         List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();
      using (SqlConnection con = new SqlConnection(connectionString))
         {
            SqlCommand cmd = new SqlCommand(Query, con);
           cmd.Parameters.AddWithValue(
      "@ProductId", productId);
           con.Open();
           SqlDataReader dr = cmd.ExecuteReader();
           if (dr.HasRows)
           {
                while (dr.Read())
                {

                     values.Add(
      new CascadingDropDownNameValue((string)dr["ModelName"], dr["ModelID"].ToString()));
                }
           }
           return values.ToArray();
           }
      }

      در قطعه کد بالا، پارامتر knownCategoryValues دارای مقدار می باشد و مشخصات گزینه انتخاب شده از ddlProducts را نگهداری می کند. اکنون باید با توجه به محصول انتخاب شده، مشخصات مدل های مربوط به آن را به ddlProductModels بایند کنیم و نیاز داریم که شناسه (ProductId) محصول انتخاب شده را جهت استفاده در دستور Select در دسترس داشته باشیم.

      کلاس CascadingDropDown دارای متدی به نام ParseKnownCategoryValuesString می باشد که مشخصات ذخیره شده در پارامتر knownCategoryValues را برای استفاده راحت تر، به شکل StringDictionary بر می گرداند.

      نکته بسیار مهم :

      در طول مقاله بعضی از کاربردهای صفت Category را که در CascadingDropDown مقدار دهی کردیم، ذکر کردم. یکی از کاربردهای دیگر این صفت این است که با استفاده از آن در قطعه کد بالا شناسه محصول انتخاب شده (در حقیقت ddlProducts.SelectedValue) را با استفاده از نام طبقه بندی cddProducts یعنی عبارت "Product" بدست آورده ایم و در عبارت Select از آن استفاده نموده ایم.


      int productId = int.Parse(kv["Product"]);

      اکنون نوبت به متد آخر، یعنی GetColors می رسد.


      [WebMethod]
      public CascadingDropDownNameValue[] GetColors(string knownCategoryValues, string category)
      {
            StringDictionary kv = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
            int modelId = int.Parse(kv["Model"]);
            string Query = "SELECT ColorId, ColorName FROM ModelColors WHERE (ModelId = @ModelId)";
            List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();
            using (SqlConnection con = new SqlConnection(connectionString))
            {
               SqlCommand cmd = new SqlCommand(Query, con);
               cmd.Parameters.AddWithValue(
      "@ModelId", modelId);
               con.Open();
               SqlDataReader dr = cmd.ExecuteReader();
               if (dr.HasRows)
               {
               while (dr.Read())
               {
                  values.Add(
      new CascadingDropDownNameValue((string)dr["ColorName"], dr["ColorID"].ToString()));
               }
            }
      return values.ToArray();
         }
      }

      عملکرد قطعه کد فوق نیز کاملا مشخص می باشد. این متد هنگامی فرخوانی می شود که مقداری از ddlProductModels انتخاب شود و در نتیجه متد یعنی لیست رنگ های مربوط به مدل انتخاب شده به کنترل ddlModelColors بایند می شود.

      نکاتی مربوط به راندمان و امنیت :

    1. همواره در مقالات خود سعی می کنم که آموزشی بودن و کاربردی بودن مقالات را در کنار هم در نظر بگیرم. ولی گاهی اوقات توجه زیاد به کاربردی بودن مقاله، ایجاد پیچیدگی هایی می کند که قسمت آموزشی بودن آن را تحت الشعاع قرار می دهد. لذا مخاطبین محترم هنگام استفاده از مطالب این مقاله و سایر مقالات در پروژه های کاربردی خود لطفا همه موارد مربوط به امنیت و راندمان و مدیریت خطا و غیره را نیز در نظر بگیرند.

    2. برای سایت هایی با ترافیک بالا، بهتر است اطلاعات Cache شوند و بازیابی اطلاعات از Cache صورت گیرد. در غیر اینصورت، این ایده چندان جالب نمی باشد که با تغییر هر گزینه یک Query روی بانک اطلاعاتی اجرا شود.

    3. بهتر است از StoredProcedure جهت بازیابی اطلاعات از بانک اطلاعاتی استفاده شود.

    4. خوب، این مقاله نیز به پایان رسید و شما می توانید این تکنیک ارزشمند را در پروژه های خود پیاده سازی کنید.

      قطعه کد کامل این مقاله از لینک بالای صفحه قابل دریافت می باشد.

      ولی یک سوپرایز هم برای علاقه مندان پر و پا قرص Pure Ajax دارم.

      در قطعه کد پایین بدون استفاده از کنترل CascadingDropDownExtender ، این تکنیک شبیه سازی شده است.

       ولی نیمی از راه رو باید خودتان طی کنید (نوشتن متدهای وب سرویس و غیره)

      نکته :

      مقداری برگشتی از متدهای وب سرویس در این حالت IEnumerable در نظر گرفته شده است. مانند الگوی زیر :


      [WebMethod]
      public IEnumerable<Product> GetProducts() {
      //  Implementation
      }
      [
      WebMethod]
      public IEnumerable<Model> GetModels(int modeId)
      {
      // Implementation
      }

      قطعه کد مربوط به صفحه اصلی نیز به شکل زیر می باشد.


      <%@ Page Language="C#" %>
      <html xmlns="http://www.w3.org/1999/xhtml">
      <
      head id="Head1" runat="server">
      <title>www.30sharp.com</title>
      <script type="text/javascript">
      var ddlProducts;
      var ddlModel;
      function pageLoad()
      {
      ddlProducts = $get(
      "ddlProducts");
      ddlModel = $get(
      "ddlModel");

      $addHandler(ddlProducts,
      "change", changeModel);
      ProductsService.GetProducts(makeSuccess);
      }
      function makeSuccess(data)
      {
      BindDropDown(ddlProducts, data);
      changeModel();
      }
      function changeModel()
      {
      ProductsService.GetModels(ddlProducts.value, modelSuccess);
      }
      function modelSuccess(data)
      {
      BindDropDown(ddlModel, data);
      }
       
      function BindDropDown(ddl, data)
      {
      ddl.options.length = 0;
      var newOption;
      for (var k=0;k < data.length; k ++)
      {
      newOption =
      new Option( data[k].Name, data[k].Id );
      ddl.options.add( newOption );
      }
      }

      </script>
      </
      head>
      <
      body>
      <form id="form1" runat="server">
      <div>
      <asp:ScriptManager ID="ScriptManager1" runat="server">
      <Services>
      <asp:ServiceReference Path="~/ProductsService.asmx" />
      </Services>
      </asp:ScriptManager>

      <label for="ddlProducts">Product:</label>
      <select id="ddlProducts"></select>

      <br /><br />

      <label for="ddlModel">Model:</label>
      <select id="ddlModel"></select>


      </div>
      </form>
      </
      body>
      </
      html>

      موفق باشید.

      قطعه کد کامل این مقاله از لینک بالای صفحه قابل دریافت می باشد.

      منابع : MSDN ، 30sharp.com