The document has no pages.
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>@ViewBag.Title</title> <link href="~/Content/PersianDatePicker.css" rel="stylesheet" /> </head> <body> @RenderBody() <script src="~/Scripts/jquery-1.10.2.min.js"></script> <script src="~/Scripts/PersianDatePicker.js"></script> @RenderSection("Scripts", required: false) </body> </html>
@using System.Globalization @model DateTime? @{ Func<DateTime, string> toPersianDate = date => { var dateTime = new DateTime(date.Year, date.Month, date.Day, new GregorianCalendar()); var persianCalendar = new PersianCalendar(); return persianCalendar.GetYear(dateTime) + "/" + persianCalendar.GetMonth(dateTime).ToString("00") + "/" + persianCalendar.GetDayOfMonth(dateTime).ToString("00"); }; var today = toPersianDate(DateTime.Now); var name = this.ViewContext.ViewData.ModelMetadata.PropertyName; var value = Model.HasValue ? toPersianDate(Model.Value) : string.Empty; } <input type="text" dir="ltr" name="@name" id="@name" value="@value" onclick="PersianDatePicker.Show(this,'@today');" />
// Location: Views\Shared\EditorTemplates\PersianDatePicker.cshtml [UIHint("PersianDatePicker")] public DateTime AddDate { set; get; }
<div> <label>تاریخ:</label> @Html.EditorFor(model => model.AddDate) </div>
همچنین اگر نیاز است مقدار رشته تاریخ شمسی آن به یک DateTime میلادی انتساب داده شود (در حین ارسال اطلاعات به سرور)، باید از یک model binder سفارشی برای اینکار در ASP.NET MVC استفاده کنید. یک نمونه پیاده سازی شده آنرا در بحث PersianDateModelBinder میتوانید مشاهده و استفاده کنید.
مروری سریع بر اصول مقدماتی MVVM
از مطالب خوبتون تشکر می کنم. دیگه تقریبا مشتری ثابت و ساعتی سایتتون شدم.
wpf & mvvm هنوز تبدیل به یه ابزار کامل برای تولید یه برنامه کاربردی حرفه ای نشده. من پوستم کنده شد تا تونستم یه برنامه کامل باهاش بنویسم. واسه راتباطش با ssrs چه مشقاتی که نکشیدم.
ناچار شدم یه فرم ویندوزی به برنامه اضافه کنم و گزارش را توی اون نمایش بدم.
راهم درسته فعلا؟ در نسخه 2011 فکری به حال این مشکل نشده؟
راستی چطور میشه توسط یه کلید و بدون نوشتم کد یه ویو جدید را نمایش داد. راهی برای بایند کردن کلید به ویو وجود داره. بدون نوشتن Command ?
آیا با وجود سیاماس فروشگاهی قدرتمندی مثل nopCommerce یا SmartStore آیا منطقی است که ما دوباره خودمان از صفر کد بزنیم؟
استفاده مجدد از پروژههای موجود که تعداد زیادی نصب از آن زیر بار است ارزانتر و سریعتر از «نوشتن از صفر» به نظر میرسد. خیلی از اوقات نوشتن از صفر به معنای اتلاف منابع است.
یک فایل web.config معمولی را در نظر بگیرید (اطلاعات غیر ضروری حذف شده اند).
<?xml version="1.0" encoding="utf-8"?> <!-- A bunch of ASP.NET MVC web config stuff goes here . . . --> <configuration> <connectionStrings> <add name="DefaultConnection" value="YourConnectionStringAndPassword"/> </connectionStrings> <appSettings file="PrivateSettings.config"> <add key="owin:AppStartup" value="AspNetIdentity2ExtendingApplicationUser.Startup,AspNetIdentity2ExtendingApplicationUser" /> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="EMAIL_PASSWORD" value="YourEmailPassword"/> </appSettings> </configuration>
همچنین کلمه عبور یک ایمیل هم وجود دارد که نمیخواهیم به مخازن سورس کنترل ارسال شود، و مجددا ممکن است مقدارش بین توسعه دهندگان متفاوت باشد.
از طرفی بسیاری از تنظیمات این فایل متعلق به کل اپلیکیشن است، بنابراین صرفنظر کردن از کل فایل web.config در سورس کنترل گزینه جالبی نیست.
خوشبختانه کلاس ConfigurationManager راه حل هایی پیش پای ما میگذارد.
استفاده از خاصیت configSource برای انتقال قسمت هایی از تنظیمات به فایلی مجزا
با استفاده از خاصیت configSource میتوانیم قسمتی از تنظیمات (configuration section) را به فایلی مجزا منتقل کنیم. بعنوان مثال، رشتههای اتصال از مواردی هستند که میتوانند بدین صورت تفکیک شوند.
بدین منظور میتوانیم فایل تنظیمات جدیدی (مثلا با نام connectionStrings.config) ایجاد کنیم و سپس با استفاده از خاصیت نام برده در فایل web.config به آن ارجاع دهیم. برای این کار فایل تنظیمات جدیدی ایجاد کنید و مقادیر زیر را به آن اضافه کنید (xml header یا هیچ چیز دیگری نباید در این فایل وجود داشته باشد، تنها مقادیر تنظیمات).
<connectionStrings> <add name="DefaultConnection" value="YourConnectionStringAndPassword"/> </connectionStrings>
<connectionStrings configSource="ConnectionStrings.config"> </connectionStrings>
var conn = ConfigurationManager.ConnectionStrings["DefaultConnection"]; string connString = conn.ConnectionString; // etc.
استفاده از خاصیت file برای انتقال بخشی از تنظیمات به فایلی مجزا
ممکن است فایل تنظیمات شما (مثلا web.config) شامل مقادیری در قسمت <appSettings> باشد که برای کل پروژه تعریف شده اند (global) اما برخی از آنها محرمانه هستند و باید از سورس کنترل دور نگاه داشته شوند. در این سناریوها خاصیتی بنام file وجود دارد که مختص قسمت appSettings است و به ما اجازه میدهد مقادیر مورد نظر را به فایلی مجزا انتقال دهیم. هنگام دسترسی به مقادیر این قسمت تمام تنظیمات بصورت یکجا خوانده میشوند.
در مثال جاری یک کلمه عبور ایمیل داریم که میخواهیم محرمانه بماند. بدین منظور میتوانیم فایل پیکربندی جدیدی مثلا با نام PrivateSettings.config ایجاد کنیم. این فایل هم نباید xml header یا اطلاعات دیگری داشته باشد، تنها مقادیر appSettings را در آن نگاشت کنید.
<appSettings> <add key="MAIL_PASSWORD" value="xspbqmurkjadteck"/> </appSettings>
<appSettings file="PrivateSettings.config"> <add key="owin:AppStartup" value="AspNetIdentity2ExtendingApplicationUser.Startup,AspNetIdentity2ExtendingApplicationUser" /> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> </appSettings>
var pwd = ConfigurationManager.AppSettings["MAIL_PASSWORD"];
فایلهای ویژه را به gitignore. اضافه کنید
حال میتوانیم فایل web.config را به سورس کنترل اضافه کنیم، فایلهای ConnectionStrings.config و PrivateSettings.config را به فایل gitignore. اضافه کنیم و پروژه را commit کنیم. در این صورت فایلهای تنظیمات خصوصی به مخازن سورس کنترل ارسال نخواهند شد.
مستند سازی را فراموش نکنید!
مسلما اگر چنین رویکردی را در پیش بگیرید باید دیگران را از آن مطلع کنید (مثلا با افزودن توضیحاتی به فایل README.txt). بهتر است در فایل web.config خود هرجا که لازم است توضیحات XML خود را درج کنید و به توسعه دهندگان توضیح دهید که چه فایل هایی را روی نسخههای محلی خود باید ایجاد کنند و هر کدام از این فایلها چه محتوایی باید داشته باشند.
- بهینه سازی زمان آغاز به کار برنامه
- کاهش صفحات کاری برنامه: از آنجا که برنامه از قبل کامپایل شده، فراهم کردن صفحه بندی از ابتدای کار امر چندان دشواری نخواهد بود؛ لذا در این حالت صفحه بندی حافظه به صورت پویاتری انجام میگردد. شیوهی کار به این صورت است که اسمبلیها به چندین پروسهی کاری کوچکتر تبدیل شده تا صفحه بندی هر کدام جدا صورت گیرد و محدودهی صفحه بندی کوچکتر میشود. در نتیجه کمتر نقصی در صفحه بندی دیده شده یا کلا دیده نخواهد شد. نتیجهی کار هم در یک فایل ذخیره میگردد که این فایل میتواند نگاشت به حافظه شود تا این قسمت از حافظه به طور اشتراکی مورد استفاده قرار گیرد و بدین صورت نیست که هر پروسهای برای خودش قسمتی را گرفته باشد.
%SystemRoot%\Assembly\NativeImages_v4.0.#####_64
معایب
احتمالا شما پیش خود میگویید این مورد فوق العاده امکان جالبی هست. کدها از قبل تبدیل شدهاند و دیگر فرآیند جیت صورت نمیگیرد. در صورتیکه ما تمامی امکانات یک CLR مثل مدیریت استثناءها و GC و ... را داریم، ولی غیر از این یک مشکلاتی هم به کارمان اضافه میشود که در زیر به آنها اشاره میکنیم:
عدم محافظت از کد در برابر بیگانگان: بعضیها تصور میکنند که این کد را میتوانند روی ماشین شخصی خود کامپایل کرده و فایل ngen را همراه با آن ارسال کنند. در این صورت کد IL نخواهد بود ولی موضوع این هست اینکار غیر ممکن است و هنوز استفاده از اطلاعات متادیتاها پابرجاست به خصوص در مورد اطلاعات چون reflection و serializationها . پس کد IL کماکان همراهش هست. نکتهی بعدی اینکه انتقال هم ممکن نیست؛ بنا به شرایطی که در مورد بعدی دلیل آن را متوجه خواهید شد.
از سینک با سیستم خارج میشوند: موقعیکه CLR، اسمبلیها را به داخل حافظه بار میکند، یک سری خصوصیات محیط فعلی را با زمانیکه عملیات تبدیل IL به کد ماشین صورت گرفته است، چک میکند. اگر این خصوصیات هیچ تطابقی نداشته باشند، عملیات JIT همانند سابق انجام میگردد. خصوصیات و ویژگیهایی که چک میشوند به شرح زیر هستند:
- ورژن CLR: در صورت تغییر، حتی با پچها و سرویس پک ها.
- نوع پردازنده: در صورت تغییر پردازنده یا ارتقا سخت افزاری.
- نسخه سیستم عامل : ارتقاء با سرویس پک ها.
- MVID یا Assemblies Identity module Version Id: در صورت کامپایل مجدد تغییر میکند.
- Referenced Assembly's version ID: در صورت کامپایل مجدد اسمبلی ارجاع شده.
- تغییر مجوزها: در صورتی که تغییری نسبت به اولین بار رخ دهد؛ مثلا در قسمت قبلی در مورد اجازه نامه اجرای کدهای ناامن صحبت کردیم. برای نمونه اگر در همین اجازه نامه تغییری رخ دهد، یا هر نوع اجازه نامه دیگری، برنامه مثل سابق (جیت) اجرا خواهد شد.
پی نوشت: در آپدیتهای دات نت فریم ورک به طور خودکار ابزار ngen صدا زده شده و اسمبلیها مجددا کمپایل و دخیره میشوند و برنامه سینک و آپدیت باقی خواهد ماند.
پس حتما نسخهی ngen شده و غیر ngen را بررسی کنید و کارآیی هر دو را با هم مقایسه کنید. برای بسیاری از برنامهها کاهش صفحه بندی یک مزیت و باعث بهبود کارآیی میشود. در نتیجه در این قسمت ngen برنده اعلام میشود.
توجه کنید برای سیستمهایی که در سمت سرور به فعالیت میپردازند، از آنجا که تنها اولین درخواست برای اولین کاربر کمی زمان میبرد و برای باقی کاربران درخواست با سرعت بالاتری اجرا میگردد و اینکه برای بیشتر برنامههای تحت سرور از آنجا که تنها یک نسخه در حال اجراست، هیچ مزیت صفحه بندی را ngen ایجاد نمیکند.
برای بسیاری از برنامههای کلاینت که تجربهی startup طولانی دارند، مایکروسافت ابزاری را به نام Managed Profile Guided Optimization Tool یا MPGO .exe دارد. این ابزار به تحلیل اجرای برنامه شما پرداخته و بررسی میکند که در زمان آغازین برنامه چه چیزهایی نیاز است. اطلاعات به دست آمده از تحلیل به سمت ngen فرستاده شده تا کد بومی بهینهتری تولید گردد. موقعیکه شما آماده ارائه برنامه خود هستید، برنامه را از طریق این تحلیل و اجرا کرده و با قسمتهای اساسی برنامه کار کنید. با این کار اطلاعاتی در مورد اجرای برنامه در داخل یک پروفایل embed شده در اسمبلی، قرار گرفته و ngen موقع تولید کد، این پروفایل را جهت تولید کد بهینه مطالعه خواهد کرد.
در مقالهی بعدی در مورد FCL صحبتهایی خواهیم کرد.
- ترکیب رنگی زیبا و طراحی متریال محیط در عکس زیر قابل مشاهده میباشد
⌘⇧a → "Plugins" → ↩ → ⌃⌥b → <search> → "Material Theme UI" → [Install plugin] → ⌃⌥c → ⎋ → <restart>
۲. تنظیمات Editor Scheme :
⌘⇧a → "Color Scheme" → [3. ChroMATERIAL]
⌘⇧a → "Android Logcat" → [Save as...] → "ChroMATERIAL + HOLO"¹ → ↩ → <set foreground colors as in the table ↑> → ⎋
Type : Color verbose: #BBB debug: #33B5E5 info: #9C0 assert: #A6C error: #F44 warning: #FB3
پروژه ای سورس باز برای نوشتن اپلیکیشن هایی که با الگوی CQRS و Eventsourcing پیاده میشوند . در مقایسه با SimpleCQRS به روزتر با قابلیتهای مدرنتر شده و از .Net Core هم پشتیبانی میکند.
مقدمه:
مدیریت آزمون مایکروسافت یا Microsoft Test Manager یک ابزار تست نویسی است که به تسترها این اجازه را میدهد تا بتوانند برای UI برنامههای خود یا sprintهای پروژه خود تست بنویسند. این ابزار برای نوشتن آزمونهای پیشرفته و مجتمع سازی مدیریت طرحهای تست یا test plans همراه با موردهای تست یا test case در طول توسعه برنامه است. یکی از مزایایی که این ابزار دارد این است که در طول انجام تست میتوانید اشکالات تست را ثبت کنید و هم چنین میتوانید شرحی در مورد انجام تست یا اشکالی که در آن تست وجود دارد، ثبت کنید. همچنین میتوانید گزارشی از تست هایی که انجام داده اید و پاس شدن یا پاس نشدن تستها و تاریخ انجام آنها را نیز مشاهده کنید. قبل از کار با نرم افزار MTM باید یک سری مطالب مهم را در مورد انجام تست و مفهوم Agile بدانیم.
استراتژی تست:
زمانی که شما تست Agile را معرفی میکنید تیم برنامه نویسی شما میتواند بر روی تستهای شما هم در سطح sprint و هم در سطح پروژه تمرکز کنند. تست در سطح sprint شامل تست هایی میشود که همه user storyها در بر بگیرد یعنی در واقع همان تستهای واحد شما میشود. در سطح پروژه هم شامل تست هایی میشود که چندین sprint را در بر میگیرد که در واقع میتوان تستهای integrated گفت. بهتر است زمانی که تیم برنامه نویسی کدنویسی میکنند شما طرح تستهای خود را بسازید و برای انجام تست کاملا آماده باشید. این تستها شامل تست واحد، تست performance، تست امنیتی و تست usability و غیره میباشد.
برای آماده کردن تست Agile در ابتدا شما باید یک تاریخچه یا history از برنامه یا سیستم خود داشته باشید. شما میتوانید با استفاده از Microsoft Test Manager طرح تست خود را برای هر یک از sprintهای پروژتان بسازید و موردهای تست را مشخص کنید.
سپس باید کدهایی که برنامه نویسان مینویسند قابلیت تست را داشته باشند و شما به عنوان یک تستر باید آشنایی کاملی از ساختار و الگوهای برنامه تان داشته باشید.
تست یک فرآیند تکراری میباشد که همزمان با اجرای پروژه تان صورت میگیرد در زیر میتوانید فرآیند کار تست و انجام کدنویسی را مشاهده نمایید:
Test Planning:
Test Planning فرآیندی است که به تیم شما کمک میکند تا درک درستی از پروژه داشته باشند و همچنین تیم را برای انجام هر گونه تستی آماده کند. تست Agile در سطح Sprint انجام میشود که در هر Sprint تیم شما تست هایی را ایجاد میکنند تا user story هایی که در هر Sprint وجود دارد، مورد بررسی قرار گیرند. در شکل زیر قالبی از test planهای شما در یک پروژه را نمایش میدهد:
البته این قالبها بر اساس سلیقه شخصی است اما در کل میتوانیم قالب تست را به صورت بالا در نظر بگیریم.
همیشه باید این را در نظر داشته باشیم که در طول هر sprint حتما باید تستها را اجرا کرده و در صورت وجود خطا، آن خطا را رفع کنیم تا در مراحل بالاتر با مشکلی مواجه نشویم. در قسمت بعد با Microsoft Test Manager و روشهای نوشتن sprint و تستها آشنا خواهیم شد.
مرور مثال این قسمت
تا اینجا در مثالی که بررسی کردیم، لیست اتاقها توسط کامپوننت IndividualRoom.razor و لیست خدمات رفاهی یک هتل توسط کامپوننت IndividualAmenity.razor در کامپوننت والد DemoHotel.razor، نمایش داده شدهاند:
دکمههای حذف و ویرایش هر اتاق نیز در کامپوننت EditDeleteButton.razor قرار دارند که توسط کامپوننت IndividualRoom.razor مورد استفاده قرار میگیرند.
اکنون میخواهیم با کلیک بر روی دکمهی حذف کامپوننت EditDeleteButton، یک modal بوت استرپی جهت دریافت تائیدیهی عملیات، نمایش داده شود و در صورت تائید آن، اتاق انتخابی از لیست اتاقهای کامپوننت DemoHotel حذف گردد.
بنابراین در ابتدا کامپوننت EditDeleteButton، به کامپوننت IndividualRoom خبر درخواست حذف یک اتاق را میدهد. سپس کامپوننت IndividualRoom، یک مودال دریافت تائیدیهی حذف را نمایش میدهد. پس از تائید حذف توسط کاربر، این رویداد به کامپوننت DemoHotel، جهت حذف اتاق انتخابی از لیست اتاقها، اطلاع رسانی خواهد شد.
ایجاد کامپوننت مودال دریافت تائید
در ابتدا، فایل جدید Pages\LearnBlazor\LearnBlazorComponents\Confirmation.razor را ایجاد کرده و به صورت زیر تکمیل میکنیم:
@if (ShowModal) { <div class="modal-backdrop show"></div> <div class="modal fade show" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style="display: block;"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title"> @Title </h5> <button @onclick="OnCancelClicked" type="button" class="close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> @ChildContent </div> <div class="modal-footer"> <button @onclick="OnCancelClicked" type="button" class="btn btn-secondary">@CancelButtonLabel</button> <button @onclick="OnConfirmClicked" type="button" class="btn btn-primary">@OkButtonLabel</button> </div> </div> </div> </div> } @code { private bool ShowModal; [Parameter] public string Title { get; set; } = "Confirm"; [Parameter] public string CancelButtonLabel { get; set; } = "Cancel"; [Parameter] public string OkButtonLabel { get; set; } = "Ok"; [Parameter] public RenderFragment ChildContent { get; set; } [Parameter] public EventCallback OnConfirm { get; set; } [Parameter] public EventCallback OnCancel { get; set; } public void Show() => ShowModal = true; public void Hide() => ShowModal = false; private async Task OnConfirmClicked() { ShowModal = false; await OnConfirm.InvokeAsync(); } private async Task OnCancelClicked() { ShowModal = false; await OnCancel.InvokeAsync(); } }
- در اینجا در ابتدا تگها و کلاسهای مرتبط با نمایش یک modal استاندارد بوت استرپی را مشاهده میکنید.
- اگر فیلد خصوصی ShowModal به false تنظیم شود، چون کل محتوای این کامپوننت از DOM حذف خواهد شد (اثر if@ تعریف شده)، سبب مخفی شدن و عدم نمایش آن میگردد.
- این کامپوننت عنوان و برچسبهای دکمههای خودش را به صورت پارامتر دریافت میکند.
- برای اینکه بتوان محتوای نمایشی این کامپوننت را پویا کرد، از یک RenderFragment استفاده کردهایم:
[Parameter] public RenderFragment ChildContent { get; set; }
- همچنین میخواهیم به کامپوننت فراخوان این امکان را بدهیم تا بتواند به صورت مستقل، سبب نمایش یا مخفی شدن وهلهای از این کامپوننت شود. به همین جهت دو متد عمومی Show و Hide نیز تعریف شدهاند.
هدایت درخواست Delete به کامپوننت نمایش مشخصات اتاق
با توجه به اینکه دکمههای حذف و ویرایش هر اتاق، در کامپوننت Pages\LearnBlazor\LearnBlazorComponents\EditDeleteButton.razor قرار دارند، به آن مراجعه کرده و امکان انتشار این رخداد را به فراخوان آن، با تعریف رویداد OnDelete میدهیم:
@if (IsAdmin) { <input type="button" class="btn btn-danger" value="Delete" @onclick="OnDelete" /> <input type="button" class="btn btn-success" value="Edit" /> } @code { [Parameter] public bool IsAdmin { get; set; } [Parameter] public EventCallback OnDelete { get; set; } }
کامپوننت Pages\LearnBlazor\LearnBlazorComponents\IndividualRoom.razor که نمایش دهندهی جزئیات هر اتاق است، با مدیریت رویداد OnDelete کامپوننت EditDeleteButton، از درخواست حذف اتاق جاری مطلع میشود:
<EditDeleteButton IsAdmin="true" OnDelete="OnDeleteClicked"></EditDeleteButton> <Confirmation @ref="Confirmation1" OnCancel="OnCancelClicked" OnConfirm="@(() => OnDeleteSelectedRoom.InvokeAsync(Room))"> <div> Do you want to delete `@Room.Name`? </div> </Confirmation>
- سپس نیاز است زمانیکه OnDelete کامپوننت EditDeleteButton رخداد، این modal دریافت تائید را نمایش دهیم. به همین جهت باید بتوانیم متد عمومی Show آنرا فراخوانی کنیم. بنابراین از ref@ برای دسترسی به وهلهای از این کامپوننت تعریف شده استفاده کردهایم تا توسط شیء Confirmation1، بتوانیم متد عمومی Show را در رویدادگردان منتسب به OnDelete فراخوانی کنیم.
- همچنین دو رویداد OnCancel و OnConfirm کامپوننت دریافت تائید را به متد خصوصی OnCancelClicked و رویداد جدید OnDeleteSelectedRoom متصل کردهایم. یعنی زمانیکه کاربر بر روی دکمهی OK مودال ظاهر شده کلیک میکند، Room جاری، از طریق رویداد OnDeleteSelectedRoom به فراخوان کامپوننت IndividualRoom ارسال میشود تا دقیقا بداند که چه اتاقی را بایدحذف کند:
@code { Confirmation Confirmation1; [Parameter] public BlazorRoom Room { get; set; } [Parameter] public EventCallback<BlazorRoom> OnDeleteSelectedRoom { get; set; } void OnDeleteClicked() { Confirmation1.Show(); } void OnCancelClicked() { // Confirmation1.Hide(); } // ... }
حذف اتاق انتخابی در کامپوننت نمایش لیست اتاقها
مرحلهی آخر این مثال، بسیار سادهاست. در حلقهای که هر اتاق را توسط کامپوننت IndividualRoom نمایش میدهد، به رویداد OnDeleteSelectedRoom گوش فرا داده و selectedRoom یا همان BlazorRoom ارسالی را، دریافت و از لیست Rooms کامپوننت جاری حذف میکنیم. این حذف شدن، بلافاصله سبب رندر مجدد UI و حذف آن از رابط کاربری نیز خواهد شد:
@foreach (var room in Rooms) { <IndividualRoom OnRoomCheckBoxSelection="RoomSelectionCounterChanged" Room="room" OnDeleteSelectedRoom="@(selectedRoom => Rooms.Remove(selectedRoom))"> </IndividualRoom> }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-12.zip