var infoTable = new PdfPTable(6)
{
WidthPercentage = 100,
RunDirection = PdfWriter.RUN_DIRECTION_RTL,
ExtendLastRow = false,
SpacingBefore = 15,
};
infoTable.DefaultCell.Border = 2;
infoTable.AddCell(new PdfPCell(new Phrase("اطلاعات شناسنامه ای", tfont))
{
RunDirection = PdfWriter.RUN_DIRECTION_RTL,
HorizontalAlignment = Element.ALIGN_LEFT,
Border = 2,
PaddingBottom = 15,
Colspan=2
});
infoTable.AddCell(new PdfPCell(new Phrase("اطلاعات خانوادگی", tfont))
{
RunDirection = PdfWriter.RUN_DIRECTION_RTL,
HorizontalAlignment = Element.ALIGN_LEFT,
Border = 2,
PaddingBottom = 15,
Colspan = 2
});
infoTable.AddCell(new PdfPCell(new Phrase("اطلاعات دانشگاهی", tfont))
{
RunDirection = PdfWriter.RUN_DIRECTION_RTL,
HorizontalAlignment = Element.ALIGN_LEFT,
Border = 2,
PaddingBottom = 15,
Colspan = 2
});
var universityLogoImage = Image.GetInstance(imgPath);
universityLogoImage.ScaleAbsolute(30, 30);
//این عکس رو اصلا نشون نمیده topTable.AddCell(new PdfPCell(universityLogoImage) { HorizontalAlignment = Element.ALIGN_CENTER, Border = 2, }); infoTable.AddCell(new PdfPCell(new Phrase("وضعیت تاهل:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0][22].ToString() == "0" ? "مجرد" : "متاهل", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("کد تحصیلی:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0][0].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("تحصیلات همسر:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["wife_edu"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("شماره پرونده:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["t_parvande_num"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("تحصیلات پدر:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["father_edu"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("کد مرکز خدمات:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["bime_code"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("شغل پدر:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["father_job"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("پایه قبولی:", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase(dt.Rows[0]["enter_paye"].ToString(), docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 2, PaddingBottom = 10, }); infoTable.AddCell(new PdfPCell(new Phrase("", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 0 }); infoTable.AddCell(new PdfPCell(new Phrase("", docFont)) { RunDirection = PdfWriter.RUN_DIRECTION_RTL, HorizontalAlignment = Element.ALIGN_LEFT, Border = 0 }); int[] infoTableColumnsWidth = { 20, 15, 20, 15,20,15 }; infoTable.SetWidths(infoTableColumnsWidth); doc.Add(infoTable);
Blazor 5x
مبانی Blazor
- قسمت 4 : بخش 1 - Data Binding
- قسمت 5 : بخش 2 - کامپوننتها
- قسمت 6 : بخش 3 - چرخههای حیات کامپوننتها
- قسمت 7 : بخش 4 - انتقال اطلاعات از کامپوننتهای فرزند به کامپوننت والد
- قسمت 8 : بخش 5 - تامین محتوای نمایشی کامپوننتهای فرزند توسط کامپوننت والد
- قسمت 9 : بخش 6 - ساده سازی تعاریف ویژگیهای المانها و انتقال پارامترها به چندین زیر سطح
- قسمت 10 : بخش 7 - مسیریابی
- قسمت 11 : بخش 8 - کار با جاوا اسکریپت
- قسمت 12 : بخش 9 - یک تمرین
کار با فرمها
- قسمت 13 : بخش 1 - کار با EF Core در برنامههای Blazor Server
- قسمت 14 : بخش 2 - تعریف فرمها و اعتبارسنجی آنها
- قسمت 15 : بخش 3 - ویرایش اطلاعات
- قسمت 16 : بخش 4 - تهیه سرویسهای آپلود تصاویر
- قسمت 17 : بخش 5 - آپلود تصاویر
- قسمت 18 : بخش 6 - حذف اطلاعات
- قسمت 19 : بخش 7 - نکات ویژهی کار با EF-Core در برنامههای Blazor Server
- قسمت 20 : بخش 8 - استفاده از یک کامپوننت ثالث HTML Editor
- قسمت 21 : بخش 1 - افزودن قالب ابتدایی Identity
- قسمت 22 : بخش 2 - ورود به سیستم و خروج از آن
- قسمت 23 : بخش 3 - کار با نقشهای کاربران
- قسمت 26 : ایجاد و تنظیمات اولیه
- قسمت 27 : کار با سرویسهای Web API
- قسمت 28 : نمایش لیست اطلاعات دریافتی از Web API
- قسمت 29 : یک تمرین: رزرو کردن یک اتاق انتخابی
- قسمت 30 : افزودن پرداخت آنلاین توسط درگاه مجازی پرباد
- قسمت 31 : بخش 1 - انجام تنظیمات اولیه
- قسمت 32 : بخش 2 - ثبت نام، ورود به سیستم و خروج از آن
- قسمت 33 : بخش 3 - بهبود تجربهی کاربری عدم دسترسیها
- پیاده سازی سیاستهای دسترسی پویای سمت سرور و کلاینت در برنامههای Blazor WASM
- قسمت اول : معرفی SSR
- قسمت دوم : بررسی حالت رندر سمت سرور
- قسمت سوم : روش ارتقاء برنامههای Blazor Server قدیمی به دات نت 8
- قسمت چهارم : معرفی فرمهای جدید تعاملی
- قسمت پنجم : امکان تعریف جزیرههای تعاملی Blazor Server
- قسمت ششم : نکات تکمیلی ویژگی راهبری بهبود یافتهی صفحات SSR
- قسمت هفتم : امکان تعریف جزیرههای تعاملی Blazor WASM
- قسمت هشتم : مدیریت انتقال اطلاعات Pre-Rendering سمت سرور، به جزایر تعاملی
- قسمت نهم : معرفی حالت رندر تعاملی خودکار
- قسمت دهم : مدیریت حالت کاربران در روشهای مختلف رندر
- قسمت یازدهم : قالب جدید پیاده سازی اعتبارسنجی و احراز هویت - بخش اول
- قسمت دوازدهم : قالب جدید پیاده سازی اعتبارسنجی و احراز هویت - بخش دوم
- قسمت سیزدهم : امکان تعریف Sections
- قسمت چهاردهم : امکان استفاده از کامپوننتهای Blazor در برنامههای ASP.NET Core 8x
- Best practiceهای یک پروژه Blazor
- کامپوننتهای جنریک در Blazor
- مدیریت حالت در برنامههای Blazor توسط الگوی Observer - قسمت اول
- مدیریت حالت در برنامههای Blazor توسط الگوی Observer - قسمت دوم
- روش آپلود فایلها به همراه اطلاعات یک مدل در برنامههای Blazor WASM 5x
- دریافت و نمایش فایلهای PDF در برنامههای Blazor WASM
- ارسال خطاهای رخدادهی در برنامههای سمت کلاینت Blazor WASM، به تلگرام
- بهبود کارآیی نمایش لیستها در Blazor با استفاده از دایرکتیو key@
- معرفی Blazor Hybrid
- امکان استفاده از کتابخانههای native در Blazor WASM 6x
- امکان ساخت برنامههای دسکتاپ چندسکویی Blazor در دات نت 6
- حل مشکل بارگذاری اولیه دستورات جاوا اسکریپتی در پروژههای Blazor
- پیاده سازی Remote Validation در Blazor
- روش ایجاد پروژههای کتابخانهای کامپوننتهای Blazor
- استفاده از date picker شمسی جاوا اسکریپتی در Blazor با قابلیت ورود تاریخ به صورت دستی
- ارائهی قالبی عمومی برای استفاده از تقویمهای جاوااسکریپتی در Blazor
- بهبود صفحهی بارگذاری اولیه در Blazor WASM
- استفاده از چند دکمه با عملکردهای مختلف برای ارسال یک EditForm در Blazor
- دستیابی به HttpContext در Blazor Server
- جلوگیری از دوباره اجرا شدن ناخواستهی متدهای نامتقارن در Blazor
- نکات ویژه کار با عملیات نامتقارن در Blazor Server
- استفاده از لنگر (anchor) برای اسکرول به قسمت خاصی از صفحه در Blazor Server
public class UserModel { public int Id { get; set; } [Required(ErrorMessage = "(*)")] [Display(Name = "نام")] [StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "نام باید حداقل 3 و حداکثر 10 حرف باشد")] public string FirstName { get; set; } [Required(ErrorMessage = "(*)")] [Display(Name = "نام خانوادگی")] [StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "نام خانوادگی باید حداقل 3 و حداکثر 10 حرف باشد")] public string LastName { get; set; } }
public class UserViewModel { public string FirstName { get; set; } public string LastName { get; set; } }
در ادامه قصد داریم راه حلی را به کمک جایگزین سازی Providerهای توکار ASP.NET MVC با نمونهی سازگار با AutoMapper، ارائه دهیم، به نحوی که دیگر نیازی نباشد تا این ویژگیها را در ViewModelها تکرار کرد.
قسمتهایی از ASP.NET MVC که باید جهت انتقال خودکار ویژگیها تعویض شوند
ASP.NET MVC به صورت توکار دارای یک ModelMetadataProviders.Current است که از آن جهت دریافت ویژگیهای هر خاصیت استفاده میکند. میتوان این تامین کنندهی ویژگیها را به نحو ذیل سفارشی سازی نمود.
در اینجا IConfigurationProvider همان Mapper.Engine.ConfigurationProvider مربوط به AutoMapper است. از آن جهت استخراج اطلاعات نگاشتهای AutoMapper استفاده میکنیم. برای مثال کدام خاصیت Model به کدام خاصیت ViewModel نگاشت شدهاست. اینکارها توسط متد الحاقی GetMappedAttributes انجام میشوند که در ادامهی مطلب معرفی خواهد شد.
public class MappedMetadataProvider : DataAnnotationsModelMetadataProvider { private readonly IConfigurationProvider _mapper; public MappedMetadataProvider(IConfigurationProvider mapper) { _mapper = mapper; } protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var mappedAttributes = containerType == null ? attributes : _mapper.GetMappedAttributes(containerType, propertyName, attributes.ToList()); return base.CreateMetadata(mappedAttributes, containerType, modelAccessor, modelType, propertyName); } }
شبیه به همین کار را باید برای ModelValidatorProviders.Providers نیز انجام داد. در اینجا یکی از تامین کنندههای ModelValidator، از نوع DataAnnotationsModelValidatorProvider است که حتما نیاز است این مورد را نیز به نحو ذیل سفارشی سازی نمود. در غیراینصورت error messages موجود در ویژگیهای تعریف شده، به صورت خودکار منتقل نخواهند شد.
public class MappedValidatorProvider : DataAnnotationsModelValidatorProvider { private readonly IConfigurationProvider _mapper; public MappedValidatorProvider(IConfigurationProvider mapper) { _mapper = mapper; } protected override IEnumerable<ModelValidator> GetValidators( ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) { var mappedAttributes = metadata.ContainerType == null ? attributes : _mapper.GetMappedAttributes(metadata.ContainerType, metadata.PropertyName, attributes.ToList()); return base.GetValidators(metadata, context, mappedAttributes); } }
و در اینجا پیاده سازی متد GetMappedAttributes را ملاحظه میکنید.
ASP.NET MVC هر زمانیکه قرار است توسط متدهای توکار خود مانند Html.TextBoxFor, Html.ValidationMessageFor، اطلاعات خاصیتها را تبدیل به المانهای HTML کند، از تامین کنندههای فوق جهت دریافت اطلاعات ویژگیهای مرتبط با هر خاصیت استفاده میکند. در اینجا فرصت داریم تا ویژگیهای مدل را از تنظیمات AutoMapper دریافت کرده و سپس بجای ویژگیهای خاصیت معادل ViewModel درخواست شده، بازگشت دهیم. به این ترتیب ASP.NET MVC تصور خواهد کرد که ViewModel ما نیز دقیقا دارای همان ویژگیهای Model است.
public static class AutoMapperExtensions { public static IEnumerable<Attribute> GetMappedAttributes( this IConfigurationProvider mapper, Type viewModelType, string viewModelPropertyName, IList<Attribute> existingAttributes) { if (viewModelType != null) { foreach (var typeMap in mapper.GetAllTypeMaps().Where(i => i.DestinationType == viewModelType)) { var propertyMaps = typeMap.GetPropertyMaps() .Where(propertyMap => !propertyMap.IsIgnored() && propertyMap.SourceMember != null) .Where(propertyMap => propertyMap.DestinationProperty.Name == viewModelPropertyName); foreach (var propertyMap in propertyMaps) { foreach (Attribute attribute in propertyMap.SourceMember.GetCustomAttributes(true)) { if (existingAttributes.All(i => i.GetType() != attribute.GetType())) { yield return attribute; } } } } } if (existingAttributes == null) { yield break; } foreach (var attribute in existingAttributes) { yield return attribute; } } }
ثبت تامین کنندههای سفارشی سازی شده توسط AutoMapper
پس از تهیهی تامین کنندههای انتقال ویژگیها، اکنون نیاز است آنها را به ASP.NET MVC معرفی کنیم:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); Mappings.RegisterMappings(); ModelMetadataProviders.Current = new MappedMetadataProvider(Mapper.Engine.ConfigurationProvider); var modelValidatorProvider = ModelValidatorProviders.Providers .Single(provider => provider is DataAnnotationsModelValidatorProvider); ModelValidatorProviders.Providers.Remove(modelValidatorProvider); ModelValidatorProviders.Providers.Add(new MappedValidatorProvider(Mapper.Engine.ConfigurationProvider)); }
در قسمت کار با ModelValidatorProviders.Providers، ابتدا صرفا همان تامین کنندهی از نوع DataAnnotationsModelValidatorProvider پیش فرض، یافت شده و حذف میشود. سپس تامین کنندهی سفارشی سازی شدهی خود را معرفی میکنیم تا جایگزین آن شود.
مثالی جهت آزمایش انتقال خودکار ویژگیهای مدل به ViewModel
کنترلر مثال برنامه به شرح زیر است. در اینجا از متد Mapper.Map جهت تبدیل خودکار مدل کاربر به ViewModel آن استفاده شدهاست:
public class HomeController : Controller { public ActionResult Index() { var model = new UserModel { FirstName = "و", Id = 1, LastName = "ن" }; var viewModel = Mapper.Map<UserViewModel>(model); return View(viewModel); } [HttpPost] public ActionResult Index(UserViewModel data) { return View(data); } }
@model Sample12.ViewModels.UserViewModel @using (Html.BeginForm("Index", "Home", FormMethod.Post, htmlAttributes: new { @class = "form-horizontal", role = "form" })) { <div class="row"> <div class="form-group"> @Html.LabelFor(d => d.FirstName, htmlAttributes: new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(d => d.FirstName) @Html.ValidationMessageFor(d => d.FirstName) </div> </div> <div class="form-group"> @Html.LabelFor(d => d.LastName, htmlAttributes: new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(d => d.LastName) @Html.ValidationMessageFor(d => d.LastName) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="ارسال" class="btn btn-default" /> </div> </div> </div> }
در این شکل هر چند نوع مدل View مورد استفاده از ViewModel ایی تامین شدهاست که دارای هیچ ویژگی و Data Annotations/Attributes نیست، اما برچسب هر فیلد از ویژگی Display دریافت شدهاست. همچنین اعتبارسنجی سمت کاربر فعال بوده و برچسبهای آنها نیز به درستی دریافت شدهاند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
روی متدهای GetAllCategory و GetAllNews به صورت جداگانه کلیک کنید. متوجه خواهید شد که هرچند در کلاس tblNews شیای از نوع tblCategory و در کلاس tblCategory شیای از نوع مجموعهی tblNews به صورت Virtual تعریف شده است ولی در بر خلاف انتظارمان اثری از آن در اینجا دیده نمیشود. نتیجهی مشاهدهشده به خاطر است که در هر دو تعریف صفت DataMember را به ویژگیهای ناوبری اختصاص نداده ایم و این میتواند راهبرد ما در طراحی WCF باشد. ولی اگر میخواهید ویژگی ناوبری میان موجودیتها در متدهای ما هم دیده شود ادامهی این درس را بخوانید وگرنه ممکن است تصمیم داشته باشید در صورت نیاز به پیوند میان موجودیتها، متد جدیدی بنویسید و از دستورهای Linq استفاده کنید و یا برای اینکار از Stored Procedured بهره ببرید.
در اینجا من این سناریو را دنبال میکنم که در صورتی که متد GetAllNews اجرا شود؛ بدون اینکه نیاز باشد برای دانستن نام دستهی خبر از متد دیگری مانند GetAllCategory استفاده کنیم؛ رکورد وابسته موجودیت دسته در هر خبر نشان داده شود.
از Solution Explorer فایل MyNewsModel.tt را باز کنید و دنبال کد زیر بگردید:
public string NavigationProperty(NavigationProperty navigationProperty) { var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); }
سپس آنرا به صورت زیر ویرایش کنید:
public string NavigationProperty(NavigationProperty navigationProperty) { var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); return string.Format( CultureInfo.InvariantCulture, "{0}{1} {2} {3} {{ {4}get; {5}set; }}", navigationProperty.ToEndMember.RelationshipMultiplicity != RelationshipMultiplicity.Many ? "[DataMember]" + Environment.NewLine : "", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); }
پس از ذخیرهی فایل، خواهید دید که صفت DataMember در کلاس tblNews پیش از ویژگی tblCategory افزوده شده است. بار دیگر پروژه را اجرا کنید. روی متد GetAllNews کلیک کنید و روی دکمه Invoke بفشارید. خواهید دید که هرچند tblCategory در ویژگیهای آن قرار گرفته است ولی مقدار آن Null است. برای حل این مشکل باید از Solution Explorer فایل MyNewsService.cs را باز کنید و به به جای کد مربوط به متدهای GetAllNews و GetNews کدهای زیر را قرار دهید:
public List<tblNews> GetAllNews()
{
return dbMyNews.tblNews.Include(p=>p.tblCategory).Where(c=>c.IsDeleted == false).ToList();
}
public tblNews GetNews(int tblNewsId)
{
return dbMyNews.tblNews.Include(p => p.tblCategory).FirstOrDefault(p => p.tblNewsId == tblNewsId);
}
این بار اگر پروژه را اجرا کنید با نتیجهای مانند شکل زیر روبهرو خواهید شد:
در بخش هفتم پیرامون میزبانی WCF Library خواهم نوشت.
C# 7.1 - Tuple Name Inference
public void Swap() { int x = 5; int y = 50; (x, y) = (y, x); }
ASP.NET MVC به همراه HtmlHelper توکاری جهت نمایش یک ChekBoxList نیست؛ اما سیستم Model binder آن، این نوع کنترلها را به خوبی پشتیبانی میکند. برای مثال، یک پروژه جدید خالی ASP.NET MVC را آغاز کنید. سپس یک کنترلر Home جدید را نیز به آن اضافه کنید. در ادامه، برای متد Index آن، یک View خالی را ایجاد نمائید. سپس محتوای این View را به نحو زیر تغییر دهید:
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@using (Html.BeginForm())
{
<input type='checkbox' name='Result' value='value1' />
<input type='checkbox' name='Result' value='value2' />
<input type='checkbox' name='Result' value='value3' />
<input type="submit" value="submit" />
}
و کنترلر Home را نیز مطابق کدهای زیر ویرایش کنید:
using System.Web.Mvc;
namespace MvcApplication21.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string[] result)
{
return View();
}
}
}
یک breakpoint را در تابع Index دوم که آرایهای را دریافت میکند، قرار دهید. سپس برنامه را اجرا کرده، تعدادی از checkboxها را انتخاب و فرم نمایش داده شده را به سرور ارسال کنید:
بله. همانطور که ملاحظه میکنید، تمام عناصر ارسالی انتخاب شده که دارای نامی مشابه بودهاند، به یک آرایه قابل بایند هستند و سیستم model binder میداند که چگونه باید این اطلاعات را دریافت و پردازش کند.
از این مقدمه میتوان به عنوان پایه و اساس نوشتن یک HtmlHelper سفارشی CheckBoxList استفاده کرد.
برای این منظور یک پوشه جدید را به نام app_code، به ریشه پروژه اضافه نمائید. سپس یک فایل خالی را به نام Helpers.cshtml نیز به آن اضافه کنید. محتوای این فایل را به نحو زیر تغییر دهید:
@helper CheckBoxList(string name, List<System.Web.Mvc.SelectListItem> items)
{
<div class="checkboxList">
@foreach (var item in items)
{
@item.Text
<input type="checkbox" name="@name"
value="@item.Value"
@if (item.Selected) { <text>checked="checked"</text> }
/>
< br />
}
</div>
}
و برای استفاده از آن، کنترلر Home را مطابق کدهای زیر ویرایش کنید:
using System.Collections.Generic;
using System.Web.Mvc;
namespace MvcApplication21.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
ViewBag.Tags = new List<SelectListItem>
{
new SelectListItem { Text = "Item1", Value = "Val1", Selected = false },
new SelectListItem { Text = "Item2", Value = "Val2", Selected = false },
new SelectListItem { Text = "Item3", Value = "Val3", Selected = true }
};
return View();
}
[HttpPost]
public ActionResult GetTags(string[] tags)
{
return View();
}
[HttpPost]
public ActionResult Index(string[] result)
{
return View();
}
}
}
و در این حالت View برنامه به شکل زیر درخواهد آمد:
@{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
@using (Html.BeginForm())
{
<input type='checkbox' name='Result' value='value1' />
<input type='checkbox' name='Result' value='value2' />
<input type='checkbox' name='Result' value='value3' />
<input type="submit" value="submit" />
}
@using (Html.BeginForm(actionName: "GetTags", controllerName: "Home"))
{
@Helpers.CheckBoxList("Tags", (List<SelectListItem>)ViewBag.Tags)
<input type="submit" value="submit" />
}
با توجه به اینکه کدهای Razor قرار گرفته در پوشه خاص app_code در ریشه سایت، به صورت خودکار در حین اجرای برنامه کامپایل میشوند، متد Helpers.CheckBoxList در تمام Viewهای برنامه در دسترس خواهد بود. در این متد، یک نام و لیستی از SelectListItemها دریافت میگردد. سپس به صورت خودکار یک CheckboxList را تولید خواهد کرد. برای دریافت مقادیر ارسالی آن به سرور هم باید مطابق متد GetTags تعریف شده در کنترلر Home عمل کرد. در اینجا Value عناصر انتخابی به صورت آرایهای از رشتهها در دسترس خواهد بود.
روشی جامعتر
در آدرس زیر میتوانید یک HtmlHelper بسیار جامع را جهت تولید CheckBoxList در ASP.NET MVC بیابید. در همان صفحه روش استفاده از آن، به همراه چندین مثال ارائه شده است:
https://github.com/devnoob/MVC3-Html.CheckBoxList-custom-extension
مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت ششم - تکمیل مستندات محافظت از API
private readonly SignInKeysOption _signInKeyOptions; private string failReason; public CredentialAuthenticationHandler( IOptionsMonitor < CustomAuthenticationOptions > options, IOptionsMonitor < SignInKeysOption > signInKeyOptions, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock): base(options, logger, encoder, clock) { _signInKeyOptions = signInKeyOptions.CurrentValue ?? throw new ArgumentNullException(nameof(signInKeyOptions)); } protected override Task HandleChallengeAsync(AuthenticationProperties properties) { Response.StatusCode = 401; if (failReason != null) { Response.HttpContext.Features.Get < IHttpResponseFeature > () !.ReasonPhrase = failReason; } return Task.CompletedTask; } protected async override Task < AuthenticateResult > HandleAuthenticateAsync() { string authorizationHeader = Request.Headers["Authorization"]; if (authorizationHeader == null) { Logger.LogWarning("Authorization key in header is null or empty"); //string result; //Context.Response.StatusCode = StatusCodes.Status401Unauthorized; //result = JsonConvert.SerializeObject(new { error = "Authorization key in header is null or empty" }); //Context.Response.ContentType = "application/json"; //await Context.Response.WriteAsync(result); failReason = "Authorization key in header is null or empty"; return AuthenticateResult.Fail("request doesn't contains header"); //return await Task.FromResult(AuthenticateResult.Fail("UnAuthenticate")); } }
.jqGrid('navButtonAdd', '#pager', { caption: "تنظیم نمایش ستونها", title: "Reorder Columns", onClickButton: function () { jQuery("#list").jqGrid('columnChooser', { done: function (perm) { if (perm) { $("#list").jqGrid("remapColumns", perm, true, false); } var colModel = $("#list").jqGrid('getGridParam', 'colModel'); var hiddenColumns = new Array(); for (var i = 0; i < colModel.length; i++) { if (colModel[i].hidden) { hiddenColumns.push(colModel[i].name); } } $.ajax({ type: "POST", url: "@Url.Action("HiddenColumns","Home")", dataType: "json", traditional: true, data: { hiddenColumns: hiddenColumns } }); } }); } })
var colModel = $("#list").jqGrid('getGridParam', 'colModel');
public ActionResult HiddenColumns(string[] hiddenColumns) { //todo: save it in the DB or cookies or session .... return Content("OK"); }
using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace MyNewsWCFLibrary { [ServiceContract] interface IMyNewsService { [OperationContract] List<tblNews> GetAllNews(); [OperationContract] tblNews GetNews(int tblNewsId); [OperationContract] int AddNews(tblNews News); [OperationContract] bool EditNews(tblNews News); [OperationContract] bool DeleteNews(int tblNewsId); [OperationContract] int AddCategory(tblCategory News); [OperationContract] bool DeleteCategory(int tblCategoryId); [OperationContract] List<tblCategory> GetAllCategory(); } }
کلاسی به نام MyNewsService با ارثبری از IMyNewsService ایجاد میشود. زیر حرف I از IMyNewsService یک خط دیده میشود که با کلیک روی آن برابر با شکل زیر عمل کنید:
ملاحظه خواهید کرد که کلیهی متدها برابر با Interface ساخته خواهد شد. اکنون همانند شکل روی نشان هرم شکلی که هنگامی که روی نام کلاس کلیک میکنید، در سمت چپ نشان داده میشود کلیک کنید و گزینه Move to another file to match type name را انتخاب کنید:
به صورت خودکار محتوای این کلاس به یک فایل دیگر انتقال مییابد. اکنون هر کدام از متدها را به شکل دلخواه ویرایش میکنیم. من کد کلاس را اینگونه تغییر دادم:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; namespace MyNewsWCFLibrary { class MyNewsService : IMyNewsService { private dbMyNewsEntities dbMyNews = new dbMyNewsEntities(); public List<tblNews> GetAllNews() { return dbMyNews.tblNews.Where(p => p.IsDeleted == false).ToList(); } public tblNews GetNews(int tblNewsId) { return dbMyNews.tblNews.FirstOrDefault(p => p.tblNewsId == tblNewsId); } public int AddNews(tblNews News) { dbMyNews.tblNews.Add(News); dbMyNews.SaveChanges(); return News.tblNewsId; } public bool EditNews(tblNews News) { try { dbMyNews.Entry(News).State = EntityState.Modified; dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public bool DeleteNews(int tblNewsId) { try { tblNews News = dbMyNews.tblNews.FirstOrDefault(p => p.tblNewsId == tblNewsId); News.IsDeleted = true; dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public int AddCategory(tblCategory Category) { dbMyNews.tblCategory.Add(Category); dbMyNews.SaveChanges(); return Category.tblCategoryId; } public bool DeleteCategory(int tblCategoryId) { try { tblCategory Category = dbMyNews.tblCategory.FirstOrDefault(p => p.tblCategoryId == tblCategoryId); Category.IsDeleted = true; dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public List<tblCategory> GetAllCategory() { return dbMyNews.tblCategory.Where(p => p.IsDeleted == false).ToList(); } } }
ولی شما ممکن است دربارهی حذف، دوست داشته باشید رکوردها از پایگاه داده حذف شوند و نه اینکه با یک فیلد بولی آنها را مدیریت کنید. در این صورت کد شما میتواند اینگونه نوشته شود:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; namespace MyNewsWCFLibrary { class MyNewsService : IMyNewsService { private dbMyNewsEntities dbMyNews = new dbMyNewsEntities(); public List<tblNews> GetAllNews() { return dbMyNews.tblNews.ToList(); } public tblNews GetNews(int tblNewsId) { return dbMyNews.tblNews.FirstOrDefault(p => p.tblNewsId == tblNewsId); } public int AddNews(tblNews News) { dbMyNews.tblNews.Add(News); dbMyNews.SaveChanges(); return News.tblNewsId; } public bool EditNews(tblNews News) { try { dbMyNews.Entry(News).State = EntityState.Modified; dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public bool DeleteNews(tblNews News) { try { dbMyNews.tblNews.Remove(News); dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public int AddCategory(tblCategory Category) { dbMyNews.tblCategory.Add(Category); dbMyNews.SaveChanges(); return Category.tblCategoryId; } public bool DeleteCategory(tblCategory Category) { try { dbMyNews.tblCategory.Remove(Category); dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } } public List<tblCategory> GetAllCategory() { return dbMyNews.tblCategory.ToList(); } } }
البته باید در نظر داشته باشید که در صورت هر گونه تغییر در پارامترهای ورودی، لایهی Interface نیز باید تغییر کند. گونهی دیگر نوشتن متد حذف خبر میتواند به صورت زیر باشد:
public bool DeleteNews(int tblNewsId) { try { tblNews News = dbMyNews.tblNews.FirstOrDefault(p => p.tblNewsId == tblNewsId); dbMyNews.tblNews.Remove(News); dbMyNews.SaveChanges(); return true; } catch (Exception exp) { return false; } }
در بخش 5 دربارهی تغییرات App.Config خواهم نوشت.