نویسار

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

نویسار

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

نویسار

اینجا صرفا محلی برای یاداشتها و بیان تجربه های شخصی صاحب وبلاگ می باشد.

پیوندهای روزانه

ساخت GridView در ASP.NET MVC 4

جمعه, ۳۱ خرداد ۱۳۹۲، ۰۲:۱۶ ب.ظ
اگر از برنامه نویسان mvc هستید حتما مشاهده کرده اید که در mvc مثل webform کنترل سمت سرور برای نمایش لیستی از داده ها در قالب gridview یا datagrid وجود ندارد.
شما برای اینکار باید هر بار با استفاده از حلقه ها، خودتون یک جدول از داده رو بسازید که به مرور زمان سرعت توسعه برنامه رو پایین میاره.
در این پست می خواهیم نحوه ساخت یک GridView سفارشی و با قابلیت استفاده مجدد رو بسازیم تا بتونیم در همه جای برنامه مون بتونیم به راحتی از اون استفاده کنیم.

چیزی که من برای ساخت این گرید در نظر داشتم مثلا یک متد بود که با گرفتن یک لیست از یک object لیستی از property های اون رو به همراه مقدارشون به شکل یک grid نمایش بدهد.

در اینجا ما برای اینکار از HTML Helper استفاده خواهیم کرد که در ادامه با آن آشنا خواهید شد.
 
یک HTML Helper تنها یک متد است که رشته‌ای را بر می‌گرداند و این رشته می‌تواند حاوی هر نوع محتوای دلخواهی باشد. برای مثال می‌توان از HTML Helpers برای رندر تگ‌های HTML، مانند img و input استفاده کرد. یا به کمک HTML Helpers می‌توان ساختارهای پیچیده‌تری مانند نمایش لیستی از اطلاعات دریافت شده از بانک اطلاعاتی را پیاده سازی کرد. به این ترتیب حجم کدهای تکراری تولید رابط کاربری در Viewهای برنامه‌های ASP.NET MVC به شدت کاهش خواهد یافت، به همراه قابلیت استفاده مجدد از متدهای الحاقی HTML Helpers در برنامه‌های دیگر.
HTML Helpers در ASP.NET MVC معادل کنترل‌های ASP.NET Web forms هستند اما نسبت به آن‌ها بسیار سبک‌تر می‌باشند؛ برای مثال به همراه ViewState و همچنین Event model نیستند.
ASP.NET MVC به همراه تعدادی متد HTML Helper توکار است و برای دسترسی به آن‌ها شیء Html که وهله‌ای از کلاس توکار HtmlHelper می‌باشد، در تمام Viewها قابل استفاده است. ^
متد های کمکی دو نوع هستند که اولی به کمک متدهای الحاقی (extension methods) نوشته می شود و دومی Declarative HTML Helpers نام دارد و مستقیما درون فایل‌های cshtml یا vbhtml به کمک امکانات Razor قابل تعریف هستند. تولید این نوع متدهای کمکی به این شکل نسبت به مثلا روش TagBuilder ساده‌تر است،‌ چون توسط Razor به سادگی و به نحو طبیعی‌تری می‌توان تگ‌های HTML و کدهای مورد نظر را با هم ترکیب کرد (این رفتار طبیعی و روان، یکی از اهداف Razor است). ^ و ما در اینجا قرار است از همین روش استفاده کنیم.

من فرض رو بر این می زارم که شما با MVC و EF اشنایی مقدماتی دارید و آموزش رو ادامه می دهم :)
_ برای شروع ابتدا باید یک پوشه با نام App_Code در مسیر اصلی پروژه ایجاد کنید .(دقیقا به همین نام. این پوشه، جزو پوشه‌های ویژه ASP.NET است).
_ بر روی این پوشه کلیک راست کرده و گزینه Add => New Item را انتخاب کنید.
_ در صفحه باز شده، MVC 3 Partial Page/Razor را یافته و نام GridView.cshtml را وارد کرده و این فایل را اضافه کنید.
تمام محتوای آن فایل را پاک کرده و سپس using های زیر را به آن اضافه نمایئد.
@using System.ComponentModel.DataAnnotations
@using System.Reflection

سپس کدهای زیر را به آن اضافه نمائید.
 
@helper Bind(dynamic list, string[] columns, GridViewOptions options) 
{

}

در ابتدا گفتم که ما می خواهیم یک لیست از یک نوع کلاس نامشخص رو از ورودی دریافت کنیم که این نوع می تواند یک کلاس People و یا Student باشد و در هر دو حالت نباید نیاز به تغییر در کد ها باشد پس برای همین از نوع dynamic که از NET 4. به بعد ارائه شده است برای دریافت لیست استفاده می کنیم .

در پارامتر دوم یک آرایه از ستون های گرید رو به صورت رشته ای میگیرم که در ادامه متوجه خواهید شد گه چگونه از آنها استفاده خواهیم کرد .

و پارامتر سوم هم یک کلاس هست که خودمون ساختیم و در زیر می تونید کدهاش رو ببینید:

    public class GridViewOptions
    {
        public bool Delete;
        public string DeleteRoute;
    }

توی این کلاس ما دو property داریم

_ اولی تعیین می کند که  ستون مربوط به حذف، هنگام ساخت گرید به آن اضافه شود یا نه .

_ دومی ادرس اکشن Delete می باشدکه پس از زدن دکمه باید به آن ارسال شود.

که البته شما می تونید بعدا امکانات بیشتری رو مثل ویرایش برای اون قرار بدید.

خب حالا برای نمایش گرید ما باید ابتدا ستون های گرید رو نمایش بدیم که این ستون ها نامشخص هستند و ما باید با استفاده از قابلیت Reflector در NET. فیلد های که با DataAnotation و [Display(Name = "نام")] علامت گذاری شدند و همچنین نام فیلد در آرایه ورودی columns ذکر شده است را نمایش دهیم.

پس ابتدا با کد زیر لیست property های کلاس رو استخراج می کنیم:

    PropertyInfo[] t = list[0].GetType().GetProperties();

و در ادامه کدهای زیر را به ان اضافه نمائید:

    <table id="grid1" class="grid-body" cellpadding="0" cellspacing="0">
        <tr class="grid-header">
            @if (options.Delete)
            {
                <td><span></span></td>
            }
            @foreach (PropertyInfo item in t)
            {

                Type type = list[0].GetType();
                DisplayAttribute attr = (DisplayAttribute)type.GetProperty(item.Name).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
                if (columns.Any(x => x == item.Name))
                {
                    if (attr != null)
                    {
                <td>
                    @attr.Name
                </td>
                    }
                }

            }
        </tr>

خب در if اول بررسی می کنیم که اگر delete برابر true بود یک td برای ستون آن در نظر گرفته شود  و در حلقه foreach اطلاعات مربوط به هر property کلاس رو استخراج می کنیم که یکی از اینها نام ستون هست که در شرط

if (columns.Any(x => x == item.Name))

بررسی می کنیم که آیا نام ستون در پارامتر ورودی columns وجود دارد یا نه ، و اگر وجود دارد نام ستون رو با @attr.Name نمایش می دهیم که این شی به [Display(Name = "نام")] اشاره می کند که در مثال ما می شود: "نام" .

حالا باید سطر ها رو ایجاد کنیم، که با حلقه زیر  کار رو شروع می کنیم :

@foreach (object record in list)
{

}

سپس کدهای زیر را به آن اضافه می کنیم:

            <tr class='grid-row'>
                @if (options.Delete)
                {
                    <td>
                        @{
                    PropertyInfo pi = t.FirstOrDefault(x => x.Name == "Id");
                        
                            <a href='@(options.DeleteRoute + "/" + (pi != null ? pi.GetValue(record, null) : ""))' title="حذف" >
                                <div class="grid-button-delete">
                                </div>
                            </a>
                        }
                    </td>
                }

در قسمت شرطی ما می خواهیم دکمه delete رو برای گرید نمایش دهیم . در خط زیر

PropertyInfo pi = t.FirstOrDefault(x => x.Name == "Id");

ما property ی Id ی کلاس رو استخراج می کنیم و در تکه کد

(pi != null ? pi.GetValue(record, null) : "")

مقدار Id رو به لینک می دهیم . که مثلا اگر ما DeleteRoute  رو برابر "People/Delete" قرار داده باشیم، مسیر دکمه حذف به شکل زیر خواهد شد:

http://webdeveloper.blog.ir/people/delete/62ad24d3-e3c5-4753-a11c-d45e07c8c6d4

 که در controller مون (people) باید متد زیر را برای انجام عملیات حذف اضافه کنیم :

        public ActionResult Delete(Guid id)
        {
            People people = db.Peoples.Find(id);
            if (people == null)
            {
                return HttpNotFound();
            }
            return View(people);
        }

خب حالا میخواهیم اطلاعات رو به صورت سطر های gridView چاپ کنیم . کدهای زیر را به ادامه کدهای قبل  اضافه کنید :

                @foreach (PropertyInfo prop in t)
                {

                    Type type = list[0].GetType();
                    DisplayAttribute attr = (DisplayAttribute)type.GetProperty(prop.Name).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
                    if (columns.Any(x => x == prop.Name))
                    {
                        if (attr != null)
                        {
                    <td>
                        @prop.GetValue(record, null)
                    </td>
                        }
                    }
                }
            </tr>

توضیح کد ها مثل قبل هست و ما برای چاپ مقادیر  از کد زیر استفاده کرده ایم

@prop.GetValue(record, null)

و در نهایت :

        }
    </table>
}

:)

 

که نحوه استفاده از اون مثل زیر خواهد بود :

@GridView.Bind(@Model, new[] { "FirstName", "LastName", "NationalCode" }, new GridViewOptions() { Delete = true, DeleteRoute = "/People/Delete" })

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

دریافت
حجم: 13.2 کیلوبایت
توضیحات: فایل های پروژه GridView در ASP.NET MVC 4
 

سوالی بود در خدمتیم.

نظرات  (۸)

با سلام .ممنون از کد .
من هم همین مشکل رو دارم "The name 'GridView' does not exist in the current context" ولی متوجه جوابتون نشدم.
"پاسخ:
مطمئن شوید که ارجاعی به فضای نامی که html helper در آن تعریف شده است، در فرم view خودتون اضافه شده است."


@GridView.Bind

خطا دارم
Error 3 The name 'GridView' does not exist in the current context 178 2
پاسخ:
مطمئن شوید که ارجاعی به فضای نامی که html helper در آن تعریف شده است، در فرم view خودتون اضافه شده است.
سلام
در کجای این کدها از refactoring استفاده شده است؟
ممنون
پاسخ:
منظور Reflector بوده است.
تصحیح شد.
آقا از لطفتون بسیار سپاسگزارم.
موفق باشید.
کد رو ه تست می کنم و نتیجه رو خدمتتون عرض میکنم.
میخوام پروژمو ایمیل کنم ولی failed میده،ایمیلتون همونی هست که تو سایت گذاشتین؟
پاسخ:
بله.
من کدهای شما رو نوشتم موقع اجرا به خط زیر که میرسه کاری انجام میشه و متغییر attr را null برمیگردونه
خواهش میکنم کمکم کنین من باید پروژمو کامل کنم
"DisplayAttribute attr = (DisplayAttribute)type.GetProperty(item.Name).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault();
"
پاسخ:
امکان بررسی کدهای شما از راه دور و بدون اصل کدها میسر نیست.
اگر ممکنه کدهاتون به همراه کاری که می خواید بکنید رو برام بفرستید.
۰۴ تیر ۹۲ ، ۱۵:۴۱ محمد وکیلی
سلام و تشکر فراوان به دلیل آموزش زیبای شما.

من کد شما را تست کردم به چند مورد برخورد کردم .
1-در استفاده از EF به خطای زیر بر خوردم که با toArray مشکل حل شد.
Cannot apply indexing with [] to an expression of type System.Data.Entity.Infrastructure.DbQuery
در خط
PropertyInfo[] t = list[0].GetType().GetProperties()
2- ترتیب قرار گیری ستون ها به ترتیب columns نیست و به ترتیب مدل بود.
پاسخ:
ممنون از توجهتون.
1- خودم اینو نداشتم (احتمالا یک استثنا هست)
2- آره نیاز به کمی تغییر داره.
سلام
خیلی ممنونم از مطلب خوبتونن

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
تجدید کد امنیتی