استفاده از کنترل Asynchronous GridView چندگانه (تو درتو) در Asp.Net

پنجشنبه 29 بهمن 1394

در این مقاله موارد استفاده از GridView در صفحات وب Asp.Net ، و نمونه هایی از دو کنترل Grid View آسنکرون(غیر همزمان) تک و تودرتو ارائه میشود که براساس نیاز می توان از دو حالت موجود این کنترل استفاده کرد.

استفاده از  کنترل  Asynchronous GridView  چندگانه (تو درتو) در Asp.Net

کنترل Grid View یکی از مولفه های  پیچیده و منبع سنگین است که میتوانیم  در صفحات وب Asp.Net از آن ها استفاده کنیم. کنترل Grid View به آسانی  داده های پیچیده با ستون هاو ردیفهای متعدد را به صورت سفارشی طراحی و با ظاهری مناسب(بصورت جدول) میسازد. برای هر کسی که نیاز به استفاده از کنترل Grid View دارد. بسیار مهم است که درک خوبی از  سرور و کلاینت داشته باشد.

 استفاده از کد

در قسمت اول با  یک grid تک که نیاز به بروز رسانی آسنکرون دارد شروع میکنیم.
مورد دوم  grid های چندگانه ویا تودرتو است  که نیاز به بروز رسانی یک به یک، با رویداد chaining  در زمان  اجرای جاوا اسکریپت دارد. این به این معنی است که ما تنها می توانیم به روز رسانی آسنکرون از grid دوم را پس از پایان به روز رسانی اولین grid  به انجام برسانیم.

کنترل (گرید ویو واحد)Single Grid View

 کنترل Grid View در داخل update panel موجود است. برای ساختgrid  بمنظور  بروز رسانی async  نیازبه   update panel داریم . Grid  تولید شده توسط کد  ، تست داده  را انجام میدهد. اگر می خواهید تست قوی داشته باشید می توانید منبع داده  grid را به جدول پایگاه داده واقعی اتصال دهید.

برای افزودن رکورد های بیشتر به grid  ،از یک ورودی میتوانید استفاده کنید. به ازای ورود یک رکورد باید grid را به روز رسانی کنیم..

از __doPostBack برای بروزرسانی grid در کدهای جاوا اسکریپت استفاده کرده ایم ، در این مثال برای درک بهتر از   doPostBack برای  grid های تورد تو استفاده میشود .

پروژه ی  Asp.Netجدیدی ایجاد کرده و سپس  فرم وب empty  را انتخاب میکنیم. درون پروژه  یک صفحه بنام  SingleGrid.aspx   اضافه کرده ، داخل این صفحه  یک update panel  بنام upMain و  در داخل  update panel یک کنترل Grid View و بنام  gridMain اضافه میکنیم.

 برای گرفتن نام ورودی و افزودن  به  داده ها یک text box اضافه میکنیم. همچنین درhtml ،یک رویداد کلیک برای اتصال  تابع جاوا اسکریپت ، برای فراخوانی تابع __doPostBack  اضافه میکنیم.

ما نیاز به اتصال ،  رویداد افزودن یک رکورد جدید به داده ها و سپس  به روز رسانی کنترل grid  داریم . اتصال  رویداد  DOMContentLoaded  با تابع onload و رویداد کلیک   با استفاده از تابع AddRecord  برقرار میسازد. تابع  AddRecord  هنگام فراخوانی  تابع  __doPostBack  بصورت آسنکرون   upMain  را در update panel ، بروزرسانی میکند.

SingleGrid.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SingleGrid.aspx.cs" Inherits="WebApplication.SingleGrid" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body dir="rtl">
    <form runat="server" >
        <div style="padding: 100px" style="border-style:groove">
        <asp:ScriptManager ID="scriptManager1" runat="server" AsyncPostBackTimeout="90"></asp:ScriptManager>

        <h3>تمام رکوردها</h3>
        <asp:UpdatePanel ID="upMain" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                <asp:GridView ID="gridMain" runat="server" OnDataBinding="gridMain_DataBinding" CellPadding="4" ForeColor="#333333" GridLines="None">
                    <AlternatingRowStyle BackColor="White" />
                    <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
                    <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
                    <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
                    <SortedAscendingCellStyle BackColor="#FDF5AC" />
                    <SortedAscendingHeaderStyle BackColor="#4D0000" />
                    <SortedDescendingCellStyle BackColor="#FCF6C0" />
                    <SortedDescendingHeaderStyle BackColor="#820000" />
                </asp:GridView>
            </ContentTemplate>
        </asp:UpdatePanel>
            <br/>
            <br/>
        <h3>اضافه کردن رکورد جدید</h3>
        <input id="txtName" type="text" placeholder="نام را وارد کنید" />
        <br />
        <a href="#" id="lnkAddRecord" >اضافه کردن</a>
        </div>
    </form>

    <script>
        var upMainID = '<%= upMain.ClientID%>';
        document.addEventListener("DOMContentLoaded", onLoad);
        var lnkAddRecord = null;

        function onLoad()
        {
            lnkAddRecord = document.querySelector('#lnkAddRecord');
            lnkAddRecord.addEventListener('click', AddRecord);
        }

        function AddRecord()
        {
            var txtName = document.querySelector('#txtName');
            __doPostBack(upMainID, txtName.value);

            return false;
        }
        
        
    </script>
</body>
</html>

در بالا  برای ارسال دو آرگومان، به  eventArgument  نیاز داریم.

 

اضافه کردن متدها در فایل .cs برای رویداد اتصال داده ها است. برای تست داده ، کد و متدی به نام SetData  ایجاد میکنیم. از متد AddRecord برای افزودن یک رکورد جدید به جدول استفاده می شود. این جدول در application session state ذخیره می شود به طوری که ما می توانیم این اطلاعات را در postback های جزئی پس از آن بازیابی کنیم.

SingleGrid.aspx.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication
{
    public partial class SingleGrid : System.Web.UI.Page
    {
        private DataTable _table;

        /// <summary>
        /// Page load event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                SetData();
            }
            else
            {
                String parameter = Request["__EVENTARGUMENT"];
                AddRecord(parameter);
            }
            gridMain.DataBind();
        }

        /// <summary>
        /// Grid data binding event
        /// </summary>
        /// <param name="e"></param>
        protected void gridMain_DataBinding(object sender, EventArgs e)
        {
            gridMain.DataSource = (Session["MainTable"] as DataTable).DefaultView;
        }

        /// <summary>
        /// Sets the sample data table
        /// </summary>
        /// <returns></returns>
        private void SetData()
        {
            _table = new DataTable();
            _table.Columns.Add("ID", typeof(Int32));
            _table.Columns.Add("نام", typeof(String));

            _table.Rows.Add(1, "کلثوم ابراهیمی");
            _table.Rows.Add(2, "مینا بحری");
            Session["MainTable"] = _table;
        }

        /// <summary>
        /// Adds a new student record
        /// </summary>
        /// <param name="name"></param>
        private void AddRecord(String name)
        {
            DataTable table = Session["MainTable"] as DataTable;
            table.Rows.Add(table.Rows.Count + 1, name);
        }
      
    }
}

هر زمان که رویداد page load انجام میشود؛  بررسی میکند که آیا postback صورت گرفته  یا نه. بسته به نتیجه ،باید  با وارد شدن داده برای اولین بار و یا افزودن یک رکورد جدید به مجموعه صورت بگیرد. رکورد Name value به عنوان آرگومان رویداد هنگامی که در حال اجرای __doPostBack از کلاینت جاوا اسکریپت باشد ،ارسال میشود. مقدار آرگومان  را در سمت سرور نگه میدارد  و پس از آن برای اضافه شدن به رکورد جدید  مورد استفاده قرار میدهد.

در این مثال با نوشتن نام  و کلیک بروی لینک "افزودن "، خواهید دید که یک نام جدید به grid  اضافه میشود.

کنترل Grid View تودرتو

در این مثال ،ما یک جفت کنترل grid  داریم که براساس هر ورودی رویداد را بروزرسانی میکند.  لیست کشویی شامل نام کشورها و دو کنترل grid ،شامل استان و شهر وجود دارند .کد این مثال پیچیده تر از مثال قبلی است ، در اینجا برای  رویدادهای صفحه از Asp.Net ajax استفاده میکنیم.دو راه برای بروزرسانی grid  ها وجود دارد

1. به روزرسانی grid  زمانی که ورودی تغییر میکند.

2. به روز رسانی grid  دوم براساس تعامل کاربر با گرید اول

در این مثال با استفاده از تست داده های جدول در کد ، داده های استاتیک را ایجاد میکند.

یک صفحه وب فرم جدید بنام MultipleGrids.aspx ایجاد کرده .و یک drop down  بنام cboCountryList که شامل لیست کشورها میباشد ،اضافه میکنیم. زمانی که مقدار در ورودی تغییر کرد ما نیاز به بروزرسانی grid  داریم.لازم نیست drop down را به عنوان کنترل سرور داشته باشیم اما اگر شما میخواهید با کد سرور لیست را پر کنید،نیاز به  ویژگی runat='server 'دارید.

دو کنترل Grid View از قسمت Update Panels برای بروزرسانی آسنکرون (غیر همزمان)،به صفحه اضافه میکنیم.در حال حاضر مشکلی که با ان مواجه هستیم این است که چگونه باید بروزرسانی آسنکرون را هندل کنیم.

برای به روز رسانی گریدهای تودرتو  نیاز به اضافه کردن رویداد handler در لود صفحه Asp.Net Ajax  داریم.

Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);

در بالا، تابع  pageLoaded  همیشه برای  بارگذاری  صفحه فراخوانی میشود. اینجا دقیقا همان جایی است که نیاز داریم کدهایی که refresh کردن  محتویاتشان بعد از Update Panel تموم شده ، فراخوانی و اجرا شوند.

MultipleGrids.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MultipleGrids.aspx.cs" Inherits="WebApplication.MultipleGrids_aspx" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="https://code.jquery.com/jquery-1.12.0.min.js" type="text/javascript"></script>
</head>
<body dir="rtl">
    <form id="Form1" runat="server" >
        <div style="padding: 50px" style="border-style:groove">
        <asp:ScriptManager ID="scriptManager1" runat="server" AsyncPostBackTimeout="90"></asp:ScriptManager>

        <select ID="cboCountryList">
            <option value="0" selected>ایالات متحده</option>
            <option value="1">ایران</option>
        </select>

        <h3> استان </h3>
        <asp:UpdatePanel ID="upMain1" runat="server" UpdateMode="Conditional" AutoGenerateColumns="false">
            <ContentTemplate>
                <asp:GridView ID="gridMain1" runat="server" OnDataBinding="gridMain1_DataBinding" OnRowDataBound="gridMain1_RowBound" CellPadding="4" ForeColor="#333333" GridLines="None">
                    <AlternatingRowStyle BackColor="White" />
                    <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
                    <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
                    <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
                    <SortedAscendingCellStyle BackColor="#FDF5AC" />
                    <SortedAscendingHeaderStyle BackColor="#4D0000" />
                    <SortedDescendingCellStyle BackColor="#FCF6C0" />
                    <SortedDescendingHeaderStyle BackColor="#820000" />
                </asp:GridView>
            </ContentTemplate>
        </asp:UpdatePanel>
        <br />
        <h3>شهرها</h3>
        <asp:UpdatePanel ID="upMain2" runat="server" UpdateMode="Conditional" AutoGenerateColumns="false">
            <ContentTemplate>
                <asp:GridView ID="gridMain2" runat="server" OnDataBinding="gridMain2_DataBinding" CellPadding="4" ForeColor="#333333" GridLines="None">
                    <AlternatingRowStyle BackColor="White" />
                    <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
                    <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
                    <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
                    <SelectedRowStyle BackColor="#FFCC66" Font-Bold="True" ForeColor="Navy" />
                    <SortedAscendingCellStyle BackColor="#FDF5AC" />
                    <SortedAscendingHeaderStyle BackColor="#4D0000" />
                    <SortedDescendingCellStyle BackColor="#FCF6C0" />
                    <SortedDescendingHeaderStyle BackColor="#820000" />
                </asp:GridView>
            </ContentTemplate>
        </asp:UpdatePanel>
</div>
    </form>

    <script>
        Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);

        var upMain1ID = '<%= upMain1.ClientID%>';
        var upMain2ID = '<%= upMain2.ClientID%>';
        var gridMain1ID = '<%= gridMain1.ClientID%>';
        var cboCountryList = null;
        document.addEventListener("DOMContentLoaded", onLoad);

        function onLoad()
        {
            cboCountryList = document.querySelector('#cboCountryList');
            cboCountryList.addEventListener('change', UpdateCountry);
            loadGridMain1();
        }
        
        function loadGridMain1()
        {
            $('#' + gridMain1ID).find('[gridmain1row="true"]').each(function (idx, el)
            {
                el.onclick = function ()
                {
                    var stateID = $(el).attr('StateID');
                    return function ()
                    {
                        grid1RowClick(stateID);
                    }
                }();
            });
        }

        function grid1RowClick(stateID)
        {
            __doPostBack(upMain2ID, stateID);
        }

        function pageLoaded(sender, args)
        {
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            if (prm.get_isInAsyncPostBack())
            {
                // get our array of update panels that were updated during the request
                var updatedPanels = args.get_panelsUpdated();
                for (var x = 0; x < updatedPanels.length; x++)
                {
                    var panel = updatedPanels[x].id;
                    switch (panel)
                    {
                        case upMain1ID:
                            loadGridMain1();
                            __doPostBack(upMain2ID, '');
                            break;
                    }
                }
            }
        }

        function UpdateCountry()
        {
            __doPostBack(upMain1ID, cboCountryList.value);

            return false;
        }
    </script>
</body>
</html>

وقتی مقدار در لیست کشویی تغییر میکند ، تمام grid  ها یک به یک refresh میشود.همچنین وقتی در grid  اول آیتمی را انتخاب میکنیم باید grid دوم refresh شود.با رویداد کلیک اتصال تغییر میکند ،بطوری که با فراخوانی  تابع UpdateCountry با انتخاب کشور در لیست کشویی  مقدار تغییر میکند و و زمانی که در ردیف گرید اول کلیک شود  تابع grid1RowClick  فراخوانی میشود. اتصال رویداد  المنتهای  HTML در داخل grid  نیاز دارد درسمت سرور و  کلاینت بکار گرفته شود. در سمت سرور ما باید ویژگی های سفارشی در رویداد Row Bound گرید اضافه کنیم .بعد وقتی صفحه در مرورگر بارگیری شد ما می توانیم المنتهای grid را بر اساس دسته ای از صفات  شناسایی و در نتیجه المنت DOM  را با توابع جاوا اسکریپت اتصال دهیم.

در grid  اول استان کشور مورد نظر انتخاب میشود و در grid دوم شهرهای استانها انتخاب میشوند.در تابع pageLoaded از PageRequestManager.get_isInAsyncPostBack() برای بررسی اینکه آیا لود صفحه جاری  جزئی است یا نه استفاده میشود. بر این اساس ما نیاز به identify کردن update panel  برای ادامه chaining  بلوک کد پس از refresh شدن داریم بنابراین، هنگامی که  بارگذاری  اولین update panel  به پایان رسید، پس از آن  __doPostBack را  برای update panel  گرید دوم فراخوانی میکنیم.

MultipleGrids.aspx.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebApplication
{
    public enum Countries
    {
        UnitedStates = 0,
        India = 1
    }
    public partial class MultipleGrids_aspx : System.Web.UI.Page
    {
        private DataTable _stateTable;
        private DataTable _cityTable;

        /// <summary>
        /// Page load event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                Session["SelectedCountry"] = 0;
                Session["SelectedState"] = 5;
                SetData();                
            }
            else
            {
                String target = Request["__EVENTTARGET"];
                String parameter = Request["__EVENTARGUMENT"];
                if (target == "upMain1")
                {
                    Session["SelectedCountry"] = Convert.ToInt32(parameter);
                    switch ((Countries)Session["SelectedCountry"])
                    {
                        case Countries.UnitedStates:
                            Session["SelectedState"] = 5;
                            break;

                        case Countries.India:
                            Session["SelectedState"] = 1;
                            break;
                    }                    
                }
                else if (target == "upMain2" && parameter.Length > 0)
                {
                    Session["SelectedState"] = Convert.ToInt32(parameter);
                }
            }
            gridMain1.DataBind();
            gridMain2.DataBind();
        }

        /// <summary>
        /// Grid data binding event
        /// </summary>
        /// <param name="e"></param>
        protected void gridMain1_DataBinding(object sender, EventArgs e)
        {
            (Session["StateTable"] as DataTable).DefaultView.RowFilter = String.Format("CountryID = '{0}'", (Int32)Session["SelectedCountry"]);
            gridMain1.DataSource = (Session["StateTable"] as DataTable).DefaultView;
        }

        /// <summary>
        /// Grid data binding event
        /// </summary>
        /// <param name="e"></param>
        protected void gridMain2_DataBinding(object sender, EventArgs e)
        {
            (Session["CityTable"] as DataTable).DefaultView.RowFilter = String.Format("StateID = '{0}'", (Int32)Session["SelectedState"]);
            gridMain2.DataSource = (Session["CityTable"] as DataTable).DefaultView;
        }

        /// <summary>
        /// Sets the initial data.
        /// </summary>
        private void SetData()
        {
            _stateTable = new DataTable();
            _cityTable = new DataTable();

            

            //State table
            _stateTable.Columns.Add("CountryID", typeof(Int32));
            _stateTable.Columns.Add("StateID", typeof(Int32));
            _stateTable.Columns.Add("استان", typeof(String));
            _stateTable.Columns.Add("مساحت", typeof(String));

            //City table
            _cityTable.Columns.Add("StateID", typeof(Int32));
            _cityTable.Columns.Add("شهر", typeof(String));
            _cityTable.Columns.Add("جمعیت", typeof(String));

            #region Iran
            _stateTable.Rows.Add(1, 1, "تهران", "342,239");
            _stateTable.Rows.Add(1, 2, "فارس", "400,362");
            _stateTable.Rows.Add(1, 3, "اصفهان", "240,752");
            _stateTable.Rows.Add(1, 4, "آذربایجان", "550,713");

            _cityTable.Rows.Add(1, "تهران", "3,046,163");
            _cityTable.Rows.Add(1, "ری", "1,138,300");
            _cityTable.Rows.Add(1, "شمیرانات", "341,422");
            _cityTable.Rows.Add(1, "ورامین", "307,713");

            _cityTable.Rows.Add(2, "شیراز ", "1,618,879");
            _cityTable.Rows.Add(2, "آباده", "1,183,549");
            _cityTable.Rows.Add(2, "جهرم", "874,412");
            _cityTable.Rows.Add(2, "فیروزآباد", "446,246");

            _cityTable.Rows.Add(3, "اصفهان", "14,057,991");
            _cityTable.Rows.Add(3, "کاشان", "1,243,414");
            _cityTable.Rows.Add(3, "محلات", "705,579");
            _cityTable.Rows.Add(3, "اصفهان", "580,990");

            _cityTable.Rows.Add(4, "تبریز", "18,394,912");
            _cityTable.Rows.Add(4, "ارومیه", "5,057,709");
            _cityTable.Rows.Add(4, "سراب", "2,497,870");
            _cityTable.Rows.Add(4, "میانه", "1,561,809");
            #endregion Iran

            #region United States
            _stateTable.Rows.Add(0, 5, "Colorado", "268,602");
            _stateTable.Rows.Add(0, 6, "Nevada", "284,422");
            _stateTable.Rows.Add(0, 7, "Texas", "677,989");
            _stateTable.Rows.Add(0, 8, "Montana", "376,944");

            _cityTable.Rows.Add(5, "Denver", "663,862");
            _cityTable.Rows.Add(5, "Colorado Springs", "445,830");
            _cityTable.Rows.Add(5, "Aurora", "353,108");
            _cityTable.Rows.Add(5, "Fort Collins", "156,480");

            _cityTable.Rows.Add(6, "Las Vegas", "613,599");
            _cityTable.Rows.Add(6, "Henderson", "277,440");
            _cityTable.Rows.Add(6, "Reno", "236,995");
            _cityTable.Rows.Add(6, "North Las Vegas", "230,788");

            _cityTable.Rows.Add(7, "Houston", "2,239,558");
            _cityTable.Rows.Add(7, "San Antonio", "1,436,697");
            _cityTable.Rows.Add(7, "Dallas", "1,281,047");
            _cityTable.Rows.Add(7, "Austin", "912,791");

            _cityTable.Rows.Add(8, "Billings", "108,869");
            _cityTable.Rows.Add(8, "Bozeman", "41,660");
            _cityTable.Rows.Add(8, "Butte-Silver Bow", "33,980");
            _cityTable.Rows.Add(8, "Great Falls", "59,152");
            #endregion United States



            Session["StateTable"] = _stateTable;
            Session["CityTable"] = _cityTable;
        }

        /// <summary>
        /// Row bound event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void gridMain1_RowBound(object sender, GridViewRowEventArgs e)
        {
            if (e.Row.RowType == DataControlRowType.DataRow)
            {
                DataRowView row = e.Row.DataItem as DataRowView;

                e.Row.Attributes.Add("gridmain1row", "true");
                e.Row.Attributes.Add("StateID", row["StateID"].ToString());
            }
        }
        }
    }

 

کد بالا عمدتا کار همان مثال اول را انجام میدهد.  به روز رسانی استان در  gridهای ذخیره شده در application session state بر اساس رویداد آرگومان ها بروزرسانی میکنیم و  پس از آن تست داده rebinds باgrid  را انجام میدهیم .

 

 رویداد gridMain1_RowBound برای افزودن ویژگیهای  سفارشی gridmain1Row به هر ردیف grid استفاده میشود. آرگومان  StateID نیز به عنوان یک ویژگی اضافه شده به طوری که در هر سطر می توانید اطلاعات استان خود را  به تابع رویداد جاوا اسکریپت ارسال کنید. در کد های سمت کلاینت ،  المنت سطرهایی که ویژگی gridmain1Row را دارند، از جی کوئری استفاده میشود  و المنتهای خارج از حلقه را  به بستار جاوا اسکریپت (JavaScript closure)اتصال میدهد.
برای تست ، کد کشور انتخاب شده را تغییر بدهید و خواهید دید که هر دو grid یکی پس از دیگری refresh  میشود. در  استان grid، روی  استان مورد نظر کلیک کنید و میبینید که اطلاعات  grid شهر refresh میشود.

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید