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

در این آموزش قصد دارم نحوه ایجاد یک Slideshow به صورت داینامیک را با ASP.NET طراحی کنم(منظور از ایجاد Slideshow به صورت داینامیک این است که عکس‌ها را به صورت داینامیک از DB بخواند).

اولین گام این است که Plugin مورد نظر را دریافت کنید که من از پلاگین Orbit استفاده کرده ام

ابتدا یک DataBase با نام DynamicSlideShow ایجاد و یک جدول با ساختار زیر با نام Picturesدرون آن ایجاد می‌کنیم

گام بعدی ایجاد یک پروژه Asp.Net با زبان C# و اضافه کردن فایلهای پلاگین به پروژه و ایجاد یک Handler برای بازیابی داده‌ها از DB است. ساختار   Solutionما باید به صورت زیر باشد

برای اینکه بتوانیم با DB ارتباط برقرار کنیم از EF استفاده می‌کنیم به همین منظور ابتدا یک Model  به نام DynamicSlideShowModel ایجاد می‌کنیم

در گام بعد بر روی GenerateFromDatabase  انتخاب کرده و بر روی دکمه Next کلیک می‌کنیم و در مراحل بعد ابتدا DB مربوط به مثال (DynamicSlideShow) و جدول آن را انتخاب می‌کنیم . حال یک Model به درون پوشه App_Code  اضافه شده است  

در ادامه برای واکشی رکوردهای موجود در جدول  Pictures  کدهای زیر را درون Handler می‌نویسیم

  var ctx = new DynamicSlideShowEntities();
  var list = ctx.Pictures.ToList();
  string str = JsonConvert.SerializeObject(list);
  context.Response.Write(str);

  

در این کدها تنها نکته‌ای که احتیاج به توضیح دارد این است که ابتدا یک لیست از رکوردها را از جدول Pictures واکشی می‌کنیم و برای پاس دادن به کلاینت ما آن‌ها را به فرمت Json تبدیل کرده ایم که برای تبدبل از کنابخانه آماده Newtonsoft.Json.dll استفاده کرده ایم 

حال باید با استفاده از jQuery کدهای درون Handler را اجرا کنیم؛ برای همین منظور یک صفحه با نام default.aspx ایجاد کرده و کدهای زیر را درون آن می‌نویسیم

<head runat="server">
    <title>Dynamic SlideShow</title>
    <link href="CSS/orbit-1.2.3.css" rel="stylesheet" type="text/css" />
    <script src="Script/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="Script/jquery.orbit-1.2.3.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $.ajax({
                url: "Handler.ashx",
                contentType: "application/json; charset=utf-8",
                success: function (data) {
                    $.each(data, function (i, b) {
                        var str = '<img src="' + b.PicturePath + '" alt="' + b.PictureText + '"/>';
                        $("#featured").append(str);
                    });
                    $('#featured').orbit();
                },
                dataType: "json"

            });
            });
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div id="featured">
    </div>
    </form>
</body>

در اینجا ابتدا با استفاده از متد ajax کتابخانه jQuery کدهای درون Handler را اجرا کرده و به ازای هر المان موجود در جدول یک تگ img به صفحه اضافه می‌کنیم.

مطالب
لینک‌های هفته دوم دی

وبلاگ‌ها ، سایت‌ها و مقالات ایرانی (داخل و خارج از ایران)


ASP. Net


طراحی و توسعه وب


PHP


اس‌کیوال سرور


سی شارپ


SharePoint

عمومی دات نت


ویندوز


مسایل اجتماعی و انسانی برنامه نویسی


متفرقه


مطالب
کار با Kendo UI DataSource
Kendo UI DataSource جهت تامین داده‌های سمت کلاینت ویجت‌های مختلف KendoUI طراحی شده‌است و به عنوان یک اینترفیس استاندارد قابل استفاده توسط تمام کنترل‌های داده‌ای Kendo UI کاربرد دارد. Kendo UI DataSource امکان کار با منابع داده محلی، مانند اشیاء و آرایه‌های جاوا اسکریپتی و همچنین منابع تامین شده از راه دور، مانند JSON، JSONP و XML را دارد. به علاوه توسط آن می‌توان اعمال ثبت، ویرایش و حذف اطلاعات، به همراه صفحه بندی، گروه بندی و مرتب سازی داده‌ها را کنترل کرد.


استفاده از منابع داده محلی
در ادامه مثالی را از نحوه‌ی استفاده از یک منبع داده محلی جاوا اسکریپتی، مشاهده می‌کنید:
    <script type="text/javascript">
        $(function () {
            var cars = [
                { "Year": 2000, "Make": "Hyundai", "Model": "Elantra" },
                { "Year": 2001, "Make": "Hyundai", "Model": "Sonata" },
                { "Year": 2002, "Make": "Toyota", "Model": "Corolla" },
                { "Year": 2003, "Make": "Toyota", "Model": "Yaris" },
                { "Year": 2004, "Make": "Honda", "Model": "CRV" },
                { "Year": 2005, "Make": "Honda", "Model": "Accord" },
                { "Year": 2000, "Make": "Honda", "Model": "Accord" },
                { "Year": 2002, "Make": "Kia", "Model": "Sedona" },
                { "Year": 2004, "Make": "Fiat", "Model": "One" },
                { "Year": 2005, "Make": "BMW", "Model": "M3" },
                { "Year": 2008, "Make": "BMW", "Model": "X5" }
            ];

            var carsDataSource = new kendo.data.DataSource({
                data: cars
            });

            carsDataSource.read();
            alert(carsDataSource.total());
        });
    </script>
در اینجا cars آرایه‌ای از اشیاء جاوا اسکریپتی بیانگر ساختار یک خودرو است. سپس برای معرفی آن به Kendo UI، کار با مقدار دهی خاصیت data مربوط به new kendo.data.DataSource شروع می‌شود.
ذکر new kendo.data.DataSource به تنهایی به معنای مقدار دهی اولیه است و در این حالت منبع داده مورد نظر، استفاده نخواهد شد. برای مثال اگر متد total آن‌را جهت یافتن تعداد عناصر موجود در آن فراخوانی کنید، صفر را بازگشت می‌دهد. برای شروع به کار با آن، نیاز است ابتدا متد read را بر روی این منبع داده مقدار دهی شده، فراخوانی کرد.


استفاده از منابع داده راه دور

در برنامه‌های کاربردی، عموما نیاز است تا منبع داده را از یک وب سرور تامین کرد. در اینجا نحوه‌ی خواندن اطلاعات JSON بازگشت داده شده از جستجوی توئیتر را مشاهده می‌کنید:
    <script type="text/javascript">
        $(function () {
            var twitterDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "http://search.twitter.com/search.json",
                        dataType: "jsonp",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET',
                        data: { q: "#kendoui" }
                    },
                    schema: { data: "results" }
                },
                error: function (e) {
                    alert(e.errorThrown.stack);
                }
            });
        });
    </script>
در قسمت transport، جزئیات تبادل اطلاعات با سرور راه دور مشخص می‌شود؛ برای مثال url ارائه دهنده‌ی سرویس، dataType بیانگر نوع داده مورد انتظار و data کار مقدار دهی پارامتر مورد انتظار توسط سرویس توئیتر را انجام می‌دهد. در اینجا چون صرفا عملیات خواندن اطلاعات صورت می‌گیرد، خاصیت read مقدار دهی شده‌است.
در قسمت schema مشخص می‌کنیم که اطلاعات JSON بازگشت داده شده توسط توئیتر، در فیلد results آن قرار دارد.


کار با منابع داده OData

علاوه بر فرمت‌های یاد شده، Kendo UI DataSource امکان کار با اطلاعاتی از نوع OData را نیز دارا است که تنظیمات ابتدایی آن به صورت ذیل است:
    <script type="text/javascript">
            var moviesDataSource = new kendo.data.DataSource({
                type: "odata",
                transport: {
                    read: "http://demos.kendoui.com/service/Northwind.svc/Orders"
                },
                error: function (e) {
                    alert(e.errorThrown.stack);
                }
            });
        });
    </script>
همانطور که ملاحظه می‌کنید، تنظیمات ابتدایی آن اندکی با حالت remote data پیشین متفاوت است. در اینجا ابتدا نوع داده‌ی بازگشتی مشخص می‌شود و در قسمت transport، خاصیت read آن، آدرس سرویس را دریافت می‌کند.


یک مثال: دریافت اطلاعات از ASP.NET Web API

یک پروژه‌ی جدید ASP.NET را آغاز کنید. تفاوتی نمی‌کند که Web forms باشد یا MVC؛ از این جهت که مباحث Web API در هر دو یکسان است.
سپس یک کنترلر جدید Web API را به نام ProductsController با محتوای زیر ایجاد کنید:
using System.Collections.Generic;
using System.Web.Http;

namespace KendoUI02
{
    public class Product
    {
        public int Id { set; get; }
        public string Name { set; get; }
    }

    public class ProductsController : ApiController
    {
        public IEnumerable<Product> Get()
        {
            var products = new List<Product>();
            for (var i = 1; i <= 100; i++)
            {
                products.Add(new Product { Id = i, Name = "Product " + i });
            }
            return products;
        }
    }
}
در این مثال، هدف صرفا ارائه یک خروجی ساده JSON از طرف سرور است.
در ادامه نیاز است تعریف مسیریابی ذیل نیز به فایل Global.asax.cs برنامه اضافه شود تا بتوان به آدرس api/products در سایت، دسترسی یافت:
using System;
using System.Web.Http;
using System.Web.Routing;

namespace KendoUI02
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.MapHttpRoute(
               name: "DefaultApi",
               routeTemplate: "api/{controller}/{id}",
               defaults: new { id = RouteParameter.Optional }
               );
        }
    }
}

در ادامه فایلی را به نام Index.html (یا در یک View و یا یک فایل aspx دلخواه)، محتوای ذیل را اضافه کنید:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Kendo UI: Implemeting the Grid</title>

    <link href="styles/kendo.common.min.css" rel="stylesheet" type="text/css" />
    <link href="styles/kendo.default.min.css" rel="stylesheet" type="text/css" />
    <script src="js/jquery.min.js" type="text/javascript"></script>
    <script src="js/kendo.all.min.js" type="text/javascript"></script>
</head>
<body>

    <div id="report-grid"></div>
    <script type="text/javascript">
        $(function () {
            var productsDataSource = new kendo.data.DataSource({
                transport: {
                    read: {
                        url: "api/products",
                        dataType: "json",
                        contentType: 'application/json; charset=utf-8',
                        type: 'GET'
                    }
                },
                error: function (e) {
                    alert(e.errorThrown.stack);
                },
                pageSize: 5,
                sort: { field: "Id", dir: "desc" }
            });

            $("#report-grid").kendoGrid({
                dataSource: productsDataSource,
                autoBind: true,
                scrollable: false,
                pageable: true,
                sortable: true,
                columns: [
                    { field: "Id", title: "#" },
                    { field: "Name", title: "Product" }
                ]
            });
        });
    </script>
</body>
</html>
- ابتدا فایل‌های اسکریپت و CSS مورد نیاز Kendo UI اضافه شده‌اند.
- گرید صفحه، در محل div ایی با id مساوی report-grid تشکیل خواهد شد.
- سپس DataSource ایی که به آدرس api/products اشاره می‌کند، تعریف شده و در آخر productsDataSource را توسط یک kendoGrid نمایش داده‌ایم.
- نحوه‌ی تعریف productsDataSource، در قسمت استفاده از منابع داده راه دور ابتدای بحث توضیح داده شد. در اینجا فقط دو خاصیت pageSize و sort نیز به آن اضافه شده‌اند. این دو خاصیت بر روی نحوه‌ی نمایش گرید نهایی تاثیر گذار هستند. pageSize تعداد رکورد هر صفحه را مشخص می‌کند و sort نحوه‌ی مرتب سازی را بر اساس فیلد Id و در حالت نزولی قرار می‌دهد.
- در ادامه، ابتدایی‌ترین حالت کار با kendoGrid را ملاحظه می‌کنید.
- تنظیم dataSource و autoBind: true (حالت پیش فرض)، سبب خواهند شد تا به صورت خودکار، اطلاعات JSON از مسیر api/products خوانده شوند.
- سه خاصیت بعدی صفحه بندی و مرتب سازی خودکار ستون‌ها را فعال می‌کنند.
- در آخر هم دو ستون گرید، بر اساس نام‌های خواص کلاس Product تعریف شده‌اند.


سورس کامل این قسمت را از اینجا می‌توانید دریافت کنید:
KendoUI02.zip
 
مطالب دوره‌ها
افزودن مشخص کننده فیلد اجباری در ASP.NET MVC به صورت خودکار توسط jQuery
در ASP.NET MVC زمانیکه خاصیتی با ویژگی Required مزین می‌شود، تا زمان اعتبار سنجی فرم، هیچ نشانی را از خود بروز نمی‌دهد. برای مثال علاقمندیم مانند شکل زیر، یک ستاره پس از فیلدهای اجباری ظاهر گردد:


انجام اینکار نیز با دو سطر کد نویسی توسط jQuery قابل انجام است.
در ادامه خروجی رندر شده فیلدی را که پرکردن آن الزامی است مشاهده می‌کنید:
        <div class="editor-label">
            <label for="Title">عنوان</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-required="*" id="Title" name="Title" type="text" value="" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>
تنها کاری که باید صورت گیرد، یافتن مواردی است که حاوی data-val-required هستند و سپس افزودن یک ستاره پس از آن‌ها:
        $(function() {    
            $('[data-val-required]').after('<span class="required-indicator"> (*)</span>');
        });
برای اعمال رنگ به آن نیز می‌توان کمی css سایت را ویرایش کرد:
.required-indicator
{
color: red;
font-size: 1.2em;
font-weight: bold;
}
مطالب
استفاده از ادیتور CKEditor در صفحات ASP.NET
در مواردی مانند همین وبلاگ احتیاج به داشتن محل نوشتن، در محیط ادیتور مانندی میباشد که برای این مورد میتوان از ادیتور CKEditor استفاده کرد. بعد از دانلود محتویات مربوطه (که در اینجا از dll مربوط به asp.net استفاده نشده بلکه فایل‌های ادیتور مستقیما مورد استفاده قرار گرفته است)، کافیست لینک‌های مربوط به جاوا اسکریپت و استایل را به هدر اضافه کرد:

<link href="admin/editor/contents.css" rel="stylesheet" type="text/css" />
<script src="admin/editor/ckeditor.js" type="text/javascript"></script>
 و در کد صفحه، بسته به زبان مورد نیاز، یک اسکریپت را به صفحه اضافه نمود؛ در اینجا فارسی استفاده شده:
var clientId = "txtPostBody";
var strEditor = strBodyFa = "CKEDITOR.replace( '" + clientId + "',{skin : 'office2003' , language : 
'fa' , contentsLangDirection : 'rtl'});";
ClientScript.RegisterStartupScript(GetType(), clientId + "_Editor", strBodyFa, true);
در مثال بالا برای باکس متنی به بنام txtPostBody، کد مربوط ایجاد و به صفحه افزوده شده است.
نکته: پوشه مربوط به ادیتور در محلی که در دسترس صفحه باشد باید قرار گیرد که در اکثر موارد قسمت ادمین وب یا اپلیکشن است
مطالب
ASP.NET MVC #20

تهیه گزارشات تحت وب به کمک WebGrid

WebGrid از ASP.NET MVC 3.0 به صورت توکار به شکل یک Html Helper در دسترس می‌باشد و هدف از آن ساده‌تر سازی تهیه گزارشات تحت وب است. البته این گرید، تنها گرید مهیای مخصوص ASP.NET MVC نیست و پروژه MVC Contrib یا شرکت Telerik نیز نمونه‌های دیگری را ارائه داده‌اند؛ اما از این جهت که این Html Helper، بدون نیاز به کتابخانه‌های جانبی در دسترس است، بررسی آن ضروری می‌باشد.


صورت مساله

لیستی از کارمندان به همراه حقوق ماهیانه آن‌ها در دست است. اکنون نیاز به گزارشی تحت وب، با مشخصات زیر می‌باشد:
1- گزارش باید دارای صفحه بندی بوده و هر صفحه تنها 10 ردیف را نمایش دهد.
2- سطرها باید یک در میان دارای رنگی متفاوت باشند.
3- ستون حقوق کارمندان در پایین هر صفحه، باید دارای جمع باشد.
4- بتوان با کلیک بر روی عنوان هر ستون، اطلاعات را بر اساس ستون انتخابی، مرتب ساخت.
5- لینک‌های حذف یا ویرایش یک ردیف نیز در این گزارش مهیا باشد.
6- لیست تهیه شده، دارای ستونی به نام «ردیف» نیست. این ستون را نیز به صورت خودکار اضافه کنید.
7- لیست نهایی اطلاعات، دارای ستونی به نام مالیات نیست. فقط حقوق کارمندان ذکر شده است. ستون محاسبه شده مالیات نیز باید به صورت خودکار در این گزارش نمایش داده شود. این ستون نیز باید دارای جمع پایین هر صفحه باشد.
8- تمام اعداد این گزارش در حین نمایش باید دارای جدا کننده سه رقمی باشند.
9- تاریخ‌های موجود در لیست، میلادی هستند. نیاز است این تاریخ‌ها در حین نمایش شمسی شوند.
10- انتهای هر صفحه گزارش باید بتوان برچسب «صفحه y/n» را مشاهده کرد. n در اینجا منظور تعداد کل صفحات است و y شماره صفحه جاری می‌باشد.
11- انتهای هر صفحه گزارش باید بتوان برچسب «رکوردهای y تا x از n» را مشاهده کرد. n در اینجا منظور تعداد کل رکوردها است.
12- نام کوچک هر کارمند، ضخیم نمایش داده شود.
13- به ازای هر شماره کارمندی، یک تصویر در پوشه images سایت وجود دارد. برای مثال images/id.jpg. ستونی برای نمایش تصویر متناظر با هر کارمند نیز باید اضافه شود.
14- به ازای هر کارمند، تعدادی پروژه هم وجود دارد. پروژه‌های متناظر را توسط یک گرید تو در تو نمایش دهید.


راه حل به کمک استفاده از WebGrid

ابتدا یک پروژه خالی ASP.NET MVC را آغاز کنید. سپس مدل‌های زیر را به آن اضافه نمائید (یک کارمند که می‌تواند تعداد پروژه منتسب داشته باشد):

using System;
using System.Collections.Generic;

namespace MvcApplication17.Models
{
public class Employee
{
public int Id { set; get; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime AddDate { get; set; }
public double Salary { get; set; }
public IList<Project> Projects { get; set; }
}
}

namespace MvcApplication17.Models
{
public class Project
{
public int Id { set; get; }
public string Name { set; get; }
}
}

سپس منبع داده نمونه زیر را به پروژه اضافه کنید. به عمد از ORM‌ خاصی استفاده نشده تا بتوانید پروژه جاری را به سادگی در یک پروژه آزمایشی جدید،‌ تکرار کنید.
using System;
using System.Collections.Generic;

namespace MvcApplication17.Models
{
public static class EmployeeDataSource
{
public static IList<Employee> CreateEmployees()
{
var list = new List<Employee>();
var rnd = new Random();
for (int i = 1; i <= 1000; i++)
{
list.Add(new Employee
{
Id = i + 1000,
FirstName = "fName " + i,
LastName = "lName " + i,
AddDate = DateTime.Now.AddYears(-rnd.Next(1, 10)),
Salary = rnd.Next(400, 3000),
Projects = CreateRandomProjects()
});
}
return list;
}

private static IList<Project> CreateRandomProjects()
{
var list = new List<Project>();
var rnd = new Random();
for (int i = 0; i < rnd.Next(1, 7); i++)
{
list.Add(new Project
{
Id = i,
Name = "Project " + i
});
}
return list;
}
}
}


در ادامه یک کنترلر جدید را با محتوای زیر اضافه نمائید:
using System.Web.Mvc;
using MvcApplication17.Models;

namespace MvcApplication17.Controllers
{
public class HomeController : Controller
{
[HttpPost]
public ActionResult Delete(int? id)
{
return RedirectToAction("Index");
}

[HttpGet]
public ActionResult Edit(int? id)
{
return View();
}

[HttpGet]
public ActionResult Index(string sort, string sortdir, int? page = 1)
{
var list = EmployeeDataSource.CreateEmployees();
return View(list);
}
}
}

علت تعریف متد index با پارامترهای sort و غیره به URLهای خودکاری از نوع زیر بر می‌گردد:

http://localhost:3034/?sort=LastName&sortdir=ASC&page=3

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

سپس یک View خالی را نیز برای متد Index ایجاد کنید. تا اینجا تنظیمات اولیه پروژه انجام شد.
کدهای کامل View را در ادامه ملاحظه می‌کنید:

@using System.Globalization
@model IList<MvcApplication17.Models.Employee>

@{
ViewBag.Title = "Index";
}

@helper WebGridPageFirstItem(WebGrid grid)
{
@(((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1));
}

@helper WebGridPageLastItem(WebGrid grid)
{
if (grid.TotalRowCount < (grid.PageIndex + 1 * grid.RowsPerPage))
{
@grid.TotalRowCount;
}
else
{
@((grid.PageIndex + 1) * grid.RowsPerPage);
}
}

<h2>Employees List</h2>

@{
var grid = new WebGrid(
source: Model,
canPage: true,
rowsPerPage: 10,
canSort: true,
defaultSort: "FirstName"
);
var salaryPageSum = 0;
var taxPageSum = 0;
var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1);
}

<div id="container">
@grid.GetHtml(
tableStyle: "webgrid",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
rowStyle: "webgrid-row-style",
htmlAttributes: new { id = "MyGrid" },
mode: WebGridPagerModes.All,
columns: grid.Columns(
grid.Column(header: "#",
style: "text-align-center-col",
format: @<text>@(rowIndex++)</text>),
grid.Column(columnName: "FirstName", header: "First Name",
format: @<span style='font-weight: bold'>@item.FirstName</span>,
style: "text-align-center-col"),
grid.Column(columnName: "LastName", header: "Last Name"),
grid.Column(header: "Image",
style: "text-align-center-col",
format: @<text><img alt="@item.Id" src="@Url.Content("~/images/" + @item.Id + ".jpg")" /></text>),
grid.Column(columnName: "AddDate", header: "Start",
style: "text-align-center-col",
format: item =>
{
int ym = item.AddDate.Year;
int mm = item.AddDate.Month;
int dm = item.AddDate.Day;
var persianCalendar = new PersianCalendar();
int ys = persianCalendar.GetYear(new DateTime(ym, mm, dm, new GregorianCalendar()));
int ms = persianCalendar.GetMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));
int ds = persianCalendar.GetDayOfMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));
return ys + "/" + ms.ToString("00") + "/" + ds.ToString("00");

}),
grid.Column(columnName: "Salary", header: "Salary",
format: item =>
{
salaryPageSum += item.Salary;
return string.Format("${0:n0}", item.Salary);
},
style: "text-align-center-col"),
grid.Column(header: "Tax", canSort: true,
format: item =>
{
var tax = item.Salary * 0.2;
taxPageSum += tax;
return string.Format("${0:n0}", tax);
}),
grid.Column(header: "Projects", columnName: "Projects",
style: "text-align-center-col",
format: item =>
{
var subGrid = new WebGrid(
source: item.Projects,
canPage: false,
canSort: false
);
return subGrid.GetHtml(
htmlAttributes: new { id = "MySubGrid" },
tableStyle: "webgrid",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
rowStyle: "webgrid-row-style"
);
}),
grid.Column(header: "",
style: "text-align-center-col",
format: item => @Html.ActionLink(linkText: "Edit", actionName: "Edit",
controllerName: "Home", routeValues: new { id = item.Id },
htmlAttributes: null)),
grid.Column(header: "",
format: @<form action="/Home/Delete/@item.Id" method="post"><input type="submit"
onclick="return confirm('Do you want to delete this record?');"
value="Delete"/></form>),
grid.Column(header: "", format: item => item.GetSelectLink("Select"))
)
)

<strong>Page:</strong> @(grid.PageIndex + 1) / @grid.PageCount,
<strong>Records:</strong> @WebGridPageFirstItem(@grid) - @WebGridPageLastItem(@grid) of @grid.TotalRowCount

@*
@if (@grid.HasSelection)
{
@RenderPage("~/views/path/_partial_view.cshtml", new { Employee = grid.SelectedRow })
}
*@
</div>

@section script{
<script type="text/javascript">
$(function () {
$('#MyGrid tbody:first').append(
'<tr class="total-row"><td></td>\
<td></td><td></td><td></td>\
<td><strong>Total:</strong></td>\
<td>@string.Format("${0:n0}", @salaryPageSum)</td>\
<td>@string.Format("${0:n0}", @taxPageSum)</td>\
<td></td><td></td><td></td></tr>');
});
</script>
}


توضیحات ریز جزئیات View فوق


تعریف ابتدایی شیء WebGrid و مقدار دهی آن
در ابتدا نیاز است یک وهله از شیء WebGrid را ایجاد کنیم. در اینجا می‌توان تنظیم کرد که آیا نیاز است اطلاعات نمایش داده شده دارای صفحه بندی (canPage) خودکار باشند؟ منبع داده (source) کدام است. در صورت فعال سازی صفحه بندی خودکار، چه تعداد ردیف (rowsPerPage) در هر صفحه نمایش داده شود. آیا نیاز است بتوان با کلیک بر روی سر ستون‌ها، اطلاعات را بر اساس فیلد متناظر با آن مرتب (canSort) ساخت؟ همچنین در صورت نیاز به مرتب سازی، اولین باری که گرید نمایش داده می‌شود، بر اساس چه فیلدی (defaultSort) باید مرتب شده نمایش داده شود:

@{ 
var grid = new WebGrid(
source: Model,
canPage: true,
rowsPerPage: 10,
canSort: true,
defaultSort: "FirstName"
);
var salaryPageSum = 0;
var taxPageSum = 0;
var rowIndex = ((grid.PageIndex + 1) * grid.RowsPerPage) - (grid.RowsPerPage - 1);
}

در اینجا همچنین سه متغیر کمکی هم تعریف شده که از این‌ها برای تهیه جمع ستون‌های حقوق و مالیات و همچنین نمایش شماره ردیف جاری استفاده می‌شود. فرمول نحوه محاسبه اولین ردیف هر صفحه را هم ملاحظه می‌کنید. شماره ردیف‌های بعدی، rowIndex++ خواهند بود.


تعریف رنگ و لعاب گرید نمایش داده شده
در ادامه به کمک متد grid.GetHtml، رشته‌ای معادل اطلاعات HTML صفحه جاری، بازگشت داده می‌شود. در اینجا می‌توان یک سری خواص تکمیلی را تنظیم نمود. برای مثال:
tableStyle: "webgrid",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
rowStyle: "webgrid-row-style",
htmlAttributes: new { id = "MyGrid" },

هر کدام از این رشته‌ها در حین رندر نهایی گرید،‌ تبدیل به یک class خواهند شد. برای نمونه:

<div id="container">
<table class="webgrid" id="MyGrid">
<thead>
<tr class="webgrid-header">

به این ترتیب با اندکی ویرایش css سایت، می‌توان انواع و اقسام رنگ‌ها را به سطرها و ستون‌های گرید نهایی اعمال کرد. برای مثال اطلاعات زیر را به فایل css سایت اضافه نمائید:

/* Styles for WebGrid
-----------------------------------------------------------*/
.webgrid
{
width: 100%;
margin: 0px;
padding: 0px;
border: 0px;
border-collapse: collapse;
font-family: Tahoma;
font-size: 9pt;
}

.webgrid a
{
color: #000;
}

.webgrid-header
{
padding: 0px 5px;
text-align: center;
border-bottom: 2px solid #739ace;
height: 20px;
border-top: 2px solid #D6E8FF;
border-left: 2px solid #D6E8FF;
border-right: 2px solid #D6E8FF;
}

.webgrid-header th
{
background-color: #eaf0ff;
border-right: 1px solid #ddd;
}

.webgrid-footer
{
padding: 6px 5px;
text-align: center;
background-color: #e8eef4;
border-top: 2px solid #3966A2;
height: 25px;
border-bottom: 2px solid #D6E8FF;
border-left: 2px solid #D6E8FF;
border-right: 2px solid #D6E8FF;
}

.webgrid-alternating-row
{
height: 22px;
background-color: #f2f2f2;
border-bottom: 1px solid #d2d2d2;
border-left: 2px solid #D6E8FF;
border-right: 2px solid #D6E8FF;
}

.webgrid-row-style
{
height: 22px;
border-bottom: 1px solid #d2d2d2;
border-left: 2px solid #D6E8FF;
border-right: 2px solid #D6E8FF;
}

.webgrid-selected-row
{
font-weight: bold;
}

.text-align-center-col
{
text-align: center;
}

.total-row
{
background-color:#f9eef4;
}

همانطور که ملاحظه می‌کنید، رنگ‌های ردیف‌ها، هدر و فوتر گرید و غیره در اینجا تنظیم می‌شوند.
به علاوه اگر دقت کرده باشید در تعاریف گرید، htmlAttributes هم مقدار دهی شده است. در اینجا به کمک یک anonymously typed object، مقدار id گرید مشخص شده است. از این id در حین کار با jQuery‌ استفاده خواهیم کرد.


تعیین نوع Pager
پارامتر دیگری که در متد grid.GetHtml تنظیم شده است، mode: WebGridPagerModes.All می‌باشد. WebGridPagerModes یک enum با محتوای زیر است و توسط آن می‌توان نوع Pager گرید را تعیین کرد:

[Flags]
public enum WebGridPagerModes
{
Numeric = 1,
//
NextPrevious = 2,
//
FirstLast = 4,
//
All = 7,
}

نحوه تعریف ستون‌های گرید
اکنون به مهم‌ترین قسمت تهیه گزارش رسیده‌ایم. در اینجا با مقدار دهی پارامتر columns، نحوه نمایش اطلاعات ستون‌های مختلف مشخص می‌گردد. مقداری که باید در اینجا تنظیم شود، آرایه‌ای از نوع WebGridColumn می‌باشد و مرسوم است به کمک متد کمکی grid.Columns،‌ اینکار را انجام داد.
متد کمکی grid.Column، یک وهله از شیء WebGridColumn را بر می‌گرداند و از آن برای تعریف هر ستون استفاده خواهیم کرد. توسط پارامتر columnName آن،‌ نام فیلدی که باید اطلاعات ستون جاری از آن اخذ شود مشخص می‌شود. به کمک پارامتر header،‌ عبارت سرستون متناظر تنظیم می‌گردد. پارامتر format، مهم‌ترین و توانمندترین پارامتر متد grid.Column است:

grid.Column(columnName: "FirstName", header: "First Name",
format: @<span style='font-weight: bold'>@item.FirstName</span>,
style: "text-align-center-col"),
grid.Column(columnName: "LastName", header: "Last Name"),

پارامتر format، به نحو زیر تعریف شده است:

Func<dynamic, object> format

به این معنا که هر بار پیش از رندر سطر جاری، زمانیکه قرار است سلولی رندر شود، یک شیء dynamic در اختیار شما قرار می‌گیرد. این شیء dynamic یک رکورد از اطلاعات Model جاری است. به این ترتیب به اطلاعات تمام سلول‌های ردیف جاری دسترسی خواهیم داشت. بر این اساس هر نوع پردازشی را که لازم بود، انجام دهید (شبیه به فرمول نویسی در ابزارهای گزارش سازی، اما اینبار با کدهای سی شارپ) و مقدار فرمت شده نهایی را به صورت یک رشته بر گردانید. این رشته نهایتا در سلول جاری درج خواهد شد.
اگر از پارامتر فرمت استفاده نشود، همان مقدار فیلد جاری بدون تغییری رندر می‌گردد.
حداقل به دو نحو می‌توان پارامتر فرمت را مقدار دهی کرد:

format: @<span style='font-weight: bold'>@item.FirstName</span>
or
format: item =>
{
salaryPageSum += item.Salary;
return string.Format("${0:n0}", item.Salary);
}

مستقیما از توانمندی‌های Razor استفاده کنید. مثلا یک تگ کامل را بدون نیاز به محصور سازی آن بین "" شروع کنید. سپس @item به وهله‌ای از رکورد در دسترس اشاره می‌کند که در اینجا وهله‌ای از شیء کارمند است.
و یا همانند روشی که برای محاسبه جمع حقوق هر صفحه مشاهده می‌کنید، مستقیما از lambda expressions برای تعریف یک anonymous delegate استفاده کنید.


نحوه اضافه کردن ستون ردیف
ستون ردیف، یک ستون محاسبه شده (calculated field) است:

grid.Column(header: "#",
style: "text-align-center-col",
format: @<text>@(rowIndex++)</text>),

نیازی نیست حتما یک grid.Column، به فیلدی در کلاس کارمند اشاره کند. مقدار سفارشی آن را به کمک پارامتر format تعیین خواهیم کرد. هر بار که قرار است یک ردیف رندر شود، یکبار این پارامتر فراخوانی خواهد شد. فرمول محاسبه rowIndex ابتدای صفحه را نیز پیشتر ملاحظه نمودید.


نحوه اضافه کردن ستون سفارشی تصاویر کارمندها
ستون تصویر کارمندها نیز مستقیما در کلاس کارمند تعریف نشده است. بنابراین می‌توان آن‌را با مقدار دهی صحیح پارامتر format ایجاد کرد:

grid.Column(header: "Image",
style: "text-align-center-col",
format: @<text><img alt="@item.Id" src="@Url.Content("~/images/" + @item.Id + ".jpg")" /></text>),


در این مثال، تصاویر کارمندها در پوشه images واقع در ریشه سایت، قرار دارند. به همین جهت از متد Url.Content برای مقدار دهی صحیح آن استفاده کردیم. به علاوه در اینجا @item.Id به Id رکورد در حال رندر اشاره می‌کند.


نحوه تبدیل تاریخ‌ها به تاریخ شمسی
در ادامه بازهم به کمک پارامتر format، یک وهله از شیء dynamic اشاره کننده به رکورد در حال رندر را دریافت می‌کنیم. سپس فرصت خواهیم داشت تا بر این اساس، فرمول نویسی کنیم. دست آخر هم رشته مورد نظر نهایی را بازگشت می‌دهیم:

grid.Column(columnName: "AddDate", header: "Start",
style: "text-align-center-col",
format: item =>
{
int ym = item.AddDate.Year;
int mm = item.AddDate.Month;
int dm = item.AddDate.Day;
var persianCalendar = new PersianCalendar();
int ys = persianCalendar.GetYear(new DateTime(ym, mm, dm, new GregorianCalendar()));
int ms = persianCalendar.GetMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));
int ds = persianCalendar.GetDayOfMonth(new DateTime(ym, mm, dm, new GregorianCalendar()));
return ys + "/" + ms.ToString("00") + "/" + ds.ToString("00");
}),


اضافه کردن ستون سفارشی مالیات
در کلاس کارمند، خاصیت حقوق وجود دارد اما مالیات خیر. با توجه به آن می‌توانیم به کمک پارامتر format، به اطلاعات شیء dynamic در حال رندر دسترسی داشته باشیم. بنابراین به اطلاعات حقوق دسترسی داریم و سپس با کمی فرمول نویسی، مقدار نهایی مورد نظر را بازگشت خواهیم داد. همچنین در اینجا می‌توان نحوه بازگشت مقدار حقوق را به صورت رشته‌ای حاوی جدا کننده‌های سه رقمی نیز مشاهده کرد:

grid.Column(columnName: "Salary", header: "Salary",
format: item =>
{
salaryPageSum += item.Salary;
return string.Format("${0:n0}", item.Salary);
},
style: "text-align-center-col"),
grid.Column(header: "Tax", canSort: true,
format: item =>
{
var tax = item.Salary * 0.2;
taxPageSum += tax;
return string.Format("${0:n0}", tax);
}),


اضافه کردن گردید‌های تو در تو
متد Grid.GetHtml، یک رشته را بر می‌گرداند. بنابراین در هر چند سطح که نیاز باشد می‌توان یک گرید را بر اساس اطلاعات دردسترس رندر کرد و سپس بازگشت داد:

grid.Column(header: "Projects", columnName: "Projects",
style: "text-align-center-col",
format: item =>
{
var subGrid = new WebGrid(
source: item.Projects,
canPage: false,
canSort: false
);
return subGrid.GetHtml(
htmlAttributes: new { id = "MySubGrid" },
tableStyle: "webgrid",
headerStyle: "webgrid-header",
footerStyle: "webgrid-footer",
alternatingRowStyle: "webgrid-alternating-row",
selectedRowStyle: "webgrid-selected-row",
rowStyle: "webgrid-row-style"
);
}),


در اینجا کار اصلی از طریق پارامتر format شروع می‌شود. سپس به کمک item.Projects به لیست پروژه‌های هر کارمند دسترسی خواهیم داشت. بر این اساس یک گرید جدید را تولید کرد و سپس رشته معادل با آن را به کمک متد subGrid.GetHtml دریافت و بازگشت می‌دهیم. این رشته در سلول جاری درج خواهد شد. به نوعی یک گزارش master detail یا sub report را تولید کرده‌ایم.


اضافه کردن دکمه‌های ویرایش، حذف و انتخاب
هر سه دکمه ویرایش، حذف و انتخاب در ستون‌هایی سفارشی قرار خواهند گرفت. بنابراین مقدار دهی header و format متد grid.Column کفایت می‌کند:

grid.Column(header: "",
style: "text-align-center-col",
format: item => @Html.ActionLink(linkText: "Edit", actionName: "Edit",
controllerName: "Home", routeValues: new { id = item.Id },
htmlAttributes: null)),
grid.Column(header: "",
format: @<form action="/Home/Delete/@item.Id" method="post"><input type="submit"
onclick="return confirm('Do you want to delete this record?');"
value="Delete"/></form>),
grid.Column(header: "", format: item => item.GetSelectLink("Select"))


نکته جدیدی که در اینجا وجود دارد متد item.GetSelectLink می‌باشد. این متد جزو متدهای توکار گرید است و کار آن بازگشت دادن شیء grid.SelectedRow می‌باشد. این شیء پویا، حاوی اطلاعات رکورد انتخاب شده است. برای مثال اگر نیاز باشد این اطلاعات به صفحه‌ای ارسال شود، می‌توان از روش زیر استفاده کرد:

@if (@grid.HasSelection)
{
@RenderPage("~/views/path/_partial_view.cshtml", new { Employee = grid.SelectedRow })
}


نمایش برچسب‌های صفحه x از n و رکوردهای x تا y از z
در یک گزارش خوب باید مشخص باشد که صفحه جاری، کدامین صفحه از چه تعداد صفحه کلی است. یا رکوردهای صفحه جاری چه بازه‌ای از تعداد رکوردهای کلی را تشکیل می‌دهند. برای این منظور چند متد کمکی به نام‌های WebGridPageFirstItem و WebGridPageLastItem تهیه شده‌اند که آن‌ها را در ابتدای View ارائه شده، مشاهده نمودید:

<strong>Page:</strong> @(grid.PageIndex + 1) / @grid.PageCount, 
<strong>Records:</strong> @WebGridPageFirstItem(@grid) - @WebGridPageLastItem(@grid) of @grid.TotalRowCount

نمایش جمع ستون‌های حقوق و مالیات در هر صفحه
گرید توکار همراه با ASP.NET MVC در این مورد راه حلی را ارائه نمی‌دهد. بنابراین باید اندکی دست به ابتکار زد. مثلا:

@section script{
<script type="text/javascript">
$(function () {
$('#MyGrid tbody:first').append(
'<tr class="total-row"><td></td>\
<td></td><td></td><td></td>\
<td><strong>Total:</strong></td>\
<td>@string.Format("${0:n0}", @salaryPageSum)</td>\
<td>@string.Format("${0:n0}", @taxPageSum)</td>\
<td></td><td></td><td></td></tr>');
});
</script>
}

در این مثال به کمک jQuery با توجه به اینکه id گرید ما MyGrid است، یک ردیف سفارشی که همان جمع محاسبه شده است، به tbody جدول نهایی تولیدی اضافه می‌شود. از tbody:first هم در اینجا استفاده شده است تا ردیف اضافه شده به گریدهای تو در تو اعمال نشود.
سپس فایل Views\Shared\_Layout.cshtml را گشوده و از section تعریف شده، برای مقدار دهی master page سایت، استفاده نمائید:

<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
@RenderSection("script", required: false)
</head>

نظرات مطالب
تعیین Fallback font برای قلم‌های فارسی در WPF
به این صورت کار می‌کنه:


<TextBlock 
            Text="نمایش مخلوطی از متن فارسی و متن English با هم"
            Margin="7"
            FontFamily="Fonts/irsans.ttf#Iranian Sans, Comic Sans Ms"
            FontSize="25"
            FlowDirection="RightToLeft"
            VerticalAlignment="Top" HorizontalAlignment="Center"  />

اشتراک‌ها
پروژه Metro UI CSS

Metro UI CSS a set of styles to create a site with an interface similar to Windows 8.  

پروژه Metro UI CSS