مطالب
گوش دادن به تغییرات تم ویندوز 10 بدون نیاز به WinRT در سی شارپ
امروز میخواستم برای یکی از پروژه‌هایم، قابلیتی را پیاده سازی کنم که هماهنگ با تم ویندوز، تم برنامه را عوض کند (تیره/روشن). به این منظور که وقتی تم ویندوز Dark می‌شد، تم برنامه‌ی من هم Dark بشود و برعکس. ساده‌ترین کار این بود که از کدهای WinRT که توسط بسته‌ی نیوگت SDK Contract ارائه میشود استفاده کرد. در این صورت کافیست فقط از کلاس ThemeManager استفاده کنیم و بدون کوچکترین خونریزی، برنامه را به این ویژگی مجهز کنیم😁 اما خب، هرچیزی هزینه‌ی خودش را دارد و من به شخصه علاقه‌ای به استفاده از 25 مگابایت، فقط برای شناسایی وضعیت تم ویندوز را ندارم! پس خودم دست به کار شدم تا یک Listener برای این منظور بنویسم.
در دات نت یکسری رخ‌داد وجود دارند که مربوط به سیستم عامل میشوند و از کلاس SystemEvents قابل دسترسی هستند. در اینجا ایونتی داریم به اسم UserPreferenceChanged که شامل مواردی میشود که کاربر، تنظیمات ویندوز را تغییر می‌دهد. هر تغییری که در تنظیمات ویندوز اعمال بشود، درون یکی از Category‌ها صدا زده میشود. پس اگر ما این ایونت را رجیستر کنیم، هر موقع تغییری در تنظیمات ویندوز اعمال بشود، برنامه‌ی ما نیز متوجه میشود.
برای این منظور یک کلاس را ایجاد می‌کنیم و در متد سازنده‌ی آن، این رخ‌داد را ثبت میکنیم:
pubic class ThemeHelper
{
    public ThemeHelper()
    {
        SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
    }

    private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
    {
        switch (e.Category)
        {
            case UserPreferenceCategory.Accessibility:
                break;
            case UserPreferenceCategory.Color:
                break;
            case UserPreferenceCategory.Desktop:
                break;
            case UserPreferenceCategory.General:
                break;
            case UserPreferenceCategory.Icon:
                break;
            case UserPreferenceCategory.Keyboard:
                break;
            case UserPreferenceCategory.Menu:
                break;
            case UserPreferenceCategory.Mouse:
                break;
            case UserPreferenceCategory.Policy:
                break;
            case UserPreferenceCategory.Power:
                break;
            case UserPreferenceCategory.Screensaver:
                break;
            case UserPreferenceCategory.Window:
                break;
            case UserPreferenceCategory.Locale:
                break;
            case UserPreferenceCategory.VisualStyle:
                break;
        }
    }
}
 همینطور که می‌بینید، این دسته بندی شامل موارد مختلفی میشود که به بخش‌های مختلف تنظیمات ویندوز مربوط است. تنظیمات مربوط به تم، درون General صدا زده میشود. پس کدهای ما قرار است وارد این قسمت بشود. متاسفانه این رخ‌داد اطلاعات کاملتری را به ما نمی‌دهد و فقط اطلاع می‌دهد که تغییری رخ داده (همینطور که قبلا گفتم، هرچیزی هزینه‌ای دارد). پس ما باید مشخصات تم فعلی را دریافت کنیم؛ اما از کجا؟ معلوم است رجیستری! همه چیز در رجیستری ثبت میشود و به‌راحتی قابل دسترسی هست. پس یک متد می‌نویسیم که کلید رجیستری مربوطه را بخواند و آن را بصورت یک مدل (Light یا Dark) برگرداند:   
    private const string RegistryKeyPathTheme = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
    private const string RegSysMode = "SystemUsesLightTheme";

    public static UITheme GetWindowsTheme()
    {
        return GetThemeFromRegistry(RegSysMode);
    }

    private static UITheme GetThemeFromRegistry(string registryKey)
    {
        using var key = Registry.CurrentUser.OpenSubKey(RegistryKeyPathTheme);
        var themeValue = key?.GetValue(registryKey) as int?;
        return themeValue != 0 ? UITheme.Light : UITheme.Dark;
    }
    
    public enum UITheme
    {
        Light,
        Dark
    }
حالا ما نیاز به یک رخ‌داد داریم که کاربر بتواند در برنامه‌ی خودش آن‌را ثبت کند و نیازی به پیاده سازی این کدها نداشته باشد. پس یک EventHandler را به اسم WindowsThemeChanged ایجاد میکنیم: 
    public event EventHandler<FunctionEventArgs<UIWindowTheme>> WindowsThemeChanged;
    
    protected virtual void OnWindowsThemeChanged(UIWindowTheme theme)
    {
        EventHandler<FunctionEventArgs<UIWindowTheme>> handler = WindowsThemeChanged;
        handler?.Invoke(this, new FunctionEventArgs<UIWindowTheme>(theme));
    }
من میخواهم که کاربر، مقدار تم فعلی و رنگ Accent فعلی را نیز بتواند از طریق این رخ‌داد، دریافت کند. پس ما باید یک EventArgs را ایجاد کنیم که پراپرتی‌های دلخواهی را داشته باشد. برای همین کلاس FunctionEventArgs را ایجاد میکنیم:
public class FunctionEventArgs<T> : RoutedEventArgs
{
    public FunctionEventArgs(T theme)
    {
        Theme = theme;
    }
    public FunctionEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }
    public T Theme { get; set; }
}
این کلاس از RoutedEventArgs ارث بری کرده و بصورت جنریک پیاده سازی شده‌است. به این معنا که ما میتوانیم هر نوع دلخواهی را که خواستیم، به عنوان arg استفاده کنیم. اگر دقت کنید من یک مدل دلخواه را به عنوان arg مشخص کرده‌ام:
FunctionEventArgs<UIWindowTheme>
میتوانستیم از همان UITheme هم استفاده کنیم؛ ولی نمی‌توانستیم مقدار Accent را به کاربر برگردانیم. برای همین، مدلی را به اسم UIWindowTheme ایجاد میکنیم:
public class UIWindowTheme
{
    public Brush AccentBrush { get; set; }
    public UITheme CurrentTheme { get; set; }
}
کار تمام است. حالا باید کدهای داخل متد SystemEvents_UserPreferenceChanged را بنویسیم (دقت کنید که کد، باید داخل بخش General نوشته شود):
case UserPreferenceCategory.General:
   var changedTheme = new UIWindowTheme()
   {
         AccentBrush = SystemParameters.WindowGlassBrush,
         CurrentTheme = GetWindowsTheme()
   };
   OnWindowsThemeChanged(changedTheme);
break;
یک مدل را ایجاد کرده و مقدار AccentBrush را برابر با WindowGlassBrush قرار می‌دهیم. این پراپرتی هم مانند ایونتی که اول معرفی کردیم، مربوط به سیستم عامل بوده و رنگ فعلی Accent را بر می‌گرداند. برای مقدار CurrentTheme نیز متدی را که بالاتر برای دریافت تم فعلی از رجیستری نوشتیم، صدا می‌زنیم و در پایان این مدل را به ایونت، پاس می‌دهیم. در پایان می‌توانیم به این صورت ایونت خود را پیاده سازی کنیم:
    ThemeHelper tm = new ThemeHelper();
    tm.WindowsThemeChanged +=OnWindowsThemeChanged;

    private void OnWindowsThemeChanged(object? sender, FunctionEventArgs<RegistryThemeHelper.UIWindowTheme> e)
    {
        rec.Fill = e.Theme.AccentBrush;
        if (e.Theme.CurrentTheme == ThemeHelper.UITheme.Light)
        {
            Background = Brushes.White;
        }
        else
        {
            Background = Brushes.Black;
        }
    }

 

  
نظرات مطالب
مروری بر کدهای کلاس SqlHelper
یک سری ویدیوی آزموشی به روز و با کیفیت در این زمینه از سایت pluralsight موجود است. در گوگل جستجو کنید قابل دریافت هستند.
نظرات مطالب
معرفی پروژه فروشگاهی Iris Store
با توجه به اینکه از بسته نیوگت DNTScheduler  استفاده شده، فکر کنم نیازی به پروژه IrisStore\Scheduler نباشه چون به روز شده‌اش در بسته نیوگت هست.
مطالب
مستند سازی ASP.NET Core 2x API توسط OpenAPI Swagger - قسمت دوم - شروع به مستند سازی یک API
پس از معرفی اجمالی OpenAPI و Swagger در قسمت قبل و همچنین ارائه‌ی یک برنامه‌ی نمونه که آن‌را به مرور تکمیل خواهیم کرد، در ادامه کتابخانه‌ی Swashbuckle را نصب کرده و شروع به مستند سازی API ارائه شده خواهیم کرد.


نصب Swashbuckle (سوواَش باکِل)

اگر عبارت Swashbuckle.AspNetCore را در سایت NuGet جستجو کنیم، چندین بسته‌ی مختلف مرتبط با آن‌را خواهیم یافت. ما در این بین، بیشتر به این بسته‌ها علاقمندیم:
- Swashbuckle.AspNetCore.Swagger: کار آن ارائه‌ی خروجی OpenAPI تولیدی بر اساس ASP.NET Core API برنامه‌ی ما، به صورت یک JSON Endpoint است.
- Swashbuckle.AspNetCore.SwaggerGen: کار آن ساخت Swagger document objects است؛ یا همان OpenAPI Specification.
عموما این دو بسته را با هم جهت ارائه‌ی OpenAPI Specification استفاده می‌کنند.
- Swashbuckle.AspNetCore.SwaggerUI: این بسته، نگارش جایگذاری شده‌ی (embedded) ابزار swagger-UI را به همراه دارد. کار آن، ارائه‌ی یک UI خودکار، بر اساس OpenAPI Specification است که از آن برای آزمایش API نیز می‌توان استفاده کرد.

یک نکته: اگر صرفا بسته‌ی Swashbuckle.AspNetCore را نصب کنیم، هر سه بسته‌ی فوق را با هم دریافت خواهیم کرد و اگر از Visual Studio برای نصب آن‌ها استفاده می‌کنید، انتخاب گزینه‌ی Include prerelease را فراموش نکنید؛ از این جهت که قصد داریم از نگارش 5 آن‌ها استفاده کنیم. چون این نگارش است که از OpenAPI 3x، پشتیبانی می‌کند. خلاصه‌ی این موارد، افزودن PackageReference زیر به فایل پروژه‌ی OpenAPISwaggerDoc.Web.csproj است و سپس اجرای دستور dotnet restore:
<Project Sdk="Microsoft.NET.Sdk.Web">
  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" />
  </ItemGroup>
</Project>


تنظیم میان‌افزار Swashbuckle

پس از افزودن ارجاعی به Swashbuckle.AspNetCore، اکنون نوبت انجام تنظیمات میان‌افزارهای آن است. برای این منظور ابتدا به کلاس Startup و متد ConfigureServices آن مراجعه می‌کنیم:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
    // ...
            services.AddSwaggerGen(setupAction =>
            {
                setupAction.SwaggerDoc(
                   name: "LibraryOpenAPISpecification",
                   info: new Microsoft.OpenApi.Models.OpenApiInfo()
                   {
                       Title = "Library API",
                       Version = "1",
                       Description = "Through this API you can access authors and their books.",
                       Contact = new Microsoft.OpenApi.Models.OpenApiContact()
                       {
                           Email = "name@site.com",
                           Name = "DNT",
                           Url = new Uri("https://www.dntips.ir")
                       },
                       License = new Microsoft.OpenApi.Models.OpenApiLicense()
                       {
                           Name = "MIT License",
                           Url = new Uri("https://opensource.org/licenses/MIT")
                       }
                   });
            });
        }
در اینجا نحوه‌ی تنظیمات ابتدایی سرویس‌های مرتبط با SwaggerGen را ملاحظه می‌کنید. ابتدا نیاز است یک SwaggerDoc به آن اضافه شود که یک name و info را دریافت می‌کند. این name، جزئی از آدرسی است که در نهایت، OpenAPI Specification تولیدی را می‌توان در آنجا یافت. پارامتر Info آن نیز به همراه یک سری مشخصات عمومی درج شده‌ی در مستندات OpenAPI است.

اکنون در متد Configure، میان‌افزار آن‌را خواهیم افزود:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
    // ...
            app.UseHttpsRedirection();

            app.UseSwagger();
    // ...
        }
بهتر است UseSwagger را پس از UseHttpsRedirection درج کرد تا هر نوع درخواست HTTP به آن، به صورت خودکار به HTTPS تبدیل و هدایت شود.

تا اینجا اگر برنامه را اجرا کنید، می‌توان OpenAPI Specification تولیدی را در آدرس زیر یافت:
 https://localhost:5001/swagger/LibraryOpenAPISpecification/swagger.json


در این آدرس، LibraryOpenAPISpecification، همان نامی است که در قسمت setupAction.SwaggerDoc تنظیم کردیم.


نگاهی به OpenAPI Specification تولیدی

در ابتدای swagger.json تولیدی، همانطور که در تصویر فوق نیز مشخص است، همان مشخصات ذکر شده‌ی در قسمت info متد setupAction.SwaggerDoc، قابل مشاهده‌است. سپس لیست مسیرهای این API مشخص شده‌اند:


این‌ها مسیرهایی هستند که توسط دو کنترلر کتاب‌ها و نویسندگان برنامه‌ی Web API ما عمومی شده‌اند. در اینجا مقابل هر مسیر، تعداد آیتم‌های متناظری نیز ذکر شده‌اند. این موارد مرتبط هستند با HTTP methods پشتیبانی شده‌:


که هر کدام به همراه نام متدها و پارامترهای متناظر با آن‌ها نیز می‌شوند. به علاوه نوع responseهای پشتیبانی شده‌ی توسط این متدها نیز ذکر شده‌اند. هر کدام از خروجی‌ها نیز نوع مشخصی دارند که توسط قسمت components -> schemas تصاویر فوق، جزئیات دقیق آن‌ها بر اساس نوع مدل‌های متناظر، استخراج و ارائه شده‌اند.


مشکل: نوع Response تولیدی در OpenAPI Specification صحیح نیست


اگر به جزئیات مسیر /api/authors/{authorId} دقت کنیم، نوع response آن‌را صرفا 200 یا Ok ذکر کرده‌است؛ در حالیکه GetAuthor تعریف شده، حالت NotFound را نیز دارد:
[HttpGet("{authorId}")]
public async Task<ActionResult<Author>> GetAuthor(Guid authorId)
{
    var authorFromRepo = await _authorsService.GetAuthorAsync(authorId);
    if (authorFromRepo == null)
    {
        return NotFound();
    }
    return Ok(_mapper.Map<Author>(authorFromRepo));
}
نمونه‌ی دیگر آن اکشن متد public async Task<ActionResult<Book>> CreateBook است که می‌تواند NotFound یا 404 و یا CreatedAtRoute را که معادل 201 است، بازگشت دهد و در اینجا فقط 200 را ذکر کرده‌است که اشتباه است. بنابراین برای نزدیک کردن این خروجی به اطلاعات واقعی اکشن متدها، نیاز است کار بیشتری انجام شود.


افزودن و راه اندازی Swagger UI

در ادامه می‌خواهیم یک رابط کاربری خودکار را بر اساس OpenAPI Specification تولیدی، ایجاد کنیم:
namespace OpenAPISwaggerDoc.Web
{
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
    // ...

            app.UseHttpsRedirection();

            app.UseSwagger();
            app.UseSwaggerUI(setupAction =>
            {
                setupAction.SwaggerEndpoint(
                    "/swagger/LibraryOpenAPISpecification/swagger.json",
                    "Library API");
            });

    // ...
        }
برای این منظور میان‌افزار SwaggerUI را پس از UseSwagger، در متد Configure کلاس Startup، تعریف می‌کنیم. در اینجا باید مشخص کنیم که OpenAPI Specification تولید شده، دقیقا در چه آدرسی قرار دارد که روش انجام آن‌را در متد setupAction.SwaggerEndpoint ملاحظه می‌کنید. پارامتر دوم آن یک نام اختیاری است.
پس از این تنظیم اگر آدرس https://localhost:5001/swagger/index.html را در مرورگر باز کنیم، چنین خروجی قابل مشاهده خواهد بود:


و اگر بر روی هر کدام کلیک کنیم، ریز جزئیات آن‌ها بر اساس OpenAPI Specification ای که بررسی کردیم، تولید شده‌است (از پارامترها تا نوع خروجی):


اکنون اگر بر روی دکمه‌ی try it out آن نیز کلیک کنید، در همینجا می‌توان این API را آزمایش کرد. برای مثال Controls Accept header را بر روی application/json قرار داده و سپس بر روی دکمه‌ی execute که پس از کلیک بر روی دکمه‌ی try it out ظاهر شده‌است، کلیک کنید تا بتوان خروجی Web API را مشاهده کرد.

در انتهای این صفحه، در قسمت schemas آن، مشخصات مدل‌های بازگشت داده شده‌ی توسط Web API نیز ذکر شده‌اند:



یک نکته: تغییر آدرس  https://localhost:5001/swagger/index.html به ریشه‌ی سایت

اگر علاقمند باشید تا زمانیکه برای اولین بار آدرس ریشه‌ی سایت را در مسیر https://localhost:5001 باز می‌کنید، Swagger UI نمایان شود، می‌توانید تنظیم RoutePrefix زیر را اضافه کنید:
app.UseSwaggerUI(setupAction =>
            {
                setupAction.SwaggerEndpoint(
                    "/swagger/LibraryOpenAPISpecification/swagger.json",
                    "Library API");
                setupAction.RoutePrefix = "";
            });


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: OpenAPISwaggerDoc-02.zip

در قسمت بعد، به بهبود و غنی سازی جزئیات OpenAPI Specification تولیدی خواهیم پرداخت.
اشتراک‌ها
کتاب رایگان MongoDB 3 Succinctly

MongoDB is one of the biggest players in the NoSQL database market, providing high performance, high availability, and automatic scaling. It’s an open-source document database written in C++ and hosted on GitHub. Zoran Maksimovic’s MongoDB 3 Succinctly touches on the most important aspects of the MongoDB database that application developers should be aware of—from installation and the usage of the Mongo Shell, to examples of the MongoDB C# driver APIs for Microsoft .NET Framework.

Table of Contents

  1. MongoDB Overview
  2. MongoDB Installation
  3. The Mongo Shell
  4. Manipulating Documents
  5. Data Retrieval
  6. Basic MongoDB with C#
  7. Data Handling in C#
  8. Inserting Data in C#
  9. Find (Query) Data in C#
  10. Binary Data (File Handling) in C#
  11. Back Up and Restore 
کتاب رایگان MongoDB 3 Succinctly
اشتراک‌ها
معرفی Nerd Fonts

Nerd Fonts patches developer targeted fonts with a high number of glyphs (icons).

معرفی Nerd Fonts