مطالب
معرفی Selector های CSS - قسمت 2
11- S1,S2
اگر بخواهیم قالب بندی را برای چند Selector به صورت یکجا انجام دهیم، این Selector‌ها را با کاما (,) از هم جدا می‌نماییم.
<style>
    div,.content,table.main {
        color: red;
    }
</style>
<div>Text 1</div>
<p>Text 2</p>
<table class="main" border="1">
    <tr>
        <td>Cell 1</td>
        <td>Cell 2</td>
        <td>Cell 3</td>
    </tr>
    <tr>
        <td>Cell 4</td>
        <td>Cell 5</td>
        <td>Cell 6</td>
    </tr>
</table>
<span class="content">Text 3</span>
<h1>Text 4</h1>
در مثال فوق Text 1 و Text 3 و همچنین محتوای تگ table به رنگ قرمز نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 Yes  Yes  Yes Yes   Yes S1,S2  1
توجه:
Selector‌ها می‌توانند به صورت ترکیبی نیز استفاده شوند. به مثال زیر توجه کنید:
<style>
    .content .tag {
        color: red;
    }

    h1#index {
        color: blue;
    }

    ul.list li.even {
        color: green;
    }
</style>
<h1 id="index">Index</h1>
<h1>Header 1</h1>
<div class="content">
    Lorem ipsum dolor sit amet, <span class="tag">consectetuer</span> adipiscing elit.
    Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies,
    purus lectus malesuada libero, <span class="tag">sit</span> amet commodo magna 
    eros quis urna. Nunc viverra <span class="tag">imperdiet</span> enim. Fusce est.
</div>
<h1>Header 2</h1>
<div class="content">
    <ul class="list">
        <li>Item 1</li>
        <li class="even">Item 2</li>
        <li>Item 3</li>
        <li class="even">Item 4</li>
        <li>Item 5</li>
        <li class="even">Item 6</li>
    </ul>
</div>
در مثال فوق، تگ h1 که ویژگی id آن برابر index می‌باشد، با توجه به Selector ی که بصورت h1#index تعریف شده است، به رنگ آبی نمایش می‌یابد. تمامی تگ‌های span که عضو کلاس tag می‌باشند و در داخل تگ div ی قرار دارند که عضو کلاس content است، با توجه به Selector ی که بصورت .content .tag تعریف شده است، به رنگ قرمز نمایش می‌یابند. تمامی تگهای li که ویژگی class آنها برابر even می‌باشد، و فرزند تگ ul ی هستند که عضو کلاس list است، با توجه به Selector ی که بصورت ul.list li.even تعریف شده است، به رنگ سبز نمایش می‌یابند.

12- [attribute]

تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص باشند.
<style>
    [readonly] {
        background: green;
    }
</style>
<input type="text" value="Value 1" readonly="readonly"/>
<input type="text"/>
در مثال فوق، رنگ پس زمینه تگ input اول که دارای ویژگی readonly می‌باشد، به رنگ سبز نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1  9.6  7.0 2.0  4.0 [attribute]    2

13- 
 [attribute=value] 
تگ هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی دقیقا برابر با value می‌باشد.
<style>
    [lang=fa] {
        direction:rtl;
    }
</style>
<div lang="fa">متن 1</div>
در مثال فوق، جهت نوشتاری محتوای تگ div که دارای ویژگی lang با مقدار fa می‌باشد، از راست به چپ می‌شود و در سمت راست صفحه نمایش می‌یابد.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1 9.6 7.0 2.0 4.0 [attribute=value] 
 2

14-
   [attribute=value i]
تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی دقیقا برابر با value می‌باشد. همچنین value، نسبت به حروف کوچک و بزرگ حساس نمی‌باشد. 
<style>
    [lang=fa] {
        direction:rtl;
    }
</style>
<div lang="FA">متن 1</div>
در مثال فوق، جهت نوشتاری محتوای تگ div که دارای ویژگی lang با مقدار FA می‌باشد، از راست به چپ می‌شود و در سمت راست صفحه نمایش می‌یابد. با اینکه در Selector مقدار fa با حروف کوچک ذکر شده است.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 No   No   No  No 
 No [attribute=value i] 
 4

15- 
 [attribute|=value] 
تگ هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی با یک value خاص آغاز می‌شود. کلمه آغازین مقدار حتما باید با value برابر باشد یا با dash (-) از کلمه‌ی بعدی جدا شده باشد.
<style>
    [class|=info] {
        color:red
    }
</style>
<div class="info">Text 1</div>
<div class="infobar">Text 2</div>
<div class="info bar">Text 3</div>
<div class="info-bar">Text 4</div>
<div class="btninfo">Text 5</div>
<div class="btn info">Text 6</div>
<div class="btn-info">Text 7</div>
<div class="toolinfoicon">Text 8</div>
<div class="tool info icon">Text 9</div>
<div class="tool-info-icon">Text 10</div>
در مثال فوق Text 1 و Text 4 به رنگ قرمز نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1  9.6  7.0 2.0  4.0 [attribute|=value] 
 2

16- 
 [attribute^=value] 
تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی با یک value خاص آغاز می شود.
<style>
    [class^=info] {
        color: red;
    }
</style>
<div class="info">Text 1</div>
<div class="infobar">Text 2</div>
<div class="info bar">Text 3</div>
<div class="info-bar">Text 4</div>
<div class="btninfo">Text 5</div>
<div class="btn info">Text 6</div>
<div class="btn-info">Text 7</div>
<div class="toolinfoicon">Text 8</div>
<div class="tool info icon">Text 9</div>
<div class="tool-info-icon">Text 10</div>
در مثال فوق Text 1 و Text 2 و Text 3 و Text 4 به رنگ قرمز نمایش می‌یابند. 
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  7.0 3.5  4.0 [attribute^=value] 
 3

17- 
 [attribute~=value] 
تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی شامل یک value خاص می‌باشد که با Space یا فاصله‌ی خالی از سایر مقادیر جدا شده است. 
<style>
    [class~=info] {
        color: red;
    }
</style>
<div class="info">Text 1</div>
<div class="infobar">Text 2</div>
<div class="info bar">Text 3</div>
<div class="info-bar">Text 4</div>
<div class="btninfo">Text 5</div>
<div class="btn info">Text 6</div>
<div class="btn-info">Text 7</div>
<div class="toolinfoicon">Text 8</div>
<div class="tool info icon">Text 9</div>
<div class="tool-info-icon">Text 10</div>
در مثال فوق Text 1 و Text 3 و Text 6 و Text 9 به رنگ قرمز نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.1  9.6  7.0 2.0  4.0 [attribute~=value] 
 2

18- 
 [attribute*=value] 
تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی شامل یک value خاص می‌باشد. 
<style>
    [class*=info] {
        color: red;
    }
</style>
<div class="info">Text 1</div>
<div class="infobar">Text 2</div>
<div class="info bar">Text 3</div>
<div class="info-bar">Text 4</div>
<div class="btninfo">Text 5</div>
<div class="btn info">Text 6</div>
<div class="btn-info">Text 7</div>
<div class="toolinfoicon">Text 8</div>
<div class="tool info icon">Text 9</div>
<div class="tool-info-icon">Text 10</div>
در مثال فوق تمامی متون به رنگ قرمز نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  7.0 3.5  4.0 [attribute*=value] 
 3

19- 
[attribute$=value]
تگ‌هایی را انتخاب می‌نماید که دارای یک attribute یا ویژگی خاص هستند و مقدار آن ویژگی به یک value خاص ختم می‌شود. 
<style>
    [class$=info] {
        color: red;
    }
</style>
<div class="info">Text 1</div>
<div class="infobar">Text 2</div>
<div class="info bar">Text 3</div>
<div class="info-bar">Text 4</div>
<div class="btninfo">Text 5</div>
<div class="btn info">Text 6</div>
<div class="btn-info">Text 7</div>
<div class="toolinfoicon">Text 8</div>
<div class="tool info icon">Text 9</div>
<div class="tool-info-icon">Text 10</div>
در مقال فوق Text 1 و Text 5 و Text 6 و Text 7 به رنگ قرمز نمایش می‌یابند.
پشتیبانی در مرورگرها:

 Selector نسخه CSS
 3.2  9.6  7.0 3.5  4.0 [attribute$=value] 
 3   
اشتراک‌ها
دوره 3 ساعته NET MAUI.

.NET MAUI Course for Beginners – Create Cross-Platform Apps with C#

Learn how to use .NET MAUI for native cross-platform desktop and mobile development! You will learn the essentials of building mobile applications with .NET MAUI and C# while creating a Contacts app.

⭐️ Contents ⭐️
⌨️ (0:00:00) Introduction
⌨️ (0:03:42) What is .Net Maui - .Net Maui vs Xamarin Forms
⌨️ (0:06:52) Prepare Development Environment _ Create first project
⌨️ (0:12:29) Project Structure
⌨️ (0:20:28) Three elements of stateful .Net Maui
⌨️ (0:23:51) Page, Layout _ View, Namespaces
⌨️ (0:33:02) URL based navigation
⌨️ (0:51:10) Basics of ListView and Data Binding
⌨️ (1:05:58) Events Handling of ListView
⌨️ (1:16:54) Parameters in URL based Navigation _ Static Repository
⌨️ (1:35:35) Stacklayout for Edit Contact page
⌨️ (1:52:47) View Contact Details _ Update Contact
⌨️ (2:06:40) Observable Collection
⌨️ (2:14:58) Field Validation with .Net Maui CommunityToolkit
⌨️ (2:27:08) Reusable Control
⌨️ (2:40:37) Grid Layout and  Use reusable control
⌨️ (2:53:23) ContextActions _ MenuItems in ListView
⌨️ (3:03:44) SearchBar in .NetMaui 

دوره 3 ساعته NET MAUI.
اشتراک‌ها
یادگیری NET MAUI. با ساخت یک دفترچه‌ی تماس‌ها

Learn .NET MAUI while Creating a Contacts App in .NET 7 - YouTube

00:00:00 Introduction
00:02:54 What is .NET MAUI
00:06:13 Prepare Development Environment & Create first project.
00:11:50 Project Structure of .Net Maui
00:19:49 Three elements of stateful .Net Maui app
00:23:12 Page, Layout & View, Namespaces
00:32:23 URL based navigation - .Net Maui
00:50:31 Basics of ListView and Data Binding in .Net Maui
01:05:19 Events Handling of ListView in .Net Maui
01:16:15 Parameters in URL based Navigation & Static Repository in .Net Maui
01:34:56 Stacklayout for Edit Contact page in  .Net Maui
01:52:08 View Contact Details & Update Contact (.Net Maui)
02:06:01 Observable Collection
02:14:19 Field Validation with .Net Maui CommunityToolkit
02:26:18 Reusable Control in .Net Maui
02:39:42 Grid Layout and  Use reusable control in .Net Maui
02:52:44 ContextActions & MenuItems in ListView for .Net Maui
03:03:05 SearchBar in .NetMaui 

یادگیری NET MAUI. با ساخت یک دفترچه‌ی تماس‌ها
اشتراک‌ها
نکات افزایش پرفرمنس برای وبسایت

In this post, I'll talk about what the different things I've made on this website to improve the performance. Some of the optimizations are just some configuration flags to turn on, others require more changes in your code.

  • Enable HTTP/2
  • Enable TLS 1.3
  • Compress responses using Brotli or gzip
  • Add caching information
  • Optimize JavaScript / CSS files
  • Reduce the number of redirections
  • Optimize images
  • Move your servers near your visitors (GeoDNS, CDN)
  • Resource Hints: Prefetch resources
  • Remove unused resources / features
  • Minify HTML
  • Optimize JavaScript code
  • Automate almost everything! 
نکات افزایش پرفرمنس برای وبسایت
نظرات مطالب
کار با Kendo UI DataSource
بله مشکل از نام ستون‌های متناظر بود توی بخش column و تعریف template فراموش کرده بودم اونجا رو هم باید دو تا فیلدی که دارم رو همه حروفشون کوچک باشه. ممنون از دقت نظر شما. فقط یه مساله ای که دارم این هستش که تاریخ رو من به شکل Datetime دارم میخونم داخل مدل هام ولی اینجا نشون نمیده داخل جدول.  به این شکل تعریف شده داخل viewModel :
 [DisplayName("تاریخ درج")]
 public DateTime CreateDate { get; set; }
 [DisplayName("تاریخ انتشار")]
 public DateTime PublishDate { get; set; }

حتی الان یه پراپرتی دیگه از مدل رو نگاه کردم که از نوع string هستش(blogtype) و اون هم از سمت سرور برمی گرده ولی تو جدول خالی نشون میده :
[DisplayName("نوع مطلب")]
public string BlogType { get; set; }
ولی بقیه پراپرتی‌ها درست کار میکنند.
این هم کد داخل View بنده : 
 <script type="text/javascript">

            $(function () {
                var blogDataSource = new kendo.data.DataSource({
                    transport: {
                        read: {
                            url: "@Url.Action("GetLastBlogs", "Admin")",
                            dataType: "json",
                            contentType: 'application/json; charset=utf-8',
                            type: 'GET'
                        },
                        parameterMap: function (options) {
                            return kendo.stringify(options);
                        }
                    },
                    schema: {
                        data: "data",
                        total: "total",
                        model: {
                            fields: {
                                "id": { type: "number" }, //تعیین نوع فیلد برای جستجوی پویا مهم است
                                "title": { type: "string" },
                                "content": { type: "string" },
                                "imagepath": { type: "string" },
                                "attachmentid": { type: "number" },
                                "createdate": { type: "string" },
                                "publishdate": { type: "date" },
                                "blogtype": { type: "string" },
                                "lastmodified": { type: "date" },
                                "visitcount": { type: "number" },
                                "isarchived": { type: "boolean" },
                                "ispublished": { type: "boolean" },
                                "isdeleted": { type: "boolean" },
                                "tags": { type: "string" }


                            }
                        }
                    },
                    error: function (e) {
                        alert(e.errorThrown);
                    },
                    pageSize: 10,
                    sort: { field: "id", dir: "desc" },
                    serverPaging: true,
                    serverFiltering: true,
                    serverSorting: true
                });

                $("#report-grid").kendoGrid({
                    dataSource: blogDataSource,
                    autoBind: true,
                    scrollable: false,
                    pageable: true,
                    sortable: true,
                    filterable: true,
                    reorderable: true,
                    columnMenu: true,
                    columns: [
                        { field: "id", title: "شماره", width: "130px" },
                        { field: "title", title: "عنوان مطلب" },
                        { field: "createdate", title: "تاریخ درج" },
                        { field: "publishdate", title: "تاریخ انتشار" },
                        { field: "content", title: "خلاصه مطلب" },
                        { field: "blogtype", title: "نوع" },
                        { field: "lastmodified", title: "آخرین ویرایش" },
                        { field: "visitcount", title: "تعداد بازدید" },
                        {
                            field: "isarchived", title: "آرشیو شده",
                            template: '<input type="checkbox" #= isarchived ? checked="checked" : "" # disabled="disabled" ></input>'
                        },
                        {
                            field: "ispublished", title: "منتشر شده",
                            template: '<input type="checkbox" #= ispublished ? checked="checked" : "" # disabled="disabled" ></input>'
                        }

                    ]
                });
            });
            </script>
نظرات مطالب
ASP.NET MVC #11
درود
پس از خواندن تمامی مطالب سایت ، تا اینجا به این نتیجه رسیدم که آقای نصیری به استفاده از ViewModel به عنوان پلی میان View و Model تاکید دارن.
استفاده از ViewModel‌ها باعث افزایش امنیت در مقابل بعضی از حملات و افزایش Performance با دوری از SELECT * میشه.
اما مطلبی که هنوز برای من روشن نشده رهایی از Duplicate شدن منطق اعتبارسنجی مدل هاست که به صورت Attribute در ViewModel‌ها مشخص می‌شوند.و این تعریف Validation Rule‌ها سیستم رو دچار Duplicated Business می‌کنه.آیا راهی هست که تمامی Validation Rule‌ها در Model انجام شه و به صورت AutoMap بر روی ViewModel‌های متناظر اعمال شود؟
مطالب دوره‌ها
ارائه کاربری ساده‌تر انتخاب چندین آیتم از یک لیست به کمک افزونه TagIt در ASP.NET MVC
چندی قبل مطلبی را در مورد بررسی تفصیلی رابطه چند به چند در این سایت مطالعه کردید. در آن مطلب صرفا به بحث ذخیره سازی اطلاعات دریافتی از کاربر اشاره شد. برای مثال اگر مطلبی چندین برچسب دارد، چگونه باید این‌ها را در بانک اطلاعاتی به نحو صحیحی ذخیره کرد.
در مطلب جاری قصد داریم با نحوه ارائه یک UI کاربر پسند برای این منظور آشنا شویم و سؤال مهم هم این است: «چگونه می‌توان کار کاربر را در حین وارد کردن تعدادی از برچسب‌های مرتبط با یک مطلب ساده‌تر کرد؟». برای این منظور یکی از راه حل‌هایی که در بسیاری از سایت‌ها مرسوم شده است، استفاده از افزونه‌هایی مانند jQuery TagIt می‌باشد که در ادامه با نحوه استفاده از آن در ASP.NET MVC آشنا خواهیم شد.


پیشنیازها:
دریافت افزونه TagIt
همچنین دریافت jQuery UI (افزونه TagIt برای نمایش لیست Auto Complete آیتم‌ها از jQuery UI در پشت صحنه استفاده می‌کند)
<head>
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/TagIt/tagit-simple-blue.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/jquery-ui-1.8.23.custom.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Content/TagIt/tagit.js")" type="text/javascript"></script>
    @RenderSection("JavaScript", required: false)
</head>
که نهایتا نیاز است یک چنین تعاریفی را به فایل layout برنامه اضافه کنیم.

آشنایی با مدل برنامه

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

namespace jQueryMvcSample04.Models
{
    public class BlogPostViewModel
    {
        [DisplayName("عنوان"), Required(ErrorMessage = "*")]
        public string Title { set; get; }

        [DisplayName("متن"), Required(ErrorMessage = "*")]
        public string Body { set; get; }

        /// <summary>
        /// آرایه‌ای محدود از برچسب‌های این مطلب خاص به صورت جی‌سون که پیشتر ثبت شده است
        /// هدف استفاده در حین ویرایش مطلب
        /// </summary>
        public string InitialTags { set; get; }

        /// <summary>
        /// آرایه‌ای جی‌سونی از تمام برچسب‌های موجود در سیستم
        /// هدف نمایش منوی انتخاب برچسب‌ها از لیست
        /// </summary>
        public string TagsSource { set; get; }

        /// <summary>
        /// آرایه‌ای از برچسب‌های وارد شده توسط کاربر در حین ثبت مطلب
        /// </summary>
        [DisplayName("برچسب‌ها"), Required(ErrorMessage = "*")]
        public string[] Tags { set; get; }

        public int? Id { set; get; }
    }
}
اگر به نام این کلاس دقت کنید، به ViewModel ختم شده است. از این لحاظ که حاوی خواصی می‌باشد که عموما جهت رندر کردن صحیح UI مورد استفاده قرار می‌گیرند و معادلی در سمت بانک اطلاعاتی نخواهند داشت.
افزونه TagIt برای نمایش اطلاعات خود به دو منبع داده نیاز دارد:
الف) TagsSource : لیستی است به فرمت JSON، از هر آنچه که در سیستم پیشتر به عنوان یک برچسب ثبت شده است. از این لیست برای نمایش منوی خودکار انتخاب آیتم‌ها استفاده می‌شود.
ب) InitialTags : لیستی است به فرمت JSON، از تمام برچسب‌های مرتبط با یک مطلب. از این اطلاعات در حین ویرایش یک مطلب استفاده خواهد شد.

در این ViewModel یک خاصیت دیگر به شکل آرایه، به نام Tags تعریف شده است که لیست برچسب‌های وارد شده توسط کاربر را دریافت خواهد کرد.


معرفی کنترلر برنامه

using System.Web.Mvc;
using jQueryMvcSample04.Extensions;
using jQueryMvcSample04.Models;

namespace jQueryMvcSample04.Controllers
{
    public class HomeController : Controller
    {
        [HttpGet]
        public ActionResult Index(int? id)
        {
            //در ابتدای کار تمام تگ‌های موجود در سیستم از بانک اطلاعاتی دریافت خواهند شد
            //از این تگ‌ها برای تشکیل منوی انتخاب برچسب‌ها استفاده می‌شود
            var tagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();

            //همچنین صرفا برچسب‌های مطلب جاری که پیشتر ثبت شده‌اند نیز باید از بانک اطلاعاتی دریافت گردند
            //از این برچسب‌ها برای ویرایش یک مطلب موجود استفاده خواهد شد
            var init = new[] { "ASP.NET" }.ToJson();

            var model = new BlogPostViewModel
            {
                TagsSource = tagsSource,
                InitialTags = init,
                Id = id
            };
            return View(model);
        }

        [HttpPost]
        public ActionResult Index(BlogPostViewModel data)
        {
            if (this.ModelState.IsValid)
            {
                //todo: save data
                // ...
                return RedirectToAction(actionName: "index", controllerName: "home");
            }

            //در صورت بروز خطا مجددا اطلاعات موجود نمایش داده خواهند شد
            data.TagsSource = new[] { "C#", "C++", "C", "ASP.NET", "MVC" }.ToJson();
            data.InitialTags = data.Tags.ToJson();
            return View(data);
        }
    }
}


با توجه به توضیحاتی که ارائه شد، کنترلر برنامه ساختار واضح‌تری را یافته است. در اولین بار نمایش صفحه، لیست منبع داده تگ‌ها و همچنین تگ‌های مرتبط با یک مطلب (در صورت وجود) به View ارائه خواهند شد.
از همین ViewModel، در عملیات Post نیز استفاده گردیده و اطلاعات دریافت می‌گردد.
تعریف متد الحاقی ToJson مورد استفاده را نیز در ادامه ملاحظه می‌نمائید:
using System.Linq;
using System.Web.Script.Serialization;

namespace jQueryMvcSample04.Extensions
{
    public static class JsonExt
    {
        public static string ToJson(this string[] initialTags)
        {            
            if (initialTags == null || !initialTags.Any())
                return "[]";
            else
                return new JavaScriptSerializer().Serialize(initialTags);
        }
    }
}

و مرحله آخر تعریف View متناظر است

@model jQueryMvcSample04.Models.BlogPostViewModel
@{
    ViewBag.Title = "Index";
}
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>ثبت مطلب جدید</legend>
        @Html.HiddenFor(model => model.Id)
        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Body)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Body)
            @Html.ValidationMessageFor(model => model.Body)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.Tags)
        </div>
        <div class="editor-field">
            <ul id="tagsList" dir="ltr" name="Tags">
            </ul>
            @Html.ValidationMessageFor(model => model.Tags)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
@section JavaScript
{
    <script type="text/javascript">
    $(document).ready(function () {
            var tagsSource = @Html.Raw(Model.TagsSource);
            $('#tagsList').tagit({
                 tagSource: tagsSource, 
                 select: true, 
                 triggerKeys: ['enter', 'comma', 'tab'],
                 initialTags:  @Html.Raw(Model.InitialTags) 
              });
});
    </script>
}
در این View دو نکته حائز اهمیت هستند:
الف) برای نمایش افزونه TagIt از یک ul با id مساوی tagsList استفاده شده است.
ب) خواص اضافی موجود در ViewModel که اطلاعات JSON ایی مورد نیاز را بازگشت می‌دهند در قسمت اسکریپت صفحه مورد استفاده قرار گرفته‌اند. در اینجا نیاز است از Html.Raw استفاده شود تا اطلاعات مرتبط با JSON اشتباها encode نشده و قابل استفاده باشند.

دریافت مثال و پروژه کامل این قسمت
jQueryMvcSample04.zip
نظرات مطالب
ایجاد سرویس چندلایه‎ی WCF با Entity Framework در قالب پروژه - 9
راه حلی که بنده پیدا کردم؛ تغییراتی در مقدار سایز پیام دریافتی به شکل زیر در appConfig مربوط به پروژه WinApp از این شکل :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_IMyNewsService" />
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:4636/SedaService.svc" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_IMyNewsService" contract="MyNewsService.IMyNewsService"
                name="BasicHttpBinding_IMyNewsService" />
        </client>
    </system.serviceModel>
</configuration>
به این شکل تغییر دهید :
<bindings>
            <basicHttpBinding>
                <binding maxReceivedMessageSize="2147483647" name="BasicHttpBinding_IMyNewsService" />
            </basicHttpBinding>
        </bindings>
حالا امنیت رو نمیدونم اینجا نقض کردم یا نه؟ لطفا اگر اطلاعاتی دارید راهنمایی بفرمایید که امنیت نقض شده یا نه؟ و کلا با یه عدد این شکلی که Max رو مشخص می‌کنه بنظرم نسبت به آینده نگری یک نرم افزار تجاری منطقی نیست...
مطالب
قالب‌های سفارشی برای HtmlHelperها

در ابتدای بحث، برای آشنایی بیشتر با HTML Helperها به مطالعه این مقاله بپردازین.
در این مقاله قرار است برای یک HTML Helper خاص، قالب نمایشی اختصاصی خودمان را طراحی کنیم و به نحوی HTML Helper موجود را سفارشی سازی کنیم. به عنوان مثال می‌خواهیم خروجی یک EditorFor() برای یک نوع خاص، به حالت دلخواهی باشد که ما خودمان آن را تولیدش کردیم؛ یا اصلا نه. حتی می‌شود برای خروجی یک EditorFor() که خصوصیتی از جنس string را می‌خواهیم به آن انتساب دهیم، به جای تولید input، یک مقدار متنی را برگردانیم. به این حالت:

        <div>
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div>
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>
        
        <div>
            @Html.LabelFor(model => model.Genre, htmlAttributes: new { @class = "control-label col-md-2" })
            <div>
                @Html.EditorFor(model => model.Genre, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Genre, "", new { @class = "text-danger" })
            </div>
        </div>

در ادامه یک پروژه‌ی عملی را شروع کرده و در آن کاری را که می‌خواهیم، انجام می‌دهیم. پروژه‌ی ما به این شکل می‌باشد که قرار است در آن به ثبت کتاب بپردازیم و برای هر کتاب هم یک سبک داریم و قسمت سبک کتاب‌های ما یک Enum است که از قبل می‌خواهیم مقدارهایش را تعریف کنیم. 

مدل برنامه

    public class Books
    {
        public int Id { get; set; }
        [Required]
        [StringLength(255)]
        public string Name { get; set; }
        public Genre Genre { get; set; }
    }
    public enum Genre
    {
        [Display(Name = "Non Fiction")]
        NonFiction,
        Romance,
        Action,
        [Display(Name = "Science Fiction")]
        ScienceFiction
    }

در داخل کلاس Books یک خصوصیت از جنس Genre برای سبک کتاب‌ها داریم و در داخل نوع شمارشی Genre، سبک‌های ما تعریف شده‌اند. همچنین هر کدام از سبک‌ها هم به ویژگی Display مزین شده‌اند تا بتونیم بعدا از مقدار آنها استفاده کنیم. 


کنترلر برنامه

    public class BookController : Controller
    {
        // GET: Book
        public ActionResult Index()
        {
            return View(DataAccess.DataContext.Book.ToList());
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Books model)
        {
            if (!ModelState.IsValid)
                return View(model);

            try
            {
                DataAccess.DataContext.Book.Add(model);
                DataAccess.DataContext.SaveChanges();
                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
                return View(model);
            }
        }

        public ActionResult Edit(int id)
        {
            try
            {
                var book = DataAccess.DataContext.Book.Find(id);
                return View(book);
            }
            catch (Exception ex)
            {
                return View("Error");
            }
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Books model)
        {
            if (!ModelState.IsValid)
                return View(model);

            try
            {
                DataAccess.DataContext.Book.AddOrUpdate(model);
                DataAccess.DataContext.SaveChanges();
                return RedirectToAction("Index");
            }
            catch (Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
                return View(model);
            }
        }

        public ActionResult Details(int id)
        {
            try
            {
                var book = DataAccess.DataContext.Book.Find(id);
                return View(book);
            }
            catch (Exception ex)
            {
                return View("Error");
            }
        }
    }

در قسمت کنترلر هم کار خاصی جز عملیات اصلی نوشته نشده‌است. لیست کتاب‌ها را از پایگاه داده بیرون آوردیم و از طریق اکشن Index به نمایش گذاشتیم. با اکشن‌های Create، Edit و Details هم کارهای روتین مربوط به خودشان را انجام دادیم. نکته‌ی قابل تذکر، DataAccess می‌باشد که کلاسی است که با آن ارتباط برقرار شده با EF و سپس اطلاعات واکشی و تزریق می‌شوند.

View مربوط به اکشن Create برنامه

@using Book.Entities
@model Book.Entities.Books

@{
    ViewBag.Title = "Create";
}
<h2>New Book</h2>
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    
    <div>
        <h4>Books</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div>
            @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
            <div>
                @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
            </div>
        </div>
        
        <div>
            @Html.LabelFor(model => model.Genre, htmlAttributes: new { @class = "control-label col-md-2" })
            <div>
                @Html.EditorFor(model => model.Genre, new { htmlAttributes = new { @class = "form-control" } })
                @Html.ValidationMessageFor(model => model.Genre, "", new { @class = "text-danger" })
            </div>
        </div>

        <div>
            <div>
                <input type="submit" value="Create" />
                <input type="reset" value="Reset" />
                @Html.ActionLink("Back to List", "Index", null, new {@class="btn btn-default"})
            </div>
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

View برنامه هم همان ویویی است که خود Visual Studio برای ما ساخته‌است. به جز یک سری دست‌کاری‌هایی داخل سی‌اس‌اس، هدف از گذاشتن View مربوط به Create این بود که قرار است بر روی این قسمت کار کنیم. اگر پروژه رو اجرا کنید و به قسمت Create بروید، مشاهده خواهید کرد که برای Genre یک input ساخته شده‌است که کاربر باید در آن مقدار وارد کند. ولی اگر یادتان باشد، ما سبک‌های نگارشی خودمان را در نوع شمارشی Genre ایجاد کرده بودیم. پس عملا باید یک لیست به کاربر نشان داده شود که تا از آن لیست، نوع را انتخاب کند. می‌توانیم بیایم همینجا در داخل View مربوطه، به‌جای استفاده از HTML Helper پیش‌فرض، از DropDownList یا EnumFor استفاده کنیم و به طریقی این لیست را ایجاد کنیم. ولی چون قرار است در این مثال به شرح موضوع مقاله خودمان بپردازیم، این کار را انجام نمی‌دهیم.

در حقیقیت می‌خوایم متد EditorFor را طوری سفارشی سازی کنیم که برای نوع شمارشی Genre، به صورت خودکار یک لیست ایجاد کرده و برگرداند. از نسخه‌ی سوم ASP.NET MVC به بعد این امکان برای توسعه دهنده‌ها فراهم شده‌است. شما می‌توانید در پوشه‌ی Shared داخل پوشه Views برنامه، پوشه‌ای را به اسم EditorTemplates ایجاد کنید؛ همینطور DisplayTemplates و برای نوع خاصی که می‌خواهید سفارشی‌سازی را برای آن انجام دهید، یک PartialView بسازید. 

Views/Shared/DisplayTemplates/<type>.cshtml
یک PartialView در داخل پوشه EditorTemplates به نام Genre.cshtml ایجاد کنید. برای اینکه مشاهده کنید چطور کار می‌کند، کافی‌است یک فایل متنی اینجا تهیه کرده و بعد پروژه را اجرا کرده و به قسمت Create روید تا تغییرات را مشاهده کنید. بله! به‌جای inputی که از قبل وجود داشت، فقط متن شما آنجا نوشته شده‌است. (به عکسی که در بالا قرار دارد هم می‌تونید نگاه کنید)

کاری که الان میخواهیم انجام دهیم این است که یک SelectListItem ایجاد کرده تا مقدارهای نوع Genreمان داخلش باشد و بتوانیم به راحتی برای ساختن DropDownList از آن استفاده کنیم. برای این کار Helper مخصوص خودمان را ایجاد می‌کنیم. پوشه‌ای به اسم Helpers در کنار پوشه‌های Controllers، Models ایجاد می‌کنیم و در داخل آن کلاسی به اسم EnumHelpers می‌سازیم.

    public static class EnumHelpers
    {
        public static IEnumerable<SelectListItem> GetItems(
            this Type enumType, int? selectedValue)
        {
            if (!typeof(Enum).IsAssignableFrom(enumType))
            {
                throw new ArgumentException("Type must be an enum");
            }

            var names = Enum.GetNames(enumType);
            var values = Enum.GetValues(enumType).Cast<int>();

            var items = names.Zip(values, (name, value) =>
                    new SelectListItem
                    {
                        Text = GetName(enumType, name),
                        Value = value.ToString(),
                        Selected = value == selectedValue
                    }
                );
            return items;
        }

        static string GetName(Type enumType, string name)
        {
            var result = name;

            var attribute = enumType
                .GetField(name)
                .GetCustomAttributes(inherit: false)
                .OfType<DisplayAttribute>()
                .FirstOrDefault();

            if (attribute != null)
            {
                result = attribute.GetName();
            }

            return result;
        }
    }

در توضیح کد بالا عنوان کرد که متدها به‌صورت متدهای الحاقی به نوع Type نوشته شدند. کار خاصی در بدنه‌ی متدها انجام نشده‌است. در بدنه‌ی متد اول لیست آیتم‌ها را تولید کردیم. در هنگام ساخت SelectListItem برای گرفتن Text، متد GetName را صدا زدیم. برای اینکه بتوانیم مقدار ویژگی Display که در هنگام تعریف نوع شمارشی استفاده کردیم را بدست بیاریم، باید چک کنیم ببینیم که آیا این آیتم به این ویژگی مزین شده‌است یا نه. اگر شده بود مقدار را می‌گیریم و به خصوصیت Text متد اول انتساب می‌دهیم.

@using Book.Entities
@using Book.Web.Helpers
@{
    var items = typeof(Genre).GetItems((int?)Model);
}

@Html.DropDownList("", items, new {@class="form-control"})

کدهایی که در بالا مشاهده می‌کنید کدهایی می‌باشند که قرار است داخل PartialViewی Genre قرار دهیم که در پوشه‌ی EditorTemplates ساختیم. ابتدا آمدیم آیتم‌ها را گرفتیم و بعد به DropDownList دادیم تا لیست نوع را برای ما بسازد. حالا اگه برنامه را اجرا کنید می‌بینید که EditorFor برای شما یه لیست از نوع شمارشی ساخته و حالا قابل استفاده هست.

 

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید  

مطالب
Test Driven Development #3
در پست قبلی با  نوشتن یک تست ساده، با مفهوم TDD بیشتر آشنا شدیم .در این پست قصد بر این  است که به وسیله Mvc.Net شروع به نوشتن تست‌های جدی‌تر کرده و از مزایای آن بهره ببریم .
برای  شروع یک پروژه Mvc.Net ساخته و Nunit را در آن نصب می‌کنیم.
مدل زیر را در پوشه مدل‌ها می‌سازیم:
    public class Idea
    {
        public static List<Idea> Ideas = new List<Idea>
            {
                new Idea{Content="سایتی که در ایده به اشتراک گذاشته شود",Title = "سایت ایده ها"},
                new Idea{Content="عینک گوگل را فارسی کنم",Title = "عینک گوگل"},
            };
        public string Content { get;  set;}
        public string Title { get; set; }
    }
  [TestFixture]
    public class IdeaTest
    {
        [Test]
        public void ShouldDisplayListOfIdea()
        {
            var viewResult = new IdeaController().Index() as ViewResult;
            Assert.AreEqual(Idea.Ideas, viewResult.Model)
             Assert.IsNotNull(viewResult.Model);


        }    
 }

کد بالا شامل مقایسه مقدار خروجی Action با لیستی از مدل Idea و همچنین اطمینان از خالی نبودن مدل ارسالی به view می‌باشد.در خط اول یک وهله از Controller می سازیم و Action مورد نظر را به شی از جنس ViewResult  تبدیل(Cast) می‌کنیم پس از آن به وسیله viewResult.Model به مدلی که به سمت view پاس داده می‌شود دسترسی خواهیم داشت.اکنون اگر تست را اجرا  کنیم با خطای کامپایل مواجه می‌شویم.حال Controller و Action مورد نظر را به صورتی  که تست ما پاس شود پیاده سازی می‌کنیم.
    public class IdeaController : Controller
    {

        public ActionResult Index()
        {
            return View(Idea.Ideas);
        }
      }
کد بالا مقدار Ideas را به view برمیگرداند.
در این دروره ما به تست کردن ویو‌ها نخواهیم پرداخت.
تست بعدی تست ساده ای است که فقط میخواهیم از از وجود داشتن یک Action و نام view بازگشتی اطمینان حاصل کنیم.
   [Test]
        public void ShouldLoadCreateIdeaView()
        {
            var viewResult = new IdeaController().Create() as ViewResult;
            Assert.AreEqual(string.Empty, viewResult.ViewName);
        }
در کد بالا مثل تست قبل، یک وهله از Controller می سازیم و سپس نام view بازگشتی را با string.Empty مقایسه میکنیم به این معنی که view خروجی Action ما نباید نامی داشته باشد و براساس قرار دادها باید هم نام اکشن باشد.
حال نوبت به پیاده سازی اکشن رسید.:
        public ActionResult Create()
        {
            return View();
        }
در تست بعدی میخواهیم عملیات اضافه شدن یک Idea را به لیست بررسی کنیم:
    [Test]
        public void ShouldAddIdeaItem()
        {
            var idea = new Idea { Title = "شبکه اجتماعی ", Content = " شبکه اجتماعی سینمایی" };
            var redirectToRouteResult = new IdeaController().Create(idea) as RedirectToRouteResult;
            Assert.Contains(idea, Idea.Ideas);
            Assert.AreEqual("Index",redirectToRouteResult.RouteValues["action"]);
        }
تست بالا نیز مانند دو تست قبل است با این تفاوت که مخواهیم ریدارکت شدن به یک Action  خاص را نیز تست کنیم.برای همین مقدار خروجی را به RedirectToRouteResult تبدیل می‌کنیم.در ادامه یک Idea جدید ساخته و به لست اضافه میکنیم سپس از وجود داشتن آن در لیست Ideas اطمینان حاصل می‌کنیم.در خط آخر نیز نام Action که انتظار داریم بعد از اضافه شدن یک Idea ,کاربر به آن هدایت شود را ست می‌کنیم.
پیاده سازی Action  به شکل زیر است:
        public ActionResult Create(Idea idea)
        {
            Idea.Ideas.Add(idea);
            return RedirectToAction("Index");
        }

در این پست شما با مدل تست نویسی برایMvc.Net آشنا شدید.در مطلب بعدی شما با تست حذف و اصلاح Ideas آشنا خواهید شد.