PM> Install-Package DNTFrameworkCore.Web
- Refactor کردن فرمهای ثبت و ویرایش مرتبط با یک Aggregate، به یک PartialView که با یک ViewModel کار میکند. برای موجودیتهای ساده و پایه، همان Model/DTO، به عنوان Model متناظر با یک ویو یا به اصطلاح ViewModel استفاده میشود؛ ولی برای سایر موارد، از مدلی که نام آن با نام موجودیت + کلمه ModalViewModel یا FormViewModel تشکیل میشود، استفاده خواهیم کرد.
- یک فرم، در قالب یک پارشالویو، به صورت Ajaxای با استفاده از افزونه jquery-unobtrusive-ajax بارگذاری شده و به سرور ارسال خواهد شد.
- یک فرم براساس طراحی خود میتواند در قالب یک مودال باز شود، یا به منظور inline-editing آن را بارگذاری و به قسمتی از صفحه که مدنظرتان میباشد اضافه شود.
- وجود ویو Index به همراه پارشالویو _List برای نمایش لیستی و یک پارشالویو برای عملیات ثبت و ویرایش الزامی میباشد. البته اگر از مکانیزمی که در مطلب « طراحی یک گرید با jQuery Ajax و ASP.NET MVC به همراه پیاده سازی عملیات CRUD» مطرح شد، استفاده نمیکنید و نیاز دارید تا اطلاعات صفحهبندی شده، مرتب شده و فیلتر شدهای را در قالب JSON دریافت کنید، از اکشنمتد ReadPagedList کنترلر پایه استفاده کنید.
public class BlogsController : CrudController<IBlogService, int, BlogModel> { public BlogsController(IBlogService service) : base(service) { } protected override string CreatePermissionName => PermissionNames.Blogs_Create; protected override string EditPermissionName => PermissionNames.Blogs_Edit; protected override string ViewPermissionName => PermissionNames.Blogs_View; protected override string DeletePermissionName => PermissionNames.Blogs_Delete; protected override string ViewName => "_BlogModal"; }
<form asp-action="@(Model.IsNew() ? "Create" : "Edit")" asp-controller="Blogs" asp-modal-form="BlogForm"> <div> <input type="hidden" name="save-continue" value="true"/> <input asp-for="RowVersion" type="hidden"/> <input asp-for="Id" type="hidden"/> <div> <div> <label asp-for="Title"></label> <input asp-for="Title" autocomplete="off"/> <span asp-validation-for="Title"></span> </div> </div> <div> <div> <label asp-for="Url"></label> <input asp-for="Url" type="url"/> <span asp-validation-for="Url"></span> </div> </div> </div> ... </form>
<div> <a asp-modal-delete-link asp-model-id="@Model.Id" asp-modal-toggle="false" asp-controller="Blogs" asp-action="Delete" asp-if="!Model.IsNew()" asp-permission="@PermissionNames.Blogs_Delete" title="Delete Blog"> <i></i> </a> <a title="Refresh Blog" asp-if="!Model.IsNew()" asp-modal-link asp-modal-toggle="false" asp-controller="Blogs" asp-action="Edit" asp-route-id="@Model.Id"> <i></i> </a> <a title="New Blog" asp-modal-link asp-modal-toggle="false" asp-controller="Blogs" asp-action="Create"> <i></i> </a> <button type="button" data-dismiss="modal"> <i></i> Cancel </button> <button type="submit"> <i></i> Save Changes </button> </div>
public class RoleModalViewModel : RoleModel { public IReadOnlyList<LookupItem> PermissionList { get; set; } }
protected override IActionResult RenderView(RoleModel role) { var model = _mapper.Map<RoleModalViewModel>(role); model.PermissionList = ReadPermissionList(); return PartialView(ViewName, model); }
برای مدیریت سناریوهای Master-Detail به مانند قسمت مدیریت دسترسیها در تب Permissions فرم بالا، امکاناتی در زیرساخت تعبیه شده است ولی پیادهسازی آن را به عنوان یک تمرین و با توجه به سری مطالب «Editing Variable Length Reorderable Collections in ASP.NET MVC» به شما واگذار میکنم.
نکته تکمیلی: برای ارسال اطلاعات اضافی به ویو Index متناظر با یک موجودیت میتوانید متد RenderIndex را به شکل زیر بازنویسی کنید:
protected override IActionResult RenderIndex(IPagedQueryResult<RoleReadModel> model) { var indexModel = new RoleIndexViewModel { Items = model.Items, TotalCount = model.TotalCount, Permissions = ReadPermissionList() }; return Request.IsAjaxRequest() ? (IActionResult) PartialView(indexModel) : View(indexModel); }
مدل RoleIndexViewModel استفاده شده در تکه کد بالا نیز به شکل زیر خواهد بود:
public class RoleIndexViewModel : PagedQueryResult<RoleReadModel> { public IReadOnlyList<LookupItem> Permissions { get; set; } }
فرآیند بارگذاری یک پارشالویو در مودال
به عنوان مثال برای استفاده از مودالهای بوت استرپ، ایده کار به این شکل است که یک مودال را به شکل زیر در فایل Layout قرار دهید:
<div class="modal fade" @*tabindex="-1"*@ id="main-modal" data-keyboard="true" data-backdrop="static" role="dialog" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-content"> <div class="modal-body"> Loading... </div> </div> </div> </div>
سپس در زمان کلیک بروی یک دکمه Ajaxای، ابتدا main-modal را نمایش داده و بعد از دریافت پارشالویو از سرور، آن را با محتوای modal-content جایگزین میکنیم. به همین دلیل Tag Halperهای مطرح شده در مطلب جاری، callbackهای failure/complete/success متناظر با unobtrusive-ajax را نیز مقداردهی میکنند. برای این منظور نیاز است تا متدهای جاوااسکریپتی زیر نیز در سطح شیء window تعریف شده باشند:
/*---------------------------------- asp-modal-link ---------------------------*/ window.handleModalLinkLoaded = function (data, status, xhr) { prepareForm('#main-modal.modal form'); }; window.handleModalLinkFailed = function (xhr, status, error) { //.... }; /*---------------------------------- asp-modal-form ---------------------------*/ window.handleModalFormBegin = function (xhr) { $('#main-modal a').addClass('disabled'); $('#main-modal button').attr('disabled', 'disabled'); }; window.handleModalFormComplete = function (xhr, status) { $('#main-modal a').removeClass('disabled'); $('#main-modal button').removeAttr('disabled'); }; window.handleModalFormSucceeded = function (data, status, xhr) { if (xhr.getResponseHeader('Content-Type') === 'text/html; charset=utf-8') { prepareForm('#main-modal.modal form'); } else { hideMainModal(); } }; window.handleModalFormFailed = function (xhr, status, error, formId) { if (xhr.status === 400) { handleBadRequest(xhr, formId); } };
برای بررسی بیشتر، پیشنهاد میکنم پروژه DNTFrameworkCore.TestWebApp موجود در مخزن این زیرساخت را بازبینی کنید.
OpenCVSharp #14
BLOB یا Binary Large OBject به معنای گروهی از نقاط به هم پیوستهی در یک تصویر باینری هستند. Large در اینجا به این معنا است که اشیایی با اندازههایی مشخص، مدنظر هستند و اشیاء کوچک، به عنوان نویز درنظر گرفته خواهند شد و پردازش نمیشوند.
برای نمونه در تصویر ذیل، تصویر سمت چپ، تصویر اصلی است و تصویر سمت راست، نمونهی باینری سیاه و سفید آن. سپس عملیات تشخیص Blobs بر روی تصویر اصلی انجام شده و نتیجهی نهایی که مشخص کنندهی اشیاء تشخیص داده شدهاست، با دوایری قرمز در تصویر سمت راست، مشخص هستند.
معرفی کلاس SimpleBlobDetector
در کدهای ذیل، نحوهی کار با کلاس SimpleBlobDetector را مشاهده میکنید. این کلاس کار تشخیص Blobs را در تصویر اصلی انجام میدهد:
var srcImage = new Mat(@"..\..\Images\cvlbl.png"); Cv2.ImShow("Source", srcImage); Cv2.WaitKey(1); // do events var binaryImage = new Mat(srcImage.Size(), MatType.CV_8UC1); Cv2.CvtColor(srcImage, binaryImage, ColorConversion.BgrToGray); Cv2.Threshold(binaryImage, binaryImage, thresh: 100, maxval: 255, type: ThresholdType.Binary); var detectorParams = new SimpleBlobDetector.Params { //MinDistBetweenBlobs = 10, // 10 pixels between blobs //MinRepeatability = 1, //MinThreshold = 100, //MaxThreshold = 255, //ThresholdStep = 5, FilterByArea = false, //FilterByArea = true, //MinArea = 0.001f, // 10 pixels squared //MaxArea = 500, FilterByCircularity = false, //FilterByCircularity = true, //MinCircularity = 0.001f, FilterByConvexity = false, //FilterByConvexity = true, //MinConvexity = 0.001f, //MaxConvexity = 10, FilterByInertia = false, //FilterByInertia = true, //MinInertiaRatio = 0.001f, FilterByColor = false //FilterByColor = true, //BlobColor = 255 // to extract light blobs }; var simpleBlobDetector = new SimpleBlobDetector(detectorParams); var keyPoints = simpleBlobDetector.Detect(binaryImage); Console.WriteLine("keyPoints: {0}", keyPoints.Length); foreach (var keyPoint in keyPoints) { Console.WriteLine("X: {0}, Y: {1}", keyPoint.Pt.X, keyPoint.Pt.Y); } var imageWithKeyPoints = new Mat(); Cv2.DrawKeypoints( image: binaryImage, keypoints: keyPoints, outImage: imageWithKeyPoints, color: Scalar.FromRgb(255, 0, 0), flags: DrawMatchesFlags.DrawRichKeypoints); Cv2.ImShow("Key Points", imageWithKeyPoints); Cv2.WaitKey(1); // do events Cv2.WaitKey(0); Cv2.DestroyAllWindows(); srcImage.Dispose(); imageWithKeyPoints.Dispose();
در اینجا ابتدا تصویر منبع به صورتی متداول باگذاری شدهاست. سپس توسط متد CvtColor به نمونهای سیاه و سفید و به کمک متد Threshold به یک تصویر باینری تبدیل شدهاست.
اگر به تصویر ابتدای بحث دقت کنید، اشیاء موجود در منبع مفروض، رنگهای متفاوتی دارند؛ اما در تصویر نهایی تولید شده، تمام آنها تبدیل به اشیایی سفید رنگ شدهاند. این کار را متد Cv2.Threshold انجام دادهاست، تا کلاس SimpleBlobDetector قابلیت تشخیص بهتری را پیدا کند. تشخیص اشیایی یک دست، بسیار سادهتر هستند از تشخیص اشیایی با رنگها و شدت نور متفاوت.
اگر بخواهیم الگوریتم Threshold را بیان کنیم به pseudo code زیر خواهیم رسید:
if src(x,y) > thresh dst(x,y) = maxValue else dst(x,y) = 0
سپس پارامتر اصلی سازندهی کلاس SimpleBlobDetector مقدار دهی شدهاست. این تنظیمات در تشخیص اشیاء موجود در تصویر بسیار مهم هستند. برای درک بهتر واژههایی مانند Circularity، Convexity، Inertia و امثال آن، به تصویر ذیل دقت کنید:
در اینجا میتوان حداقل و حداکثر تقعر و تحدب اشیاء و یا حتی حداقل و حداکثر اندازهی اشیاء مدنظر را مشخص کرد.
اگر سازندهی کلاس SimpleBlobDetector به نال تنظیم شود، از مقادیر پیش فرض آن استفاده خواهند شد که در اینجا به معنای تشخیص اشیاء کدر دایرهای شکل هستند. به همین جهت در تنظیمات فوق FilterByColor به false تنظیم شدهاست تا اشکال سفید رنگ تصویر اصلی را بتوان تشخیص داد.
در ادامه متد simpleBlobDetector.Detect بر روی تصویر باینری بهبود داده شده، فراخوانی گشتهاست. خروجی آن نقاط کلیدی اشیاء یافت شدهاست. این نقاط را میتوان توسط متد DrawKeypoints ترسیم کرد که حاصل آن دوایر قرمز رنگ موجود در مرکز اشیاء یافت شدهی در تصویر سمت راست هستند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
محدودیتهای نسخهگذاری در .NET
اشارهگرهای مدیریت شده در NET.
My last post investigated ways to build a .NET Core desktop/console app with a web-rendered UI without bringing in the full weight of Electron. This seems to have interested a lot of people, so I decided to upgrade it to newer technologies and add cross-platform support.
The result is a little NuGet package called WebWindow that you can add to any .NET Core console app. It can open a native OS window (Windows/Mac/Linux) containing web-based UI, without your app having to bundle either Node or Chromium.
namespace FirstProjectServices { public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { public string GetSiteName() { return "DNT"; } } } namespace FirstProjectServices { public interface IMessagesService2 { string GetSiteName(); } public class MessagesService2 : IMessagesService2 { public string GetSiteName() { return "DNT"; } } }
public class Startup { public void ConfigureServices(IServiceCollection services) { var collection = new ServiceCollection(); services.Scan(scan => scan // We start out with all types in the assembly of ITransientService .FromAssemblyOf<IMessagesService>() .AddClasses(classes => classes.AssignableTo<MessagesService>()) .AsImplementedInterfaces() .WithTransientLifetime() ); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessagesService2 _messagesService) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // app.UseDefaultFiles(); // app.UseStaticFiles(); app.Run(async (context) => { string siteName = _messagesService.GetSiteName(); await context.Response.WriteAsync($"Site Name {siteName}"); }); } }
مقدمهای بر NET MAUI.
An Introduction to .NET MAUI For Mobile Development
.NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating native mobile and desktop apps with C# and XAML.
.NET MAUI is open-source and is the evolution of Xamarin.Forms, extended from mobile to desktop scenarios, with UI controls rebuilt from the ground up for performance and extensibility. If you've previously used Xamarin.Forms to build cross-platform user interfaces, you'll notice many similarities with .NET MAUI. However, there are also some differences. Using .NET MAUI, you can create multi-platform apps using a single project, but you can add platform-specific source code and resources if necessary. One of the key aims of .NET MAUI is to enable you to implement as much of your app logic and UI layout as possible in a single code-base.
0:00 - Setup Visual Studio and MAUI Project
00:16:25 - Create MAUI Pages with C#
00:27:42 - Create MAUI Pages with XAML
00:32:28 - Explore MAUI Layouts
00:39:38 - Static Shared Resources
00:44:36 - Platform Specific Values
00:50:11 - Page Navigation