نظرات مطالب
خلاصه‌ای از LINQ to XML
برای تکمیل بحث با اجازه آقای نصیری این لینک هم من اضافه می کنم که قابلیت های خارق العاده LINQ رو برای کار با XML نشون می ده :

http://windowsclient.net/learn/video.aspx?v=6895

موفق باشید.
مسیرراه‌ها
WPF
          نظرات مطالب
          نحوه ارتقاء برنامه‌های موجود MVC3 به MVC4
          من از این کلاس شما در mvc 4 استفاده کردم اوکی بودش اما الان در 5 دارم استفاده میکنم تمام فایل ها 
          این شکلی میشن.
          debug هم false کردم
           <script src="/Asset/Scripts/js?v="></script>
           <script src="/Asset/Scripts/JQuery/Zebra/js?v="></script>
           <script src="/Asset/Scripts/Angular/Asset/js?v="></script>
          مشکل از کجا میتونه باشه ؟ ممنون 
          مطالب
          Blazor 5x - قسمت 28 - برنامه‌ی Blazor WASM - نمایش لیست اطلاعات دریافتی از Web API
          در قسمت قبل، سرویس و کامپوننت دریافت اطلاعات اتاق‌ها را از Web API برنامه، تکمیل کردیم. در این قسمت با استفاده از اطلاعات مهیا شده، UI آن‌را نیز تکمیل خواهیم کرد.


          نمایش منتظر بمانید در حین بارگذاری اولیه‌ی کامپوننت

          کامپوننت‌هایی که قرار است اطلاعات را از یک Web API دریافت کنند، مدتی باید منتظر بمانند تا عملیات رفت و برگشت به سرور، تکمیل شود. در این بین می‌توان یک loading را به کاربر نمایش داد:
          @page "/hotel/rooms"
          
          @if (Rooms is not null && Rooms.Any())
          {
          
          }
          else
          {
              <div style="position:fixed;top:50%;left:50%;margin-top:-50px;margin-left:-100px;">
                  <img src="images/loader.gif" />
              </div>
          }
          
          @code {
              IEnumerable<HotelRoomDTO> Rooms = new List<HotelRoomDTO>();
              // ... 
          }
          - فیلد Rooms را در قسمت قبل، در متد LoadRooms، از Web API دریافت و مقدار دهی کردیم. تا زمان تکمیل عملیات این متد، فیلد Rooms، فاقد عضوی است؛ بنابراین قسمت else شرط فوق اجرا می‌شود که یک loading را نمایش خواهد داد. مابقی UI برنامه در قسمت if آن قرار می‌گیرد.
          - هر زمانیکه کار روال رویدادگردان OnInitializedAsync به پایان برسد (که شامل اجرای متد LoadRooms نیز هست)، سبب فراخوانی خودکار StateHasChanged می‌شود. این فراخوانی، UI را مجددا رندر می‌کند. به همین جهت است که پس از پایان کار، محتوای if، رندر خواهد شد.
          - از این loading سفارشی که در میانه‌ی صفحه نمایش داده می‌شود، می‌توان در فایل wwwroot\index.html نیز بجای loading پیش‌فرض آن استفاده کرد:
            <body>
              <div id="app">
                <div
                  style="
                    position: fixed;
                    top: 50%;
                    left: 50%;
                    margin-top: -50px;
                    margin-left: -100px;
                  "
                >
                  <img src="images/ajax-loader.gif" />
                </div>
              </div>

          افزودن خواصی جدید به HotelRoomDTO

          می‌خواهیم به کاربر امکان تغییر تعداد روزهای اقامت را بدهیم. این انتخاب باید در لیست اتاق‌های نمایش داده شده، با تغییر تعداد روزهای اقامت (TotalDays) و هزینه‌ی جدید متناظر با آن (TotalAmount)، منعکس شود. به همین جهت این خواص را به HotelRoomDTO، اضافه می‌کنیم:
          namespace BlazorServer.Models
          {
              public class HotelRoomDTO
              {
                  // ...
          
                  public int TotalDays { get; set; }
                  public decimal TotalAmount { get; set; }
              }
          }
          محاسبات مربوط به این خواص را هم می‌توان در همان کامپوننت HotelRooms.razor، پس از بارگذاری لیست اتاق‌ها از Web API، انجام داد:
          @code
          {
               HomeVM HomeModel = new HomeVM();
              // ...
          
              private async Task LoadRoomsAsync()
              {
                  Rooms = await HotelRoomService.GetHotelRoomsAsync(HomeModel.StartDate, HomeModel.EndDate);
                  foreach (var room in Rooms)
                  {
                      room.TotalAmount = room.RegularRate * HomeModel.NoOfNights;
                      room.TotalDays = HomeModel.NoOfNights;
                  }
              }
          }


          افزودن امکان تغییر تعداد روزهای اقامت در همان صفحه‌ی نمایش لیست اتاق‌ها


          همانطور که در تصویر فوق هم مشاهده می‌کنید، می‌خواهیم در این صفحه نیز کاربر بتواند زمان شروع اقامت و مدت مدنظر را تغییر دهد. به همین جهت، HomeModel ای را که در قسمت قبل از Local Storage دریافت کردیم، به فرم زیر متصل می‌کنیم تا اجزای آن در این فرم، نمایش داده شده و قابل تغییر شوند:
          @if (Rooms is not null && Rooms.Any())
          {
              <EditForm Model="HomeModel" OnValidSubmit="SaveBookingInfo" class="bg-light">
                  <div class="pt-3 pb-2 px-5 mx-1 mx-md-0 bg-secondary">
                      <DataAnnotationsValidator />
                      <div class="row px-3 mx-3">
                          <div class="col-6 col-md-4">
                              <div class="form-group">
                                  <label class="text-warning">Check in Date</label>
                                  <InputDate @bind-Value="HomeModel.StartDate" class="form-control" />
                              </div>
                          </div>
                          <div class="col-6 col-md-4">
                              <div class="form-group">
                                  <label class="text-warning">Check Out Date</label>
                                  <input @bind="HomeModel.EndDate" disabled="disabled"
                                      readonly="readonly" type="date" class="form-control" />
                              </div>
                          </div>
                          <div class=" col-4 col-md-2">
                              <div class="form-group">
                                  <label class="text-warning">No. of nights</label>
                                  <select class="form-control" @bind="HomeModel.NoOfNights">
                                      <option value="Select" selected disabled="disabled">(Select No. Of Nights)</option>
                                      @for (var i = 1; i <= 10; i++)
                                      {
                                          <option value="@i">@i</option>
                                      }
                                  </select>
                              </div>
                          </div>
          
                          <div class="col-8 col-md-2">
                              <div class="form-group" style="margin-top: 1.9rem !important;">
                                  @if (IsProcessing)
                                  {
                                      <button class="btn btn-success btn-block form-control">
                                          <i class="fa fa-spin fa-spinner"></i>Processing...
                                      </button>
                                  }
                                  else
                                  {
                                      <input type="submit" value="Update" class="btn btn-success btn-block form-control" />
                                  }
                              </div>
                          </div>
                      </div>
                  </div>
              </EditForm>
          نکته‌ی مهم این فرم، مدیریت قسمت کلیک بر روی دکمه‌ی Update است که سبب فراخوانی روال رویدادگران OnValidSubmit می‌شود:
          @code {
              bool IsProcessing;
          
              // ...
          
              private async Task SaveBookingInfo()
              {
                  IsProcessing = true;
                  HomeModel.EndDate = HomeModel.StartDate.AddDays(HomeModel.NoOfNights);
                  await LocalStorage.SetItemAsync(ConstantKeys.LocalInitialBooking, HomeModel);
                  await LoadRoomsAsync();
                  IsProcessing = false;
              }
          }
          در ابتدای عملیات، فیلد جدید IsProcessing را به true تنظیم می‌کنیم. این مورد سبب می‌شود تا برچسب دکمه‌ی Update به Processing... تغییر کند. سپس فیلد محاسباتی EndDate را بر اساس اطلاعات جدید فرم، به روز رسانی می‌کنیم. در ادامه، مجددا این اطلاعات را در Local Storage ذخیره سازی کرده و کار LoadRoomsAsync را انجام می‌دهیم که به همراه آن، خواص جدید تعداد روزها و هزینه‌ی اقامت نیز مجددا محاسبه می‌شوند. در آخر برچسب دکمه‌ی Update را به حالت اول باز می‌گردانیم.

          سؤال: زمانیکه IsProcessing به true تنظیم می‌شود که هنوز کار متد رویدادگردان SaveBookingInfo به پایان نرسیده‌است و فراخوانی خودکار StateHasChanged در پایان متدهای رویدادگردان صورت می‌گیرد. پس چطور است که سبب رندر مجدد UI و تغییر برچسب دکمه‌ی Update می‌شود؟
          پاسخ به این سؤال را در قسمت 6 این سری با بررسی چرخه‌ی حیات کامپوننت‌ها، مشاهده کردیم:
          «البته متدهای رویدادگردان async، دوبار سبب فراخوانی ضمنی StateHasChanged می‌شوند؛ یکبار زمانیکه قسمت sync متد به پایان می‌رسد (در این مثال یعنی تا قبل از اولین await نوشته شده) و یکبار هم زمانیکه کار فراخوانی کلی متد به پایان خواهد رسید»


          نمایش لیست اتاق‌ها


          نمایش لیست اتاق‌ها مطابق تصویر فوق، دو قسمت اصلی را دارد:
          الف) نمایش لیست تصاویر منتسب به یک اتاق، توسط کامپوننت carousel بوت استرپ
          @foreach (var room in Rooms)
          {
                      <div class="row p-2 my-3 " style="border-radius:20px; border: 1px solid gray">
                          <div class="col-12 col-lg-3 col-md-4">
                              <div id="carouselExampleIndicators_@room.Id"
                                  class="carousel slide mb-4 m-md-3 m-0 pt-3 pt-md-0"
                                  data-ride="carousel">
                                  <ol class="carousel-indicators">
                                      @{
                                          int imageIndex = 0;
                                          int innerImageIndex = 0;
                                      }
                                      @foreach (var image in room.HotelRoomImages)
                                      {
                                          if (imageIndex == 0)
                                          {
                                              <li data-target="#carouselExampleIndicators_@room.Id"
                                                  data-slide-to="@imageIndex" class="active"></li>
          
                                          }
                                          else
                                          {
                                              <li data-target="#carouselExampleIndicators_@room.Id"
                                                  data-slide-to="@imageIndex"></li>
                                          }
                                          imageIndex++;
                                      }
                                  </ol>
                                  <div class="carousel-inner">
                                      @foreach (var image in room.HotelRoomImages)
                                      {
                                          var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
                                          if (innerImageIndex == 0)
                                          {
                                              <div class="carousel-item active">
                                                  <img class="d-block w-100" style="border-radius:20px;"
                                                      src="@imageUrl" alt="First slide">
                                              </div>
                                          }
                                          else
                                          {
                                              <div class="carousel-item">
                                                  <img class="d-block w-100" style="border-radius:20px;"
                                                      src="@imageUrl" alt="First slide">
                                              </div>
                                          }
          
                                          innerImageIndex++;
                                      }
                                  </div>
                                  <a class="carousel-control-prev" href="#carouselExampleIndicators_@room.Id"
                                      role="button" data-slide="prev">
                                      <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                                      <span class="sr-only">Previous</span>
                                  </a>
                                  <a class="carousel-control-next" href="#carouselExampleIndicators_@room.Id"
                                      role="button" data-slide="next">
                                      <span class="carousel-control-next-icon" aria-hidden="true"></span>
                                      <span class="sr-only">Next</span>
                                  </a>
                              </div>
                          </div>
          }
          - هرچند این قطعه کد، طولانی به نظر می‌رسد اما قسمت‌های مختلف آن صرفا بر اساس مستندات سایت بوت استرپ، جهت تشکیل ساختار ابتدایی و استاندارد کامپوننت carousel، تهیه شده‌اند.
          - سپس در حلقه‌ای که برای نمایش لیست اتاق‌ها تهیه کرده‌ایم، قسمت‌های مختلف carousel را تکمیل می‌کنیم که در اینجا نیاز به ایندکس تصاویر، لیست تصاویر و یک Id منحصربفرد برای این carousel خاص را دارد تا بتوان چندین وهله از آن‌را در صفحه قرار داد که این id را بر اساس Id اتاق مشخص کرد‌ه‌ایم.

          دو نکته:
          - در این مثال برای تعریف لینک به تصاویر، کد زیر را مشاهده می‌کنید:
          var imageUrl = $"{ImagesBaseAddress}/{image.RoomImageUrl}";
          و این ImagesBaseAddress، به صورت زیر تعریف شده که همان آدرس برنامه‌ی blazor server ای است که مشخصات اتاق‌ها و تصاویر را ثبت می‌کند:
          @code {
             string ImagesBaseAddress = "https://localhost:5006";
          بنابراین اگر می‌خواهید تصاویر را هم مشاهده کنید، باید برنامه‌ی مجزای blazor server این سری نیز در حال اجرا باشد.
          - کامپوننت carousel برای اجرا، نیاز به فایل lib/bootstrap/dist/js/bootstrap.bundle.min.js را نیز دارد. به همین جهت مدخل اسکریپت آن‌را باید به فایل wwwroot\index.html اضافه کرد.

          ب) نمایش جزئیات نام و هزینه‌ی اتاق
          قسمت دوم حلقه‌ی foreach نمایش لیست اتاق‌ها، جهت نمایش جزئیات هر اتاق تعریف شده‌است:
          @foreach (var room in Rooms)
          {
                          <div class="col-12 col-lg-9 col-md-8">
                              <div class="row pt-3">
                                  <div class="col-12 col-lg-8">
                                      <p class="card-title text-warning" style="font-size:xx-large">@room.Name</p>
                                      <p class="card-text">
                                          @((MarkupString)room.Details)
                                      </p>
                                  </div>
                                  <div class="col-12 col-lg-4">
                                      <div class="row pb-3 pt-2">
                                          <div class="col-12 col-lg-11 offset-lg-1">
                                              <a href="@($"hotel/room-details/{room.Id}")" class="btn btn-success btn-block">Book</a>
                                          </div>
                                      </div>
                                      <div class="row ">
                                          <div class="col-12 pb-5">
                                              <span class="float-right">
                                                  <span class="float-right">Occupancy : @room.Occupancy adults </span><br />
                                                  <span class="float-right pt-1">Room Size : @room.SqFt sqft</span><br />
                                                  <h4 class="text-warning font-weight-bold pt-4">
                                                      <span style="border-bottom:1px solid #ff6a00">
                                                          @room.TotalAmount.ToString("#,#.00;(#,#.00#)")
                                                      </span>
                                                  </h4>
                                                  <span class="float-right">Cost for  @room.TotalDays nights</span>
                                              </span>
                                          </div>
                                      </div>
                                  </div>
                              </div>
                          </div>
                      </div>
          }
          - در این مثال از MarkupString استفاده شده تا بتوان یک محتوای HTML ای را در صفحه نمایش داد.
          - هر اتاق نمایش داده شده، لینکی را به صفحه‌ی خاص خودش نیز دارد که آن‌را در قسمت بعدی تکمیل می‌کنیم.
          - در اینجا TotalAmount و TotalDays محاسباتی و قابل تغییر بر اساس انتخاب کاربر نیز درج شده‌اند.


          یک تمرین: در برنامه‌ی Blazor Server، سرویسی را جهت درج مشخصات امکانات رفاهی هتل تهیه کردیم. این امکانات رفاهی را از طریق Web API برنامه دریافت و سپس در برنامه‌ی سمت کلاینت نمایش دهید.
          بنابراین تکمیل این تمرین شامل تهیه‌ی موارد زیر است که کدنویسی آن، با دو قسمت اخیر این سری دقیقا یکی است و نکته‌ی جدیدی را به همراه ندارد (و کدهای کامل آن را از انتهای بحث می‌توانید دریافت کنید):
          - تهیه‌ی HotelAmenityController در پروژه‌ی Web API که به کمک IAmenityService، لیست امکانات رفاهی را بازگشت می‌دهد.
          - تهیه‌ی ‍ClientHotelAmenityService در پروژه‌ی WASM که همانند ClientHotelRoomService قسمت قبل ، از Web API، لیست HotelAmenityDTO‌ها را دریافت می‌کند.
          - ثبت سرویس جدید ‍ClientHotelAmenityService در Program.cs.
          - در آخر حلقه‌ای را بر روی لیست HotelAmenityDTO دریافتی از ClientHotelRoomService در کامپوننت Index.razor تشکیل داده و آن‌ها را نمایش می‌دهیم.


          کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-28.zip
          مطالب
          آشنایی با سورس AndroidBreadCrumb

          زمانی که سیستم عامل های GUI مثل ویندوز به بازار آمدند، یکی از قسمت‌های گرافیکی آن‌ها AddressBar   نام داشت که مسیر حرکت آن‌ها را در فایل سیستم نشان میداد و در سیستم عامل‌های متنی  CLI با دستور  cd یا pwd انجام می‌شد. بعدها در وب هم همین حرکت با نام BreadCrumb صورت گرفت که به عنوان مثال مسیر رسیدن به صفحه‌ی یک محصول یا یک مقاله را نشان می‌داد. در یک پروژه‌ی اندرویدی نیاز بود تا یک ساختار درختی را پیاده سازی کنم، ولی در برنامه‌های اندروید ایجاد یک درخت، کار هوشمندانه و مطلوبی نیست و روش کار به این صورت است که یک لیست از گروه‌های والد را نمایش داده و با انتخاب هر آیتم لیست به آیتم‌های فرزند تغییر میکند. حالا مسئله این بود که کاربر باید مسیر حرکت خودش را بشناسد. به همین علت مجبور شدم یک BreadCrumb را برای آن طراحی کنم که در زیر تصویر آن را مشاهده می‌کنید.


           از نکات جالب توجه در مورد این ماژول می‌توان گفت که قابلیت این را دارد تا تصمیمات خود را بر اساس اندازه‌های مختلف صفحه نمایش بگیرد. به عنوان مثال اگر آیتم‌های بالا بیشتر از سه عدد باشد و در صفحه جا نشود از یک مسیر جعلی استفاده می‌کند و همه‌ی آیتم‌ها با اندیس شماره 1 تا index-3 را درون یک آیتم با عنوان (...) قرار می‌دهد که من به آن می‌گویم مسیر جعلی. به عنوان نمونه مسیر تصویر بالا در صفحه جا شده است و نیازی به این کار دیده نشده است. ولی تصویر زیر از آن جا که مسیر، طول width صفحه نمایش رد کرده است، نیاز است تا چنین کاری انجام شود. موقعی‌که کاربر آیتم ... را کلیک کند، مسیر باز شده و به محل index-3 حرکت می‌کند. یعنی دو مرحله به عقب باز می‌گردد.


          نگاهی به کارکرد ماژول 

          قبل از توضیح در مورد سورس، اجازه دهید نحوه‌ی استفاده از آن را ببینیم.

          این سورس شامل دو کلاس است که ساده‌ترین کلاس آن AndBreadCrumbItem می‌باشد که مشابه کلاس ListItem در بخش وب دات نت است و دو مقدار، یکی متن و دیگری Id را می‌گیرد:

          سورس:

          public class AndBreadCrumbItem {
          
              private int Id;
              private String diplayText;
          
              public AndBreadCrumbItem(int Id, String displayText)
              {
                  this.Id=Id;
                  this.diplayText=displayText;
              }
              public String getDiplayText() {
                  return diplayText;
              }
              public void setDiplayText(String diplayText) {
                  this.diplayText = diplayText;
              }
              public int getId() {
                  return Id;
              }
              public void setId(int id) {
                  Id = id;
              }
          }

          به عنوان مثال می‌خواهیم یک breadcrumb را با مشخصات زیر بسازیم:

          AndBreadCrumbItem itemhome=new AndBreadCrumbItem(0,"Home");
          AndBreadCrumbItem itemproducts=new AndBreadCrumbItem(12,"Products");
           AndBreadCrumbItem itemdigital=new AndBreadCrumbItem(15,"Digital");
          AndBreadCrumbItem itemhdd=new AndBreadCrumbItem(56,"Hard Disk Drive");
          حال از کلاس اصلی یعنی AndBreadCrumb استفاده می‌کنیم و آیتم‌ها را به آن اضافه می‌کنیم:
          AndBreadCrumb breadCrumb=new AndBreadCrumb(this);
          
                  breadCrumb.AddNewItem(itemhome);
                  breadCrumb.AddNewItem(itemproducts);
                  breadCrumb.AddNewItem(itemdigital);
                  breadCrumb.AddNewItem(itemhdd);
          به این نکته دقت داشته باشید که با هر شروع مجدد چرخه‌ی Activity، حتما شیء Context این کلاس را به روز نمایید تا در رسم المان‌ها به مشکل برنخورد. می‌توانید از طریق متد زیر context را مقداردهی نمایید:
          breadCumb.setContext(this);
          هر چند راه حل پیشنهادی این است که این کلاس را نگهداری ننماید و از یک لیست ایستا جهت نگهداری AndBreadCrumbItem‌ها استفاده کنید تا باهر بار  فراخوانی رویدادهای اولیه چون oncreate یا onstart و.. شی BreadCrumb را پر نمایید.

          پس از افزودن آیتم ها، تنظیمات زیر را اعمال نمایید:

                  LinearLayout layout=(LinearLayout)getActivity().findViewById(R.id.breadcumblayout);
                  layout.setPadding(8, 8, 8, 8);
                  breadCrumb.setLayout(layout);
                  breadCrumb.SetTinyNextNodeImage(R.drawable.arrow);
                  breadCrumb.setTextSize(25);
                  breadCrumb.SetViewStyleId(R.drawable.list_item_style);
          در سه خط اول، یک layout  از نوع Linear جهت رسم اشیاء به شیء breadcrumb معرفی می‌شود. سپس در صورت تمایل می‌توانید از یک شیء تصویر گرافیکی کوچک هم استفاده کنید که در تصاویر بالا می‌بینید از تصویر یک فلش جهت دار استفاده شده است تا بین هر المان ایجاد شده از آیتم‌ها قرار بگیرد. سپس در صورت تمایل اندازه‌ی قلم متون را مشخص می‌کنید و در آخر هم متد SetViewStyleId هم برای نسبت دادن یک استایل یا selector و ... استفاده می‌شود.
          حال برای رسم آن متد UpdatePath را صدا می‌زنیم:
                  breadCrumb.UpdatePath();

          الان اگر برنامه اجرا شود باید breadcrumb از چپ به راست رسم گردد. برای استفاده‌های فارسی، راست به چپ می‌توانید از متد زیر استفاده کنید:
          breadCrumb.setRTL(true);
          در صورت هر گونه تغییری در تنظیمات، مجددا متد UpdatePath را فراخوانی کنید تا عملیات رسم، با تنظمیات جدید آغاز گردد.

          در صورتیکه قصد دارید تنظیمات بیشتری چون رنگ متن، فونت متن و ... را روی هر المان اعمال کنید، از رویداد زیر استفاده کنید:

          breadCrumb.setOnTextViewUpdate(new ITextViewUpdate() {
                      @Override
                      public TextView UpdateTextView(Context context, TextView tv) {
                          tv.setTextColor(...);
                          tv.setTypeface(...);
                          return tv;
                      }
                  });
          با هر بار ایجاد المان که از نوع TextView است، این رویداد فراخوانی شده و تنظیمات شما را روی آن اجرا می‌کند.
          همچنین در صورتیکه می‌خواهید بدانید کاربر بر روی چه عنصری کلیک کرده است، از رویداد زیر استفاده کنید:
          breadCumb.setOnClickListener(new IClickListener() {
                      @Override
                      public void onClick(int position, int Id) {
                            //...
                      }
                  });
          کد بالا دو آرگومان را ارسال میکند که اولی position یا اندیس مکانی عنصر کلیک شده را بر می‌گرداند و دومی id هست که با استفاده ازکلاس AndBreadCrumbItem به آن پاس کرده‌اید. هنگام کلیک کاربر روی عنصر مورد نظر، برگشت به عقب به طور خودکار صورت گرفته و عناصر بعد از آن موقعیت، به طور خودکار حذف خواهند شد.

          آخرین متد موجود که کمترین استفاده را دارد، متد SetNoResize است. در صورتیکه این متد با True مقداردهی گردد، عملیات تنظیم بر اساس صفحه‌ی نمایش لغو می‌شود. این متد برای زمانی مناسب است که به عنوان مثال شما از یک HorozinalScrollView استفاده کرده باشید. در این حالت layout شما هیچ گاه به پایان نمی‌رسد و بهتر هست عملیات اضافه را لغو کنید.

          نگاهی به سورس

            کلاس زیر شامل بخش‌های زیر است:
          فیلدهای خصوصی
           //=-=--=-=-=-=-=-=-=-=-=-=-=- Private Properties -=-=-=-=-=-=-=--=-=-=
              private List<AndBreadCrumbItem> items=null;
              private List<TextView> textViews;
              private int tinyNextNodeImage;
              private int viewStyleId;
              private Context context;
              private boolean RTL;
              private float textSize=20;
              private boolean noResize=false;
          
              LinearLayout layout;
              IClickListener clickListener;
              ITextViewUpdate textViewUpdate;
              LinearLayout.LayoutParams params ;

          با نگاهی به نام آن‌ها میتوان حدس زد که برای چه کاری استفاده می‌شوند. به عنوان نمونه از اصلی‌ترین‌ها، متغیر items جهت نگهداری آیتم‌های پاس شده استفاده می‌شود و textviews هم برای نگهداری هر breadcrumb یا همان المان TextView که روی صفحه رسم می‌شود.
          اینترفیس‌ها هم با حرف I شروع و برای تعریف رویدادها ایجاد شده‌اند. در ادامه از تعدادی متد get و Set برای مقدار دهی بعضی از فیلدهای خصوصی بالا استفاده شده است:
              //=-=---=-=-=-=-- Constructor =--=-=-=-=-=--=-=-
          
              public AndBreadCrumb(Context context)
              {
                  this.context=context;
                  params = new LinearLayout.LayoutParams
                          (LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
              }
          
              //=-=-=--=--=-=-=-=-=-=-=-=-  Public Properties --=-=-=-=-=-=--=-=-=-=-=-=-
          
              //each category would be added to create path
              public void AddNewItem(AndBreadCrumbItem item)
              {
                  if(items==null)
                      items=new ArrayList<>();
                  items.add(item);
              }
          
              // if you want a pointer or next node between categories or textviews
              public void SetTinyNextNodeImage(int resId) {this.tinyNextNodeImage=resId;}
          
              public void SetViewStyleId(int resId) {this.viewStyleId=resId;}
          
              public void setTextSize(float textSize) {this.textSize = textSize;}
          
              public boolean isRTL() {
                  return RTL;
              }
          
              public void setRTL(boolean RTL) {
                  this.RTL = RTL;
              }
          
              public void setLayout(LinearLayout layout) {
          
                  this.layout = layout;
              }
          
              public void setContext(Context context) {
                  this.context = context;
              }
          
              public boolean isNoResize() {
                  return noResize;
              }
          
              public void setNoResize(boolean noResize) {
                  this.noResize = noResize;
              }

          بعد از آن به متدهای خصوصی می‌رسیم که متد زیر، متد اصلی ما برای ساخت breadcrumb است:
           //primary method for render objects on layout
              private void DrawPath() {
          
          
                  //stop here if essentail elements aren't present
                  if (items == null) return ;
                  if (layout == null) return;
                  if (items.size() == 0) return;
          
          
          //we need to get size of layout,so we use the post method to run this thread when ui is ready
                  layout.post(new Runnable() {
                      @Override
                      public void run() {
          
          
                          //textviews created here one by one
                          int position = 0;
                          textViews = new ArrayList<>();
                          for (AndBreadCrumbItem item : items) {
                              TextView tv = MakeTextView(position, item.getId());
                              tv.setText(item.getDiplayText());
                              textViews.add(tv);
                              position++;
                          }
          
          
                          //add textviews on layout
                          AddTextViewsOnLayout();
          
                          //we dont manage resizing anymore
                          if(isNoResize()) return;
          
                          //run this code after textviews Added to get widths of them
                          TextView last_tv=textViews.get(textViews.size()-1);
                          last_tv.post(new Runnable() {
                              @Override
                              public void run() {
                                  //define width of each textview depend on screen width
                                  BatchSizeOperation();
                              }
                          });
          
                      }
                  });
          
          
              }
          متد DrawPath برای ترسیم breadcumb است و می‌توان گفت اصلی‌ترین متد این کلاس است. در سه خط اول، عناصر الزامی را که باید مقداردهی شده باشند، بررسی می‌کند. این موارد وجود آیتم‌ها و layout است. اگر هیچ یک از اینها مقدار دهی نشده باشند، عملیات رسم خاتمه می‌یابد. بعد از آن یک پروسه‌ی UI جدید را در متد post شیء Layout معرفی می‌کنیم. این متد زمانی این پروسه را صدا می‌زند که layout در UI برنامه جا گرفته باشد. دلیل اینکار این است که تا زمانی که ویوها در UI تنظیم نشوند، نمی‌توانند اطلاعاتی چون پهنا و ارتفاع را برگردانند و همیشه مقدار 0 را باز می‌گردانند. پس ما بامتد post اعلام می‌کنیم زمانی این پروسه را اجرا کن که وضعیت UI خود را مشخص کرده‌ای.
          به عنوان نمونه کد زیر را ببینید:
          TextView tv=new TextView(this);
          tv.getWidth(); //return 0
          layout.add(tv);
          tv.getWidth(); //return 0
          در این حالت کنترل در هر صورتی عدد ۰ را به شما باز می‌گرداند و نمی‌توانید اندازه‌ی آن را بگیرید مگر اینکه درخواست یک callback بعد از رسم را داشته باشید که این کار از طریق متد post انجام می‌گیرد:
          TextView tv=new TextView(this);
          tv.post(new Runnable() {
                              @Override
                              public void run() {
                                  tv.getWidth(); //return x
                              }
                          });
          در اینجا مقدار واقعی x بازگردانده می‌شود.

          باز می‌گردیم به متد DrawPath و داخل متد post
           در اولین خط این پروسه به ازای هر آیتم، یک TextView توسط متد MakeTextView ساخته می‌شود که شامل کد زیر است:
            private TextView MakeTextView(final int position, final int Id)
              {
                  //settings for cumbs
                  TextView tv=new TextView(this.context);
                  tv.setEllipsize(TextUtils.TruncateAt.END);
                  tv.setSingleLine(true);
                  tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
                  tv.setBackgroundResource(viewStyleId);
          
                  /*call custom event - this event will be fired when user click on one of
                   textviews and returns position of textview and value that user sat as id
                   */
                  tv.setOnClickListener(new View.OnClickListener() {
                      @Override
                      public void onClick(View v) {
          
                          SetPosition(position);
                          clickListener.onClick(position, Id);
                      }
                  });
          
                  //if user wants to update each textviews
                  if(textViewUpdate!=null)
                      tv=textViewUpdate.UpdateTextView(context,tv);
          
                  if(isRTL())
                      tv.setRotationY(180);
          
                  return tv;
              }

          در خطوط اولیه، یک Textview ساخته و متد Ellipsize را با Truncate.END مقداردهی می‌نماید. این مقدار دهی باعث می‌شود اگر متن، در Textview جا نشد، ادامه‌ی آن با ... مشخص شود. در خط بعدی Textview را تک خطه معرفی می‌کنیم. در خط بعدی اندازه‌ی قلم را بر اساس آنچه کاربر مشخص کرده است، تغییر می‌دهیم و بعد هم استایل را برای آن مقداردهی می‌کنیم. بعد از آن رویداد کلیک را برای آن مشخص می‌کنیم تا اگر کاربر بر روی آن کلیک کرد، رویداد اختصاصی خودمان را فراخوانی کنیم.
          در خط بعدی اگر rtl با true مقدار دهی شده باشد، textview را حول محور Y چرخش می‌دهد تا برای زبان‌های راست به چپ چون فارسی آماده گردد و در نهایت Textview ساخته شده و به سمت متد DrawPath باز می‌گرداند.

          بعد از ساخته شدن TextViewها، وقت آن است که به Layout اضافه شوند که وظیفه‌ی اینکار بر عهده‌ی متد AddTextViewOnLayout است:
           //this method calling by everywhere to needs add textviews on the layout like master method :drawpath
              private void AddTextViewsOnLayout()
              {
                  //prepare layout
                  //remove everything on layout for recreate it
                  layout.removeAllViews();
                  layout.setOrientation(LinearLayout.HORIZONTAL);
                  layout.setVerticalGravity(Gravity.CENTER_VERTICAL);
                  if(isRTL())
                      layout.setRotationY(180);
          
          
          
                  //add textviews one by one
          
                  int position=0;
                  for (TextView tv:textViews)
                  {
                      layout.addView(tv,params);
          
                      //add next node image between textviews if user defined a next node image
                      if(tinyNextNodeImage>0)
                          if(position<(textViews.size()-1)) {
                              layout.addView(GetNodeImage(), params);
                              position++;
                          }
                  }
          
              }

          در چند خط اول، Layout آماده سازی می‌شود. این آماده سازی شامل پاکسازی اولیه Layout یا خالی کردن ویوهای درون آن است که می‌تواند از رندر قبلی باشد. افقی بودن جهت چینش Layout، در مرکز نگاه داشتن ویوها و نهایتا چرخش حول محور Y در صورت true بودن خاصیت RTL است. در خطوط بعدی یک حلقه وجود دارد که Textview‌های ایجاد شده را یک به یک در Layout می‌چیند و اگر کاربر تصویر گرافیکی را هم به (همان فلش‌های اشاره‌گر) متغیر tinyNextNodeImage نسبت داده باشد، آن‌ها را هم بین TextView‌ها می‌چیند و بعد از پایان یافتن کار، مجددا به متد DrawPath باز می‌گردد.
          تا به اینجا کار چیدمان به ترتیب انجام شده است ولی از آنجا که اندازه‌ی Layout در هر گوشی و  در دو حالت حالت افقی یا عمودی نگه داشتن گوشی متفاوت است، نمی‌توان به این چینش اعتماد کرد که به چه نحوی عناصر نمایش داده خواهند شد و این مشکل توسط متد BatchSizeOperation (تغییر اندازه دسته جمعی) حل می‌گردد. در اینجا هم باز متد post به آخرین textview اضافه شده است. به این علت که موقعی‌که همه‌ی textview‌ها در ui جا خوش کردند، بتوانیم به خاصیت‌های ui آن‌ها دستیابی داشته باشیم. حالا بعد از ترسیم باید اندازه آن‌ها را اصلاح کنیم. قدم به قدم متد BatchSizeOperation را بررسی می‌کنیم:
          //set textview width depend on screen width
          private void BatchSizeOperation()
          {
          //get width of next node between cumbs
          Bitmap tinyBmap = BitmapFactory.decodeResource(context.getResources(), tinyNextNodeImage);
          int tinysize=tinyBmap.getWidth();
          //get sum of nodes
          tinysize*=(textViews.size()-1);
          ...
          }
          ابتدا لازم است ‍‍‍‍‍طول مسیری که همه ویوها یا المان‌های ما را دارند، به دست آوریم. اول از تصویر کوچک شروع می‌کنیم و پهنای آن را می‌گیریم. سپس عدد به دست آمده را در تعداد آن ضرب می‌کنیم تا جمع پهناها را داشته باشیم. سپس نوبت به TextView‌ها می‌رسد.

            //get width size of screen(layout is screen here)
                  int screenWidth=GetLayoutWidthSize();
          
                  //get sum of arrows and cumbs width
                  int sumtvs=tinysize;
                  for (TextView tv : textViews) {
          
                      int width=tv.getWidth();
                      sumtvs += width;
                  }
          در ادامه‌ی این متد، متد GetLayoutWidthSize را صدا می‌زنیم که وظیفه‌ی آن برگرداندن پهنای layout است و کد آن به شرح زیر است:
              private int GetLayoutWidthSize()
              {
                  int width=layout.getWidth();
                  int padding=layout.getPaddingLeft()+layout.getPaddingRight();
                  width-=padding;
                  return width;
              }
          در این متد پهنا به احتساب padding‌های چپ و راست به دست می‌آید و مقدار آن را به عنوان اندازه‌ی صفحه نمایش، تحویل متد والد می‌دهد. در ادامه هم پهنای هر Textview محاسبه شده و جمع کل آن‌ها را با اندازه‌ی صفحه مقایسه می‌کند. اگر کوچکتر بود، کار این متد در اینجا تمام می‌شود و نیازی به تغییر اندازه نیست. ولی اگر نبود کد ادامه می‌یابد:
              private void  BatchSizeOperation()
              {
                  ....
          
              //if sum of cumbs is less than screen size the state is good so return same old textviews
                  if(sumtvs<screenWidth)
                      return ;
          
          
                  if(textViews.size()>3)
                  {
                      //make fake path
                      MakeFakePath();
          
                      //clear layout and add textviews again
                      AddTextViewsOnLayout();
                  }
          
                  //get free space without next nodes -> and spilt rest of space to textviews count to get space for each textview
                  int freespace =screenWidth-tinysize;
                  int each_width=freespace/textViews.size();
          
                  //some elements have less than each_width,so we should leave size them and calculate more space again
                  int view_count=0;
                  for (TextView tv:textViews)
                  {
                      if (tv.getWidth()<=each_width)
                          freespace=freespace-tv.getWidth();
                      else
                          view_count++;
                  }
                  if (view_count==0) return;
          
                  each_width=freespace/view_count;
                  for (TextView tv:textViews)
                  {
                      if (tv.getWidth()>each_width)
                          tv.setWidth(each_width);
                  }
          
          
              }

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

          نحوه‌ی کارکرد متد MakeFakePath بدین صورت است که 4 عدد TextView را ایجاد کرده که المان‌های با اندیس 0 و 2 و 3 به صورت نرمال و عادی ایجاد شده و همان کارکرد سابق را دارند. ولی المان شماره دو با اندیس 1 با متن ... نماینده‌ی آیتم‌های میانی است و رویدادکلیک  آن به شکل زیر تحریف یافته است:

           //if elements are so much(mor than 3),we make a fake path to decrease elements
              private void MakeFakePath()
              {
                  //we make 4 new elements that index 1 is fake element and has a rest of real path in its heart
                  //when user click on it,path would be opened
                  textViews=new ArrayList<>(4);
                  TextView[] tvs=new TextView[4];
                  int[] positions= {0,items.size()-3,items.size()-2,items.size()-1};
          
                  for (int i=0;i<4;i++)
                  {
                      //request for new textviews
                      tvs[i]=MakeTextView(positions[i],items.get(positions[i]).getId());
          
                      if(i!=1)
                          tvs[i].setText(items.get(positions[i]).getDiplayText());
                      else {
                          tvs[i].setText("...");
                          //override click event and change it to part of code to open real path by call setposition method and redraw path
                          tvs[i].setOnClickListener(new View.OnClickListener() {
                              @Override
                              public void onClick(View v) {
                                  int pos = items.size() - 3;
                                  int id = items.get(pos).getId();
                                  SetPosition(items.size() - 3);
                                  clickListener.onClick(pos, id);
                              }
                          });
                      }
                      textViews.add(tvs[i]);
                  }
              }
          این رویداد با استفاده از setPosition به آیتم index-3 بازگشته و مجددا المان‌ها رسم می‌گردند و سپس رویداد کلیک این آیتم را هم اجرا می‌کند و المان‌های با اندیس 2 و 3 را به ترتیب به رویدادهای index-1 و index-2 متصل می‌کنیم.
          مطالب
          ایجاد چارت سازمانی تحت وب #3
          در مطالب قسمت اول و دوم به نحوه ایجاد و تغییر رنگ چارت سازمانی اشاره شد. در این مطلب ، نحوه تغییر فونت‌ها، مکان قرار گرفتن شاخه‌ها و ایجاد لینک در شاخه‌ها ارائه میشود. بدین صورت که در شکل زیر مشاهده مینمائید:

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

          با توجه به محدودیت IE در پیاده سازی excanvas  ، در کل کاراکترها متن نود کوتاه میشود. ( اگر کاراکترهای نود ، کاملا پرشونده fit نشوند ، بخشی از کل متن کاراکترهای نود نوشته یا رسم خواهد شد )

          پارامترهای تابع ()setFont :

          1. نام فونت . حالت فونت ضخیم bold  یا مورب italic قابل استفاده است.
          2. اندازه فونت در واحد پیکسل
          3. رنگ فونت ( اختیاری )
          4. چیدمان عمودی ( 1 و c یا center برای وسط چین . ( اختیاری )
          برای مشاهده از این کدها میتوانید استفاده نمائید:
          var o = new orgChart();
          
          o.setColor('#99CC99', '#CCFFCC');
          o.setFont('arial', 18);
          o.addNode(0, '', '', 'Arial 18', 1);
          
          o.setColor('#CCCC66', '#FFFF99');
          o.setFont('arial', 10, '#000000');
          o.addNode(11, 0, 'u', 'text will be split if it does not fit. ThisIsAVeryLongWordAndItWillBeClipped. Too many lines will be clipped too.');
          
          o.setFont('arial', 10, '#000000', 0);
          o.addNode(12, 0, 'u', 'aligned at top');
          
          o.setFont('arial', 10, '#000000', 1);
          o.addNode(13, 0, 'u', 'centered');
          o.setColor('#CC4950', '#FF7C80');
          
          o.setFont('times', 16, '#FF0F00');
          o.addNode(21, 0, 'l', 'Times 16 red');
          
          o.setFont('times', 16, '#000000');
          o.addNode(22, 0, 'l', 'Times 16');
          
          o.setFont('times', 48, '#000000');
          o.addNode(23, 0, 'l', 'Times 48 does not fit at all');
          o.setColor('#CC9966', '#FFCC99');
          
          o.setFont('jokerman', 12, '#000000');
          o.addNode(31, 0, 'r', 'Jokerman (if available)');
          
          o.setFont('bold times', 16, '#000000');
          o.addNode(32, 0, 'r', 'bold Times 16');
          
          o.setFont('italic times', 16, '#000000');
          o.addNode(33, 0, 'r', 'italic Times 16');
          
          o.setFont('bold italic times', 16, '#000000');
          o.addNode(34, 0, 'r', 'bold italic Times 16');
          
          o.setColor('#B5D9EA', '#CFE8EF');
          o.setFont('arial', 28, '#000000');
          o.addNode(50, '', '', 'Arial 28');
          o.setFont('arial', 29);
          o.addNode(51, 50, 'u', 'Arial 29');
          o.setFont('arial', 30);
          o.addNode(52, 51, 'u', 'Arial 30');
          o.setFont('arial', 31);
          o.addNode(53, 52, 'u', 'Arial 31');
          o.setFont('arial', 32);
          o.addNode(54, 53, 'u', 'Arial 32');
          
          o.drawChart('c_fonts');

          اندازه و مکان :

          شما میتوانید اندازه نودها و فضا و offset بین نودها را نیز تنظیم نمائید.این تنظیم بصورت عمومی تاثیر گذار است و تمامی نودها از این تنظیم تبعیت خواهند نمود:


          پارامترهای تابع ()setSize:
          1. عرض نودها در واحد پیکسل.
          2. ارتفاع نودها در واحد پیکسل.
          3. فاصله عرضی بین نودهای پدر u-nodes. ( اختیاری ).
          4. فاصله طولی بین نودها ( اختیاری ).
          5. offset عرضی ( فاصله )  از نود چپ و نود راست ( اختیاری ).
          var o = new orgChart();
          
          o.setSize(36, 12, 4, 25, 180);
          
          o.setColor('#99CC99', '#CCFFCC');
          o.setFont('arial', 18);
          o.addNode(0, '', '', 'Root node');
          o.setFont('arial', 12);
          o.setColor('#CCCC66', '#FFFF99');
          o.addNode(11, 0, 'u', 'u-node 1');
          o.addNode(12, 0, 'u', 'u-node 2');
          o.addNode(13, 0, 'u', 'u-node 3');
          o.setColor('#CC4950', '#FF7C80');
          o.addNode(21, 0, 'l', 'l-node 1');
          o.addNode(22, 0, 'l', 'l-node 2');
          o.addNode(23, 0, 'l', 'l-node 3');
          o.setColor('#CC9966', '#FFCC99');
          o.addNode(31, 0, 'r', 'r-node 1');
          
          o.drawChart('c_size');
          پیوندها: LINKS
          شما میتوانید به نودها در پارامتر ششم تابع ()addNode آدرس پیوند خود را اضافه نمائید.
          در صورت ایجاد پیوند کامل ( مانند : http://www.yourdomain.com ) پیوند در برگه ( tab ) یا یک پنجره جدید ( بسته به تنظیمات مرورگر سمت کاربر ) باز خواهد شد.
          اگر نشانگر ماوس ، روی این نوع از نود‌ها قرار بگیرد تغییر شکل به مانند دست ( اشاره گر ) میدهد.



          نکته : در این نمونه کد ، هر نود در یک چارت سازمانی جدید دوباره رسم شده اند.در چارت سازمانی قدیمی ، نودها از بین نمی‌روند و همه مسیرهای باقی مانده فعال خواهند ماند.بنابراین اگر reDraw در این نمونه استفاده شود ، چند پیوند در یک نود باز خواهد شد .
          اگر بخواهید فقط یک لینک به نودی اختصاص دهید ، یک نود پیوندی بدون پیوند به آن اضافه کنید ( مانند نودها سبز مثال نمونه ).
          var o = new orgChart();
          
          o.setColor('#99CC99', '#CCFFCC');
          o.setFont('arial', 18);
          o.addNode( 0, '', '', 'Searching', 1);
          o.addNode(50, '', '', 'Social', 1);
          o.addNode(90, '', '', 'Misc.', 1);
          
          o.setColor('#CCCC66', '#FFFF99');
          o.setFont('arial', 12);
          o.addNode(11, 50, 'u', 'Facebook', 0, 'http://facebook.com');
          o.addNode(13, 90, 'u', 'Youtube', 0, 'http://youtube.com');
          o.addNode(14, 13, 'l', 'Youtube Music', 0, 'http://youtube.com/music');
          o.addNode(15, 13, 'l', 'Youtube Entertainment', 0, 'http://youtube.com/entertainment');
          
          o.setColor('#CC4950', '#FF7C80');
          o.addNode(21, 0, 'l', 'Google', 0, 'http://google.com');
          o.addNode(22, 0, 'l', 'Bing', 0, 'http://bing.com');
          
          o.addNode('r2', '', '', 'Top of this Page', 0, '#');
          o.addNode('', 'r2', 'u', 'Back to the introduction', 0, '/orgchart');
          
          o.drawChart('c_links');

          در قسمت چهارم و آخر این مطلب ، نمونه‌های بیشتری از ایجاد چارت سازمانی تحت وب ، درج تصویر در نودها و نمایش نمودار بعنوان یک تصویر ارائه خواهد شد.

          مطالب
          کار با شیوه‌نامه‌های فرم‌ها در بوت استرپ 4
          بوت استرپ، به همراه کلاس‌هایی است، برای نمایش زیباتر فرم‌ها، که شامل کلاس‌های اعتبارسنجی و حتی کنترل نحوه‌ی چیدمان و اندازه‌ی آن‌ها نیز می‌شود.


          ایجاد فرم‌های مقدماتی، با بوت استرپ 4

          بوت استرپ به همراه کلاس‌هایی مانند form-group و form-control است که از آن‌ها می‌توان برای ایجاد یک فرم مقدماتی استفاده کرد. در ابتدا مثال غیر تزئین شده‌ی زیر را در نظر بگیرید:
          <body>
              <div class="container">
                  <h2>Medical Questionnaire</h2>
                  <form>
                      <fieldset>
                          <legend>Owner Info</legend>
                          <div>
                              <label for="ownername">Owner name</label>
                              <input type="text" id="ownername" placeholder="Your Name">
                          </div>
                          <div>
                              <label for="owneremail">Email address</label>
                              <input type="email" id="owneremail" aria-describedby="emailHelp"
                                  placeholder="Enter email">
                              <small id="emailHelp">We'll never share your email</small>
                          </div>
                      </fieldset>
          
                      <fieldset>
                          <legend>Pet Info</legend>
                          <div>
                              <label for="petname">Pet name</label>
                              <input type="text" id="petname" placeholder="Your Pet's name">
                          </div>
                          <div>
                              <label for="pettype">Pet type</label>
                              <select id="pettype">
                                  <option>Choose</option>
                                  <option value="cat">Dog</option>
                                  <option value="cat">Cat</option>
                                  <option value="bird">Other</option>
                              </select>
                          </div>
                          <div>
                              <label for="reasonforvisit">Reason for today's visit</label>
                              <textarea id="reasonforvisit" rows="3"></textarea>
                          </div>
                          <div>
                              <label>Has your pet been spayed or neutered?</label>
                              <label><input type="radio" name="spayneut" value="yes"
                                      checked> Yes</label>
                              <label><input type="radio" name="spayneut" value="no"> No</label>
                          </div>
                          <div>
                              <label>Has the pet had any of the following in the past 30
                                  days</label>
                              <label><input type="checkbox"> Abdominal pain</label>
                              <label><input type="checkbox"> Lack of appetite</label>
                              <label><input type="checkbox"> Weakness</label>
                          </div>
                      </fieldset>
                      <button type="submit">Submit</button>
                  </form>
          
              </div><!-- content container -->
          </body>
          که چنین خروجی ابتدایی را نیز به همراه دارد:


          در ادامه شروع می‌کنیم به تزئین کردن این فرم، با کلاس‌های بوت استرپ 4:
          - ابتدا به fieldsetهای تعریف شده، کلاس form-goup را انتساب می‌دهیم. این مورد سبب می‌شود تا اندکی فاصله بین آن‌ها ایجاد شود.
          - سپس به تمام divهایی که المان‌ها را در بر گرفته‌اند نیز کلاس form-group را اعمال می‌کنیم.
          با اینکار فاصله‌ی مناسبی بین المان‌های تعریف شده‌ی در صفحه ایجاد می‌شود:


          - در ادامه به تمام المان‌های input، select و textarea (منهای checkboxها) کلاس form-control را نسبت می‌دهیم:


          با اینکار، ظاهر این المان‌ها بسیار شکیل‌تر شده‌است و همچنین این فرم واکنشگرا نیز می‌باشد.

          - پس از آن، تمام المان‌های label را انتخاب کرده و کلاس form-control-label را به آن‌ها انتساب می‌دهیم. هرچند با اینکار ظاهر فعلی فرم تغییری نمی‌کند، اما چنین تعریفی برای فعالسازی کلاس‌های اعتبارسنجی ضروری است.
          اگر به هر دلیلی نخواستید این برچسب‌ها را نمایش دهید، می‌توانید از کلاس sr-only استفاده کنید که صرفا سبب نمایش آن‌ها به screen readers می‌شود.
          - ذیل فیلد ورود ایمیل، متنی وجود دارد. این متن را با کلاس‌های form-text text-muted مزین می‌کنیم:


          - به دکمه‌ی پایین صفحه نیز کلاس‌های btn btn-primary را اضافه می‌کنیم که در مطلب «بررسی شیوه‌نامه‌های المان‌های پر کاربرد بوت استرپ 4» بیشتر به آن‌ها پرداختیم.


          مزین سازی checkboxها و radio-buttonها در بوت استرپ 4

          روش مزین سازی checkboxها و radio-buttonها در بوت استرپ، با سایر المان‌ها اندکی متفاوت است:
          <div class="form-check">
              <label class="form-check-label">
                  <input class="form-check-input" type="checkbox">
                  Lack of appetite
              </label>
          </div>
          در اینجا تفاوتی نمی‌کند که بخواهیم با checkboxها کار کنیم و یا radio-buttonها، هر دوی این المان‌ها ابتدا داخل یک div با کلاس form-check قرار می‌گیرند. سپس برچسب آن‌ها دارای کلاس form-check-label می‌شود و در آخر به خود این المان‌های input، کلاس form-check-input اضافه خواهد شد.

          یک نکته: اگر نیاز است این المان‌ها کنار یکدیگر نمایش داده شوند، می‌توان بر روی div آن‌ها از کلاس‌های form-check form-check-inline استفاده کرد. در این حالت اگر می‌خواهید برچسب برای مثال radio-button تعریف شده، در یک سطر و گزینه‌ها آن در سطری دیگر باشند، از کلاس d-block بر روی این برچسب استفاده کنید:
          <div class="form-group">
              <label class="d-block">Has your pet been spayed or
                  neutered?</label>
              <div class="form-check form-check-inline">
                  <label class="form-check-label">
                      <input class="form-check-input" type="radio" name="spayneut"
                             value="yes" checked>
                      Yes
                  </label>
              </div>
              <div class="form-check form-check-inline">
                  <label class="form-check-label">
                      <input class="form-check-input" type="radio" name="spayneut"
                             value="no"> No
                  </label>
              </div>
          </div>
          با این خروجی:



          کلاس‌های کنترل اندازه و اعتبارسنجی المان‌های فرم‌های بوت استرپ 4

          - با استفاده از کلاس form-control-sm می‌توان اندازه‌ی فیلدهای input را با ارتفاع کوچکتری نمایش داد و یا توسط کلاس form-control-lg می‌توان آن‌ها را بزرگتر کرد.
          - کلاس form-inline سبب می‌شود تا یک form-group به صورت inline نمایش داده شود. یعنی برچسب و کنترل‌های درون آن، در طی یک سطر نمایش داده خواهند شد. در این حالت، نیاز به کلاس‌های Margin مانند mx-sm-2 خواهد بود تا فاصله‌ی بین کنترل‌ها را بتوان کنترل کرد.
          - برای نمایش نتایج اعتبارسنجی کنترل‌ها:
            - اگر کل فرم اعتبارسنجی شده‌است، کلاس was-validated را به المان form اضافه کنید.
            - اگر اعتبارسنجی کنترلی با موفقیت روبرو شود، کلاس is-valid و اگر خیر کلاس is-invalid را به آن نسبت دهید.
            - اگر می‌خواهید پیام خاصی را پس از موفقیت اعتبارسنجی نمایش دهید، آن‌را درون یک div با کلاس valid-feedback قرار دهید و یا برعکس از کلاس invalid-feedback استفاده کنید.
            - برای تغییر رنگ برچسب المان‌ها نیز از کلاس‌های text-color همانند قبل استفاده کنید؛ مانند text-success.

          یک مثال:
          <div class="form-group">
              <label for="owneremail" class="text-success">Email address</label>
              <input class="form-control is-valid" type="email" id="owneremail"
                  aria-describedby="emailHelp" placeholder="Enter email">
              <small class="form-text text-muted" id="emailHelp">We'll
                  never share your email</small>
              <div class="valid-feedback">
                  Looks good!
              </div>
          </div>
          با این خروجی:



          تغییر نحوه‌ی چیدمان عناصر فرم‌ها در بوت استرپ 4

          فرم زیر را در نظر بگیرید:


          قصد داریم با استفاده از کلاس‌های ویژه‌ی بوت استرپ 4، آن‌را دو ستونی کنیم؛ به طوریکه برچسب‌ها در یک ستون و فیلدهای ورودی، در ستونی دیگر نمایش داده شوند. همچنین این فرم واکنشگرا نیز باشد؛ به این معنا که این دو ستونی شدن، فقط در اندازه‌های پس از md رخ دهد:
          <body>
              <div class="container">
                  <h2>Medical Questionnaire</h2>
                  <form>
                      <fieldset class="form-group">
                          <legend>Owner Info</legend>
                          <div class="form-group row">
                              <label class="form-control-label col-md-2 col-form-label text-md-right"
                                  for="ownername">Owner</label>
                              <div class="col-md-10">
                                  <input class="form-control" type="text" id="ownername"
                                      placeholder="Your Name">
                              </div>
                          </div>
                          <div class="form-group row">
                              <label class="form-control-label col-md-2 col-form-label text-md-right"
                                  for="owneremail">Address</label>
                              <div class="col-md-10">
                                  <input class="form-control" type="text" id="owneremail"
                                      placeholder="Address">
                              </div>
                          </div>
                          <div class="form-group row">
                              <div class="form-group col-6 offset-md-2">
                                  <label class="form-control-label sr-only" for="ownercity">City</label>
                                  <input class="form-control" type="text" id="ownercity"
                                      placeholder="City">
                              </div>
                              <div class="form-group col-md-4 col-6">
                                  <label class="form-control-label sr-only" for="ownerzip">Zip</label>
                                  <input class="form-control" type="text" id="ownerzip"
                                      placeholder="Zip">
                              </div>
                          </div>
          
                          <div class="form-group row">
                              <div class="offset-md-2 col-md-10">
                                  <button class="btn btn-primary" type="submit">Submit</button>
                              </div>
                          </div>
                      </fieldset>
                  </form>
              </div>
          </body>
          با این خروجی در اندازه‌ی پس از md:


          توضیحات:
          برای ستونی کردن فرم‌ها، ابتدا کلاس row، به form-group قرار گرفته‌ی داخل container اصلی اضافه می‌شود:
                          <div class="form-group row">
                              <label class="form-control-label col-md-2 col-form-label text-md-right"
                                  for="ownername">Owner</label>
                              <div class="col-md-10">
                                  <input class="form-control" type="text" id="ownername"
                                      placeholder="Your Name">
                              </div>
                          </div>
          سپس توسط کلاس col-md-2 تعریف شده‌ی بر روی برچسب، سبب خواهیم شد تا در اندازه‌ی صفحه‌ی بیش از md، این برچسب در یک ستون با عرض دو واحد قرار گیرد. در یک چنین حالتی، ذکر col-form-label نیز ضروری است. همچنین اگر مایل باشیم تا این برچسب، در سمت راست این ستون قرار گیرد، می‌توان از کلاس واکنشگرای text-md-right استفاده کرد.
          پس از آن نوبت به تعریف ستون فیلد تعریف شده‌است که با ایجاد یک div و تعریف تعداد واحدی را که به خود اختصاص می‌دهد (col-md-10)، انجام می‌شود.

          در اینجا برچسب‌های فیلدهای city و zip با کلاس sr-only مشخص شده‌اند. به همین جهت فقط به screen readers نمایش داده می‌شوند.
          <div class="form-group row">
             <div class="form-group col-6 offset-md-2">
             <label class="form-control-label sr-only" for="ownercity">City</label>
             <input class="form-control" type="text" id="ownercity"placeholder="City">
          </div>
          در یک چنین حالتی، برای اینکه این فیلدها در ستون دوم ظاهر شوند، از کلاس offset-md-2 استفاده شده‌است. از این offset برای تراز کردن دکمه، با ستون دوم نیز استفاده کرده‌ایم:
          <div class="form-group row">
              <div class="offset-md-2 col-md-10">
                  <button class="btn btn-primary" type="submit">Submit</button>
              </div>
          </div>

          ایجاد گروهی از ورودی‌ها در بوت استرپ 4

          برای افزودن آیکن‌هایی به فیلدهای ورودی، از روش ایجاد گروهی از ورودی‌ها در بوت استرپ 4 استفاده می‌شود:
          <div class="form-group">
              <label class="form-control-label" for="donationamt">
                  Donation Amount
              </label>
              <div class="input-group">
                  <div class="input-group-prepend">
                      <span class="input-group-text">$</span>
                  </div>
                  <input type="text" class="form-control" id="donationamt"
                      placeholder="Amount">
                  <div class="input-group-append">
                      <span class="input-group-text">.00</span>
                  </div>
              </div>
          </div>
          در مثال فوق، روش تعریف یک input-group را مشاهده می‌کنید. داخل آن یک input-group-prepend و سپس input-group-text تعریف می‌شود که می‌تواند شامل یک متن و یا آیکن باشد. اگر نیاز به تعریف دکمه‌ای وجود داشت، از این کلاس استفاده نکنید. با این خروجی:


          در بوت استرپ 4، کلاس‌های input-group-addon و input-group-btn  بوت استرپ 3 حذف و با کلاس‌های input-group-prepend و input-group-append جایگزین شده‌اند. از prepend برای قرار دادن آیکنی پیش از فیلد ورودی و از append همانند مثال فوق، برای قرار دادن آیکنی اختیاری پس از فیلد ورودی استفاده می‌شود.

          نمونه‌ی متداول دیگر آن، نحوه‌ی تعریف ویژه‌ی فیلد جستجوی سایت، در منوی راهبری آن است:
              <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
                  <div class="container">
                      <div class="navbar-brand d-none d-sm-inline-block">
                          Wisdom Pet Medicine
                      </div>
                      <div class="navbar-nav mr-auto">
                          <a class="nav-item nav-link active" href="#">Home</a>
                          <a class="nav-item nav-link" href="#">Mission</a>
                          <a class="nav-item nav-link" href="#">Services</a>
                          <a class="nav-item nav-link" href="#">Staff</a>
                          <a class="nav-item nav-link" href="#">Testimonials</a>
                      </div>
                      <form class="form-inline d-none d-md-inline-block">
                          <div class="input-group">
                              <label for="search" class="form-control-label sr-only"></label>
                              <input type="text" id="search" class="form-control"
                                  placeholder="Search ...">
                              <div class="input-group-append">
                                  <button class="btn btn-outline-light" type="submit">Go</button>
                              </div>
                          </div>
                      </form>
                  </div>
              </nav>
          با این خروجی که در آن دکمه، توسط کلاس input-group-append، با فیلد ورودی کنار آن، یکپارچه به نظر می‌رسد:




          کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_10.zip
          اشتراک‌ها
          ویژگیهای نسخه 1.12 شیرپوینت فریم ورک SharePoint Framework (SPFx) V1.12


           تقریبا چهار روز پیش اواسط هفته گذشته ( در تاریخ 16 March )  مایکروسافت نسخه 1.12 شیرپوینت فریم ورک ( SPFx ) را منتشر کرد. نسخه تازه منتشر شده شامل برخی از تغییرات مربوط به generator و بعضی از تغییرات همانند سایر نسخه‌ها بود. (همانطور که بهتر میدانید generator همان تکنولوژی است که با آن سولوشن‌های شیرپوینت فریم ورک ایجاد میشوند ، با استفاده از yeoman ) . اما موضوع مهمی که در این نسخه که برای توسعه دهندگاه SharePoint Framework حائز اهمیت است، پشتیبانی از Node.js V12 است. این موضوع خبر بسیار خوبی برای توسعه دهنگان SPFx است چون از ماه آینده ( اواخر April ) نسخه Node.js V10 رسما پشتیبانی نخواهد شد.

          البته ممکن است بسته به نوع سولوشنی که در SPFx v1.12 ایجاد می‌نمایید بتوانید بدون مشکل از node.js v14  استفاده کنید اما نسخه ای که به صورت غیر رسمی توصیه شده همان نسخه 12 نود جی اس است. خبر خوب‌تر اینکه انتظار می‌رود برای نسخه 1.13 شیرپوینت فریم ورک ( SharePoint Framework v.13 ) که از هفته‌های آینده )نهایتا ( در ماه آینده منتشر خواهد شد بتوان از آخرین نسخه node.js بدون محدودیت استفاده از ورژن‌های مختلف بدون مشکل استفاده نمود.

          ویژگیهای نسخه 1.12 شیرپوینت فریم ورک SharePoint Framework (SPFx) V1.12
          مطالب
          رمزنگاری خودکار فیلدها توسط Entity Framework Core
          از EF Core 2.1 به بعد، قابلیت جدیدی تحت عنوان «تبدیلگرهای مقدار»، به آن اضافه شده‌است. برای مثال در EF Core، زمانیکه اطلاعات Enums، در بانک اطلاعاتی ذخیره می‌شوند، معادل عددی آن‌ها درج خواهند شد. اگر علاقمند باشید تا بجای این مقادیر عددی دقیقا همان رشته‌ی تعریف کننده‌ی Enum درج شود، می‌توان یک «تبدیلگر مقدار» را برای آن نوشت. برای مثال در موجودیت Rider زیر، خاصیت Mount از نوع یک enum است.
          public class Rider
          {
              public int Id { get; set; }
              public EquineBeast Mount { get; set; }
          }
          
          public enum EquineBeast
          {
              Donkey,
              Mule,
              Horse,
              Unicorn
          }
          برای اینکه در حین درج رکوردهای Rider در بانک اطلاعاتی دقیقا از مقادیر رشته‌ای EquineBeast استفاده شود، می‌توان به صورت زیر عمل کرد:
          protected override void OnModelCreating(ModelBuilder modelBuilder)
          {
              modelBuilder
                  .Entity<Rider>()
                  .Property(e => e.Mount)
                  .HasConversion(
                      v => v.ToString(),
                      v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
          }
          در اینجا در حین تعریف جزئیات نگاشت یک مدل می‌توان متد جدید HasConversion را نیز فراخوانی کرد. پارامتر اول آن، روش تبدیل مقدار enum را به یک رشته، جهت درج در بانک اطلاعاتی و پارامتر دوم آن، روش تبدیل مقدار رشته‌ای خوانده شده‌ی از بانک اطلاعاتی را جهت وهله سازی یک Rider داری خاصیت enum، مشخص می‌کند.

          نکته 1: مقادیر نال، هیچگاه به تبدیلگرهای مقدار، ارسال نمی‌شوند. اینکار پیاده سازی آن‌ها را ساده‌تر می‌کند و همچنین می‌توان آن‌ها را بین خواص نال‌پذیر و نال‌نپذیر، به اشتراک گذاشت. بنابراین برای مقادیر نال نمی‌توان تبدیلگر نوشت.

          نکته 2: کاری که در متد HasConversion فوق انجام شده‌است، در حقیقت وهله سازی ضمنی یک ValueConverter و استفاده از آن است. می‌توان اینکار را به صورت صریح نیز انجام داد:
          var converter = new ValueConverter<EquineBeast, string>(
              v => v.ToString(),
              v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
          modelBuilder
              .Entity<Rider>()
              .Property(e => e.Mount)
              .HasConversion(converter);
          مزیت اینکار این است که اگر قرار شد برای چندین خاصیت از تبدیلگر مقدار مشابهی استفاده کنیم، می‌توان از یک converter تعریف شده بجای تکرار کدهای آن استفاده کرد.


          تبدیلگرهای مقدار توکار EF Core

          برای بسیاری از اعمال متداول، در فضای نام Microsoft.EntityFrameworkCore.Storage.ValueConversion، تعدادی تبدیلگر مقدار تدارک دیده شده‌اند که به این شرح می‌باشند:
          BoolToZeroOneConverter: تبدیلگر bool به صفر و یک
          BoolToStringConverter: تبدیلگر bool به Y و یا N
          BoolToTwoValuesConverter: تبدیلگر bool به دو مقداری دلخواه
          BytesToStringConverter: تبدیلگر آرایه‌ای از بایت‌ها به یک رشته‌ی Base64-encoded
          CastingConverter: تبدیلگر یک نوع به نوعی دیگر
          CharToStringConverter: تبدیلگر char به string
          DateTimeOffsetToBinaryConverter: تبدیلگر DateTimeOffset به یک مقدار 64 بیتی باینری
          DateTimeOffsetToBytesConverter: تبدیلگر DateTimeOffset به آرایه‌ای از بایت‌ها
          DateTimeOffsetToStringConverter: تبدیلگر DateTimeOffset به رشته
          DateTimeToBinaryConverter: تبدیلگر DateTime به یک مقدار 64 بیتی با درج DateTimeKind
          DateTimeToStringConverter: تبدیلگر DateTime به یک رشته
          DateTimeToTicksConverter: تبدیلگر DateTime به ticks آن
          EnumToNumberConverter: تبدیلگر Enum به عدد متناظر با آن
          EnumToStringConverter: تبدیلگر Enum به رشته
          GuidToBytesConverter: تبدیلگر Guid به آرایه‌ای از بایت‌ها
          GuidToStringConverter: تبدیلگر Guid به رشته
          NumberToBytesConverter: تبدیلگر اعداد به آرایه‌ای از بایت‌ها
          NumberToStringConverter: تبدیلگر اعداد به رشته
          StringToBytesConverter: تبدیلگر رشته به آرایه‌ای از بایت‌های UTF8 معادل آن
          TimeSpanToStringConverter: تبدیلگر TimeSpan به رشته
          TimeSpanToTicksConverter: تبدیلگر TimeSpan به ticks آن

          برای نمونه در این لیست، EnumToStringConverter نیز وجود دارد. بنابراین نیازی به تعریف دستی آن مانند مثال ابتدای بحث نیست و می‌توان به صورت زیر از آن استفاده کرد:
          var converter = new EnumToStringConverter<EquineBeast>();
          modelBuilder
              .Entity<Rider>()
              .Property(e => e.Mount)
              .HasConversion(converter);
          نکته: تمام تبدیل کننده‌های مقدار توکار EF Core، بدون حالت هستند. بنابراین می‌توان یک تک وهله‌ی از آن‌ها را بین چندین خاصیت به اشتراک گذاشت.


          تعیین نوع تبدیلگر مقدار، جهت ساده سازی تعاریف

          برای حالاتی که تبدیلگر مقدار توکاری تعریف شده‌است، صرفا تعریف نوع تبدیل، کفایت می‌کند:
          modelBuilder
              .Entity<Rider>()
              .Property(e => e.Mount)
              .HasConversion<string>();
          برای نمونه در اینجا با ذکر نوع رشته، تبدیل enum به string به صورت خودکار انجام خواهد شد. معادل اینکار، تعریف نوع سمت بانک اطلاعاتی این خاصیت است:
          public class Rider
          {
              public int Id { get; set; }
          
              [Column(TypeName = "nvarchar(24)")]
              public EquineBeast Mount { get; set; }
          }
          در این حالت حتی نیازی به تعریف HasConversion هم نیست.


          نوشتن تبدیلگر خودکار مقادیر خواص، به نمونه‌ای رمزنگاری شده

          پس از آشنایی با مفهوم «تبدیلگرهای مقدار» در +EF Core 2.1، اکنون می‌توانیم یک نمونه‌ی سفارشی از آن‌را نیز طراحی کنیم:
          namespace DbConfig.Web.DataLayer.Context
          {
              public class MyAppContext : DbContext
              {
                // …
          
                  protected override void OnModelCreating(ModelBuilder builder)
                  {
                      var encryptedConverter = new ValueConverter<string, string>(
                         convertToProviderExpression: v => new string(v.Reverse().ToArray()), // encrypt
                         convertFromProviderExpression: v => new string(v.Reverse().ToArray()) // decrypt
                      );
          
                      // Custom application mappings
                      builder.Entity<ConfigurationValue>(entity =>
                      {
                          entity.Property(e => e.Value).IsRequired().HasConversion(encryptedConverter);
                      });
                  }
              }
          }
          در اینجا معکوس کردن رشته‌ها به عنوان الگوریتم ساده‌ی رمزنگاری اطلاعات انتخاب شده‌است. نحوه‌ی اعمال این ValueConverter جدید را نیز ملاحظه می‌کنید.
          می‌توان قسمت HasConversion را به صورت زیر خودکار کرد:
          ابتدا یک Attribute جدید را به نام Encrypted به برنامه اضافه می‌کنیم:
          using System;
          
          namespace Test
          {
              [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
              public sealed class EncryptedAttribute : Attribute
              { }
          }
          هدف از این Attribute خالی، صرفا نشانه گذاری خاصیت‌هایی است که قرار است به صورت رمزنگاری شده در بانک اطلاعاتی ذخیره شوند؛ مانند خاصیت Value زیر:
          namespace DbConfig.Web.DomainClasses
          {
              public class ConfigurationValue
              {
                  public int Id { get; set; }
                  public string Key { get; set; }
          
                  [Encrypted]
                  public string Value { get; set; }
              }
          }
          پس از آن، متد OnModelCreating را به صورت زیر اصلاح می‌کنیم تا به کمک Reflection و اطلاعات موجودیت‌های ثبت شده‌ی در سیستم، متد SetValueConverter را بر روی خواصی که دارای EncryptedAttribute هستند، به صورت خودکار فراخوانی کند:
          namespace DbConfig.Web.DataLayer.Context
          {
              public class MyAppContext : DbContext
              {
                  protected override void OnModelCreating(ModelBuilder builder)
                  {
                      var encryptedConverter = new ValueConverter<string, string>(
                         convertToProviderExpression: v => new string(v.Reverse().ToArray()), // encrypt
                         convertFromProviderExpression: v => new string(v.Reverse().ToArray()) // decrypt
                      );
          
                      foreach (var entityType in builder.Model.GetEntityTypes())
                      {
                          foreach (var property in entityType.GetProperties())
                          {
                              var attributes = property.PropertyInfo.GetCustomAttributes(typeof(EncryptedAttribute), false);
                              if (attributes.Any())
                              {
                                  property.SetValueConverter(encryptedConverter);
                              }
                          }
                      }
                  }


          تاثیر ValueConverter‌ها بر روی اعمال متداول کار با بانک اطلاعاتی

          از دیدگاه برنامه، ValueConverterهای تعریف شده، هیچگونه تاثیری را بر روی کوئری نوشتن و یا ثبت و ویرایش اطلاعات ندارند و عملکرد آن‌ها کاملا از دیدگاه سایر قسمت‌های برنامه مخفی است. برای مثال در برنامه، فرمان به روز رسانی خاصیت Value را با مقدار .A new value to test صادر کرده‌ایم (مقدار دهی متداول)، اما همانطور که ملاحظه می‌کنید، نمونه‌ی رمزنگاری شده‌ی آن به صورت خودکار در بانک اطلاعاتی درج شده‌است (پارامتر p0):
           Executed DbCommand (22ms) 
             [Parameters=[@p1='1', 
                          @p0='.tset ot eulav wen A' (Nullable = false) (Size = 4000)],
          CommandType='Text', CommandTimeout='180']
          SET NOCOUNT ON;
          UPDATE [Configurations] SET [Value] = @p0
          WHERE [Id] = @p1;
          SELECT @@ROWCOUNT;

          و یا کوئری زیر
           db.Set<ConfigurationValue>().Where(x => x.Value.EndsWith("world!"))
          به این نحو ترجمه خواهد شد:
          SELECT [x].[Id], [x].[Key], [x].[Value]
          FROM [Configurations] AS [x]
          WHERE RIGHT([x].[Value], LEN(N'world!')) = N'!dlrow'
          یعنی نیازی نیست تا مقداری را که در حال جستجوی آن هستیم، خودمان به صورت دستی رمزنگاری کرده و سپس در کوئری قرار دهیم. اینکار به صورت خودکار انجام می‌شود.
          مطالب
          کش کردن اطلاعات غیر پویا در ASP.Net - قسمت دوم

          قسمت قبل به IIS7‌ اختصاص داشت که شاید برای خیلی‌ها کاربرد نداشته باشد خصوصا اینکه برنامه نویس‌ها ترجیح می‌دهند به روش‌هایی روی بیاورند که کمتر نیاز به دخالت مدیر سرور داشته باشد؛ یا زمانیکه سایت شما بر روی یک هاست اینترنتی قرار گرفته است عملا شاید دسترسی خاصی به تنظیمات IIS نداشته باشید (مگر اینکه یک هاست اختصاصی را تهیه کنید).
          برای IIS6 و ماقبل از آن و حتی بعد از آن!، حداقل دو روش برای کش کردن اطلاعات استاتیک وجود دارد:

          الف) استفاده از web resources معرفی شده در ASP.Net 2.0 به بعد
          در مورد نحوه‌ی تعریف و بکارگیری web resources می‌توان به مقاله "تبدیل پلاگین‌های jQuery‌ به کنترل‌های ASP.Net" رجوع کرد.


          همانطور که در شکل فوق نیز ملاحظه می‌کنید، هدر مربوط به مدت زمان منقضی شدن کش سمت کلاینت یک web resource توسط موتور ASP.Net به صورت خودکار به سال 2010 تنظیم شده است و این مقدار خالی نیست.

          ب) افزودن این هدر به صورت دستی

          برای این منظور باید در نحوه‌ی ارائه فایل‌های استاتیک دخالت کنیم و این‌کار را با استفاده از یک generic handler می‌توان انجام داد.


          کد این generic handler می‌تواند به صورت زیر باشد:

          using System;
          using System.IO;
          using System.Web;
          using System.Web.Services;
          using System.Reflection;

          namespace test1
          {
          [WebService(Namespace = "http://tempuri.org/")]
          [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
          public class cache : IHttpHandler
          {

          private static void cacheIt(TimeSpan duration)
          {
          HttpCachePolicy cache = HttpContext.Current.Response.Cache;

          FieldInfo maxAgeField = cache.GetType().GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic);
          maxAgeField.SetValue(cache, duration);

          cache.SetCacheability(HttpCacheability.Public);
          cache.SetExpires(DateTime.Now.Add(duration));
          cache.SetMaxAge(duration);
          cache.AppendCacheExtension("must-revalidate, proxy-revalidate");
          }

          public void ProcessRequest(HttpContext context)
          {
          string file = context.Request.QueryString["file"];
          if (string.IsNullOrEmpty(file))
          {
          return;
          }

          string contetType = context.Request.QueryString["contetType"];
          if (string.IsNullOrEmpty(contetType))
          {
          return;
          }

          context.Response.Write(File.ReadAllText(context.Server.MapPath(file)));

          //Set the content type
          context.Response.ContentType = contetType;

          // Cache the resource for 30 Days
          cacheIt(TimeSpan.FromDays(30));
          }

          public bool IsReusable
          {
          get
          {
          return false;
          }
          }
          }
          }
          توضیحات:
          این generic handler دو کوئری استرینگ را دریافت می‌کند؛ file جهت دریافت نام فایل و contetType جهت مشخص سازی نوع محتوایی که باید سرو شود؛ مثلا جاوا اسکریپت یا استایل شیت و امثال آن. سپس زمانیکه محتوا را Response.Write می‌کند، هدر مربوط به کش شدن آن‌را نیز به 30 روز تنظیم می‌نماید.
          تابع مربوط به کش کردن اطلاعات از مقاله ASP.NET Ajax Under-the-hood Secrets استخراج شد.

          روش استفاده در مورد فایل‌های CSS
          بجای تعریف یک فایل CSS در صفحه، به صورت استاندارد، اکنون تعریف متداول را به صورت زیر اصلاح کنید:

          <link type="text/css" href="cache.ashx?v=1&file=site.css&contetType=text/css" rel="Stylesheet" />
          هر زمانیکه که فایل site.css درخواست می‌شود، باید از فیلتر ما عبور کند و سپس ارائه گردد. در این حین، هدر مربوط به مدت زمان کش شدن سمت کلاینت به آن اضافه می‌شود. از کوئری استرینگ مربوط v هم جهت به روز رسانی‌های بعدی استفاده می‌شود تا اگر تغییری را اعمال کردیم، کلاینت حتما با توجه به آدرس جدید، محتویات جدید را یکبار دیگر دریافت کند. (مرورگر آدرس‌های مشابه را در صورتیکه هدر مربوط به کش شدن آن‌ها تنظیم شده باشد، از کش خواهد خواند و کاری به آخرین تغییرات شما در سرور ندارد)

          روش استفاده در مورد فایل‌های JS
          <script type="text/javascript" src="cache.ashx?v=1&file=js/jquery-1.3.2.min.js&contetType=application/x-javascript"></script>
          اکنون اگر سایت را مجددا با افزونه YSlow بررسی کنیم، می‌توان این هدر جدید را مشاهده کرد: