اشتراک‌ها
انتشار Visual Studio 2022 version 17.6 Preview 1

GitHub Issues

The GitHub Issues integration allows you to search and reference your issues from the commit message box in VS, in response to this suggestion ticket. You can reference an issue or a pull request by typing # or clicking on the # button in the lower right side of the commit message text box. If you weren't already authenticated to access related issues, you will now be prompted to sign in to take advantage of this feature.

Line Unstaging

To continue improving our line-staging (aka interactive staging) feature, we've added unstage. You can now use the tool tip option to unstage changes, line by line, as requested here Unstage individual lines and hunks in a file - 4 votes

Arm64

We continue to build native support for Arm64 on Windows 11 for the most popular developer scenarios. We now support the .NET Multi-platform App UI (MAUI) workload on Arm64 Visual Studio.

C++

  • Available as a preview feature, you can now view Unreal Engine logs without leaving VS. To see the logs from the Unreal Engine Editor, click View > Other Windows > UE Log. To filter your logs, click on the "Categories" or "Verbosity" dropdowns. Since this is an experimental feature, feedback is greatly appreciated.
  • You can now import STM32CubeIDE projects for embedded development within Visual Studio with File > Open > Import STM32CubeIDE project. This generates a CMake project with device flashing and debugging settings for STLink. You must have the STM32CubeIDE installed with the board support package for your device. More details available here.
  • You can use the new CMake Debugger to debug your CMake scripts at build time. You can set breakpoints based on filenames, line numbers, and when CMake errors are triggered. Additionally, you can view call stacks of filenames and watch defined variables. Currently, this only works with bundled CMake, and projects targeting WSL or remote machines are not supported yet. We are actively working to add more support to the CMake debugger, and feedback is greatly appreciated. 
انتشار Visual Studio 2022 version 17.6 Preview 1
مطالب
ویژگی های کمتر استفاده شده در NET. - بخش پنجم

Nullable<T>.GetValueOrDefault Method

با استفاده از متد GetValueOrDefault مقدار فعلی یک شیء Nullable و یا مقدار پیش فرض آن را می‌توان بدست آورد. این متد از عملگر ?? سریع‌تر است.
float? yourSingle = -1.0f;
Console.WriteLine( yourSingle.GetValueOrDefault() );

yourSingle = null;
Console.WriteLine( yourSingle.GetValueOrDefault() );

// assign different default value
Console.WriteLine( yourSingle.GetValueOrDefault( -2.4f ) );

// returns the same result as the above statement
Console.WriteLine( yourSingle ?? -2.4f );

در صورتیکه مقداری را به عنوان پیش فرض، به پارامتر این متد ارسال نکنید، مقدار پیش فرض آن از نوع استفاده شده بدست می‌آید.

شما می‌توانید برای دیکشنری نیز یک متد Get امن ایجاد کنید (در صورت عدم وجود کلید، بجای پرتاب استثناء، مقدار پیش فرض بازگشت داده شود).

public static class DictionaryExtensions
{
    public static TValue GetValueOrDefault< TKey, TValue >( this Dictionary< TKey, TValue > dic,
                                                            TKey key )
    {
        TValue result;
        return dic.TryGetValue( key,
                                out result )
            ? result
            : default(TValue);
    }
}

و روش استفاده

var names = new Dictionary< int, string >
            {
                { 0, "Vahid" }
            };
Console.WriteLine( names.GetValueOrDefault( 1 ) );


ZipFile in .NET

با استفاده از کلاس ZipFile ( رفرنس به اسمبلی System.IO.Compression.FileSystem ) می‌توان عملیات بازکردن، ایجاد و استخراج فایل‌های Zip را انجام داد.
var startPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Start" );
var resultPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Result" );
var extractPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Extract" );
Directory.CreateDirectory( startPath );
Directory.CreateDirectory( resultPath );
Directory.CreateDirectory( extractPath );

var zipPath = Path.Combine( resultPath, Guid.NewGuid() + ".zip" );
ZipFile.CreateFromDirectory( startPath, zipPath );
ZipFile.ExtractToDirectory( zipPath, extractPath );

C# Preprocessor Directives

با استفاده از  warning#  می توان یک هشدار را در یک قسمت خاص از کد تولید کرد.
#if DEBUG
#warning DEBUG is defined
#endif
و خروجی آن

با استفاده از  error#  می توان یک خطا را در یک جای خاصی از کد تولید کرد.
#if DEBUG
#error DEBUG is defined
#endif
در صورتی که کد بالا را اجرا کنید (در حال دیباگ) کامپایلر با نمایش DEBUG is defined در پنجره Error List، جلوی اجرای برنامه را می‌گیرد. اما در حالت ریلیز، برنامه بدون هیچ مشکلی اجرا می‌شود.

با استفاده از  line#  می توانید شماره خط کامپایلر و نام فایل خروجی (اختیاری) را برای خطاها و هشدارها تغییر دهید.

در مثال زیر، در صورتیکه در خط اول break point قرار دهید و با کلید F10 برنامه را اجرا کنید، مشاهده می‌کنید که دیباگر، خطی را که بعد از دستور line hidden# نوشته شده است، در نظر نمی‌گیرد (برای دیباگ) اما اجرا می‌شود و دیباگر بر روی دستور بعد از line default# قرار می‌گیرد.

    Console.WriteLine("Normal line #1."); // Set break point here.
#line hidden
    Console.WriteLine("Hidden line.");
#line default
    Console.WriteLine("Normal line #2.");


Stackalloc

کلمه کلیدی stackalloc برای اختصاص یک بلاک از حافظه در stack، در زمینه کد غیرامن (unsafe code) استفاده می‌شود.
مثال زیر 20 عدد اول دنباله فیبوناچی را تولید می‌کند. هر عدد از مجموع دو عدد قبلی به دست می‌آید. در این مثال، یک بلاک از حافظه به اندازه 20 عدد از نوع int را در stack (نه heap) اختصاص می‌دهد. (تفاوت stack با heap)
static unsafe void Fibonacci()
{
    const int arraySize = 20;
    int* fib = stackalloc int[arraySize];
    var p = fib;
    *p++ = *p++ = 1;

    for ( var i = 2; i < arraySize; ++i, ++p )
    {
        *p = p[-1] + p[-2];
    }

    for ( var i = 0; i < arraySize; ++i )
    {
        System.Console.WriteLine( fib[i] );
    }
}
آدرس بلاک حافظه در اشاره گر fib ذخیره می‌شود. این متغیر توسط GC جمع آوری نمی‌شود و طول عمر آن محدود به متدی است که در آن تعریف شده است و شما نمی‌توانید قبل از بازگشت متد، حافظه را آزاد کنید.
تنها دلیل استفاده از stackalloc، عملکرد بهتر آن است (برای محاسبات و یا ردوبدل اطلاعات). با استفاده از stackalloc به جای اختصاص دادن آرایه (heap)، فشار کمتری را بر GC وارد می‌کنید (نیاز کمتری به اجرای GC وجود دارد). در نتیجه سرعت اجرای بالاتری خواهید داشت.
توجه: برای اجرای مثال بالا باید پنجره خصوصیات پروژه را باز کنید و در بخش Build، گزینه Allow unsafe code را تیک بزنید.
بازخوردهای دوره
عیب یابی و دیباگ برنامه‌های SignalR
با سلام،
برنامه‌ی من بعد از مدتی دچار مشکل شماره سوم (متدهای سمت کلاینت من فراخوانی نمی‌شوند. ) شده است، کد مربوط به لاگ کردن خروجی در کنسول را نیز اضافه کردم اما مشکل حل نشد. داخل هر کدام از Hubها یک breakpoint قرار دادم؛ مشکلی نداشت یعنی کدهای سمت سرور به خوبی اجرا می‌شوند. در نهایت با مراجعه به Event Viewer ویندوز خطا را پیدا کردم. به طور خلاصه خطا بیانگر عدم پشتیبانی از WebSocket در برنامه است، که در این حالت پیشنهاد شده است خط زیر را به WebConfig اضافه کنیم:
<httpRuntime targetFramework="4.5" />
اما باز هم مشکل حل نشد. به نظر شما مشکل از کجا می‌تواند باشد؟ با تشکر
برنامه در حالت لوکال هاست (ویندوز 8.1) و بر روی سرور (Windows Server 2012) هم همین مشکل را دارد.  
مطالب
کار با وب سرویس جاوایی تشخیص ایمیل‌های موقتی در دات نت
یکی از وب سرویس‌های سایت name api، امکان تشخیص موقتی بودن ایمیل مورد استفاده‌ی جهت ثبت نام در یک سایت را فراهم می‌کند. آدرس WSDL آن نیز در اینجا قرار دارد. اگر مطابق معمول استفاده از سرویس‌های وب در دات نت، بر روی ارجاعات پروژه کلیک راست کرده و گزینه‌ی Add service refrence را انتخاب کنیم و سپس آدرس WSDL یاد شده را به آن معرفی کنیم، بدون مشکل ساختار این وب سرویس دریافت و برای استفاده‌ی از آن به یک چنین کدی خواهیم رسید:
var client = new SoapDisposableEmailAddressDetectorClient();
 
var context = new soapContext
{
    //todo: get your API key here: http://www.nameapi.org/en/register/
    apiKey = "test"
};
var result = client.isDisposable(context, "DaDiDoo@mailinator.com"); 
 
if (result.disposable.ToString() == "YES")
{
    Console.WriteLine("YES! It's Disposable!");
}
متد isDisposable ارائه شده‌ی توسط این وب سرویس، دو پارامتر context که در آن باید API Key خود را مشخص کرد و همچنین آدرس ایمیل مورد بررسی را دریافت می‌کند. اگر به همین ترتیب این پروژه را اجرا کنید، با خطای Bad request از طرف سرور متوقف خواهید شد:
 Additional information: The remote server returned an unexpected response: (400) Bad Request.
اگر به خروجی این وب سرویس در فیدلر مراجعه کنیم، چنین شکلی را خواهد داشت:
 <html><head><title>Bad Request</title></head><body><h1>Bad Request</h1><p>No api-key provided!</p></body></html>
عنوان کرده‌است که api-key را، در درخواست وب خود ذکر نکرده‌ایم.
اگر همین وب سرویس را توسط امکانات سایت http://wsdlbrowser.com بررسی کنید، بدون مشکل کار می‌کند. اما تفاوت در کجاست؟
خروجی ارسالی به سرور، توسط سایت http://wsdlbrowser.com به این شکل است:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://disposableemailaddressdetector.email.services.v4_0.soap.server.nameapi.org/">
  <SOAP-ENV:Body>
    <ns1:isDisposable>
      <context>
        <apiKey>test</apiKey>       
      </context>
      <emailAddress>sdsdg@site.com</emailAddress>
    </ns1:isDisposable>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
و نمونه‌ی تولید شده‌ی توسط WCF (امکان Add service reference در حقیقت یک WCF Client را ایجاد می‌کند) به صورت زیر می‌باشد:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
     <isDisposable xmlns="http://disposableemailaddressdetector.email.services.v4_0.soap.server.nameapi.org/">
          <context xmlns=""><apiKey>test</apiKey></context>
          <emailAddress xmlns="">DaDiDoo@mailinator.com</emailAddress>
      </isDisposable>
  </s:Body>
</s:Envelope>
از لحاظ اصول XML، خروجی تولیدی توسط WCF هیچ ایرادی ندارد. از این جهت که نام فضای نام مرتبط با http://schemas.xmlsoap.org/soap/envelope/ را به s تنظیم کرده‌است و سپس با استفاده از این نام، Envelope را تشکیل داده‌است. اما ... این وب سرور جاوایی دقیقا با نام SOAP-ENV کار می‌کند و فضای نام ns1 بعدی آن. کاری هم به اصول XML ندارد که باید بر اساس نام xmlns ذکر شده، کار Parse ورودی دریافتی صورت گیرد و نه بر اساس یک رشته‌ی ثابت از پیش تعیین شده. بنابراین باید راهی را پیدا کنیم تا بتوان این s را تبدیل به SOAP-ENV کرد.

برای این منظور به سه کلاس ذیل خواهیم رسید:
public class EndpointBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    { }
 
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    { }
 
    public void Validate(ServiceEndpoint endpoint)
    { }
 
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new ClientMessageInspector());
    }
}
 
public class ClientMessageInspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref Message reply, object correlationState)
    { }
 
    public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
    {
        request = new MyCustomMessage(request);
        return request;
    }
}
 
/// <summary>
/// To customize WCF envelope and namespace prefix
/// </summary>
public class MyCustomMessage : Message
{
    private readonly Message _message;
 
    public MyCustomMessage(Message message)
    {
        _message = message;
    }
 
    public override MessageHeaders Headers
    {
        get { return _message.Headers; }
    }
 
    public override MessageProperties Properties
    {
        get { return _message.Properties; }
    }
 
    public override MessageVersion Version
    {
        get { return _message.Version; }
    }
 
    protected override void OnWriteStartBody(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
    }
 
    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
    {
        _message.WriteBodyContents(writer);
    }
 
    protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer)
    {
        writer.WriteStartElement("SOAP-ENV", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
        writer.WriteAttributeString("xmlns", "ns1", null, value: "http://disposableemailaddressdetector.email.services.v4_0.soap.server.nameapi.org/");
    }
}
که پس از تعریف client به نحو ذیل معرفی می‌شوند:
 var client = new SoapDisposableEmailAddressDetectorClient();
client.Endpoint.Behaviors.Add(new EndpointBehavior());
توسط EndpointBehavior سفارشی، می‌توان به متد OnWriteStartEnvelope دسترسی یافت و سپس s آن‌را با SOAP-ENV درخواستی این وب سرویس جایگزین کرد. اکنون اگر برنامه را اجرا کنید، بدون مشکل کار خواهد کرد و دیگر پیام یافت نشدن API-Key را صادر نمی‌کند.

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
اشتراک‌ها
بی‌جهت لینوکسی‌ها را بخاطر باگ bash شرمنده نکنید

This is a defense of the most prolific and dedicated public servant that has graced the world in my lifetime. One man has added hundreds of billions, if not trillions of dollars of value to the global economy. This man has worked tirelessly for the benefit of everyone around him. It is impossible to name a publicly traded company that has not somehow benefited from his contributions, and many have benefited to the tune of billions. In return for the countless billions of wealth that people made from the fruits of his labor, he was rewarded with poverty and ridicule. Now that the world is done taking from him, they are heading to the next step of vilifying him as incompetent.

بی‌جهت لینوکسی‌ها را بخاطر باگ bash شرمنده نکنید
مطالب
آموزش فریم ورک Vuetify قسمت دوم - UI Components بخش اول

 کاربران امروزه با عناصری که به نحوی خاص درون صفحه عمل می‌کنند، آشنا شده‌اند. بنابراین انتخاب مناسب برای اتخاذ این عناصر زمانی مناسب است که به تکمیل کارآیی و رضایت کاربر کمک کند.

یک کامپوننت در واقع از دو بخش تشکیل شده‌است:

1 - اول اینکه چگونه به نظر می‌رسد ( UI ).

2 – دوم اینکه چگونه کار میکند ( UX ).

این عناصر رابط ( component ) شامل :

Input Controls : check boxes, radio buttons, drop down lists, list boxes, buttons, toggles, text fields, date field

Navigational Components : breadcrumb, slider, search field, pagination, slider, tags, icons

Informational Components : tool tips, icons, progress bar, notifications, message boxes, modal windows

Containers : accordion 

اما باید توجه داشت که فقط به این موارد محدود نمی‌شوند.

در این قسمت به طور مختصر با این دست از کامپوننت‌ها ( UI Component ) آشنا می‌شویم. 


کامپوننت v-alert 

کامپوننت V-Alert برای انتقال اطلاعات مهم به کاربر مورد استفاده قرار می‌گیرد. این کامپوننت چهار نوع اطلاعات را به کاربر گوشزد می‌کند که شامل موفقیت‌ها، اطلاعات، هشدارها و خطاها می‌باشد.

هشدارها می‌توانند یک رنگ خاص را داشته باشند که به طور پیش فرض نمایش داده نمی‌شوند. 

در مثال پایین، کامپوننت v-alert شامل دو مقدار است که برای آن تنظیم شده‌است. مقدار (value) که شامل یک مقدار Boolean است و مقدار (type) که مشخص کننده نوع هشدار است (موفقیت ، اطلاعات ، هشدار و خطا).

در قطعه کد پایین، این چهار نوع اطلاعات قابل نمایش به کاربر مشخص شده‌اند:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-alert :value="true" type="success">
        This is a success alert.
      </v-alert>
  
      <v-alert :value="true" type="info">
        This is a info alert.
      </v-alert>
  
      <v-alert :value="true" type="warning">
        This is a warning alert.
      </v-alert>
  
      <v-alert :value="true" type="error">
        This is a error alert.
      </v-alert>
    </div>
  </v-app>
</div>



برای کامپوننت V-Alert می‌توان properties‌های مختلفی را مشخص نمود که از جمله آنها می‌توان به موارد زیر اشاره کرد:

Color : به وسیله این property می‌توان رنگ پیغام را مشخص نمود. هم به وسیله نام رنگ میتوان رنگ مورد نظر را مشخص کرد و هم به وسیله‌ی کد RGB این کار را انجام داد. dismissible : این تنظیم مشخص می‌کند که پیغام، قابلیت بسته شدن را دارد یا خیر که حاوی یک مقدار Boolean است.

icon : مشخص کننده یک نماد خاص است که درون جعبه پیغام قرار می‌گیرد.

type :  مشخص کننده نوع پیام است که پیشتر در مورد آن توضیح داده شد. 


کامپوننت v-avatar 

کامپوننت v-avatar برای تغییر اندازه تصاویر مورد استفاده قرار می‌گیرد که معمولا جهت نمایش عکس پروفایل استفاده می‌شود.

طریقه استفاده :

avatar، دارای یک اندازه‌ی پویا است که می‌تواند برای هر وضعیتی تغییر کند.

برای این کامپوننت سه properties قابل تنظیم است:

color : به وسیله این property می‌توان رنگ دلخواهی را برای آواتار مشخص نمود. هم به وسیله نام رنگ می‌توان رنگ مورد نظر را مشخص کرد و هم به وسیله کد RGB این کار را صورت داد.

size : به طور پیش‌فرض برای avatar، سایز 48 تنظیم شده‌است که می‌توان این میزان را کم و یا زیاد کرد.

tile : همانند border radius در css عمل می‌کند. با تنظیم این گزینه میتوانیم یک آواتار گرد داشته باشیم.


کامپوننت v-badge

به وسیله این کامپوننت می‌توان نمادهایی را برای نمایش اطلاعاتی به کاربر یا جلب توجه کاربر به یک عنصر خاص، ایجاد نمود.

این کامپوننت نیز properties خاص خود را دارد که از جمله آن می‌توان به color , left , mode , overlab  و غیره اشاره کرد.

قطعه کد پایین نشان دهنده چگونگی عملکرد این کامپوننت است:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-badge color="purple" left overlap>
        <template v-slot:badge>
          <v-icon dark small>
            done
          </v-icon>
        </template>
        <v-icon color="grey lighten-1" large>
          account_circle
        </v-icon>
      </v-badge>
        <v-badge overlap color="orange">
        <template v-slot:badge>
          <v-icon dark small>
            notifications
          </v-icon>
        </template>
        <v-icon large color="grey darken-1">
          account_box
        </v-icon>
      </v-badge>
    </div>
  </v-app>
</div>

نتیجه قطعه کد بالا بدین ترتیب است:


کامپوننت v-bottom-nav 

این کامپوننت را می‌توان جایگزین sidebar‌ها نمود. این کامپوننت در درجه اول در موبایل مورد استفاده قرار میگیرد که می‌تواند شامل متن و یا آیکن باشد. 

به وسیله این کامپوننت امکان انتقال از یک بخش از صفحه به بخشی دیگر امکان پذیر می‌شود. 

این کامپوننت نیز properties خاص خود را دارد که از جمله آن می‌توان به active-sync (برای نشان دادن فعال یا غیر فعال بودن گزینه انتخاب شده)، fixed ( برای مشخص کردن موقعیت کامپوننت در صفحه) و موارد دیگر اشاره کرد.

تقسیم بندی اجزاء این کامپوننت به شرح زیر است:

1 - محل قرار گیری کامپوننت

2- آیکن غیر فعال

3- برچسب غیر فعال

4 - آیکن فعال

5- برچسب فعال

قطعه کد پایین نشان دهنده چگونگی یک bottom navbar است:

<div id="app">
  <v-app id="inspire">
    <v-card height="200px" flat>
      <div>
        Active: {{ bottomNav }}   // 
      </div>
      <v-bottom-nav :active.sync="bottomNav" :value="true" absolute color="transparent">
        <v-btn color="teal" flat value="recent">
          <span>Recent</span>
          <v-icon>history</v-icon>
        </v-btn>
  
        <v-btn color="teal" flat value="favorites">
          <span>Favorites</span>
          <v-icon>favorite</v-icon>
        </v-btn>
  
        <v-btn color="teal" flat value="nearby">
          <span>Nearby</span>
          <v-icon>place</v-icon>
        </v-btn>
      </v-bottom-nav>
    </v-card>
  </v-app>
</div>



کامپوننت v-btn  

این کامپوننت برای ایجاد یک دکمه چه به صورت متن و یا آیکن مورد استفاده قرار میگیرد. دکمه‌ها به کاربران این امکان را می‌دهند تا اقداماتی را انجام دهند و انتخاب‌های خود را تنها با یک کلیک انجام دهند.

از دکمه‌ها ممکن است در جاهای مختلف صفحه به خصوص در دیالوگ باکس‌ها، فرمها و ابزارها مورد استفاده قرار گیرد. 

کامپوننت v-btn نیز مانند سایر کامپوننت‌ها تنظیمات خاص خود را دارد که از جمله آن می‌توان به کوچکی و بزرگی دکمه، فعال یا غیر فعال بودن دکمه، نوع متن یا آیکن بودن دکمه اشاره نمود. 

حالت‌های مختلفی از دکمه‌ها وجود دارند که میتوانند به بهتر شدن UI برنامه ما کمک کنند. برای مثال می‌توان به موارد زیر اشاره کرد:

button drop-down variants : دکمه‌های کرکره‌ای که معمولا برای نظم و کم جا بودن در صفحه مورد استفاده قرار می‌گیرند.

icons : آیکن‌ها می‌توانند برای محتوای اصلی یک دکمه مورد استفاده قرار بگیرند تا ظاهر زیباتری را به دکمه ما بدهند.

floating : این دکمه‌ها حالت آیکن را دارند؛ با این تفاوت که آیکن مورد نظر، درون یک محتوا قرار می‌گیرد.

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

round : این نوع دکمه‌ها دقیقا کارآیی دکمه‌های معمولی را دارند؛ با این تفاوت که این دکمه‌ها دارای لبه‌هایی گرد هستند.

یک نمونه از ایجاد انواع دکمه‌ها در زیر آمده است:

<div id="app">
  <v-app id="inspire">
    <div>
      <v-btn color="success">Success</v-btn>
      <v-btn color="error">Error</v-btn>
      <v-btn color="warning">Warning</v-btn>
      <v-btn color="info">Info</v-btn>
    </div>
  </v-app>
</div>




کامپوننت v-calendar

یکی از کامپوننت‌هایی که به تازگی به vuetify اضافه شده است، کامپوننت تقویم یا v-calendar است. از این کامپوننت برای نمایش تاریخ، روز، هفته، ماه و سال استفاده می‌شود. یک تقویم دارای یک نوع و یک مقدار است که تعیین می‌کند چه نوع تقویمی، در طول چه مدت زمانی نمایش داده شود.

حالت‌های مختلفی برای نمایش تقویم در صفحه وجود دارد که برای مثال می‌توان به موارد زیر اشاره کرد: 

events : به وسیله این گزینه میتوان برای هر روز یک رخداد خاص را مشخص نمود که به وسیله کلیک بر روی آن، اطلاعات آن رخداد نمایش داده شود.

weekly : می‌توان یک تقویم هفتگی را ایجاد نمود و رخدادهای هفتگی را برای آن تنظیم کرد.

نمونه ایجاد یک تقویم در پایین آمده است:

<div id="app">
  <v-app id="inspire">
    <v-layout wrap>
      <v-flex xs12>
        <v-sheet height="500">
          <v-calendar ref="calendar" v-model="start" :type="type" :end="end" color="primary">
          </v-calendar>
        </v-sheet>
      </v-flex>
  
      <v-flex sm4 xs12>
        <v-btn @click="$refs.calendar.prev()">
          <v-icon dark left>
            keyboard_arrow_left
          </v-icon>
          Prev
        </v-btn>
      </v-flex>
      <v-flex sm4 xs12>
        <v-select v-model="type" :items="typeOptions" label="Type">
        </v-select>
      </v-flex>
      <v-flex sm4 xs12>
        <v-btn @click="$refs.calendar.next()">
          Next
          <v-icon right dark>
            keyboard_arrow_right
          </v-icon>
        </v-btn>
      </v-flex>
    </v-layout>
  </v-app>
</div>
js قطعه کد
new Vue({
  el: '#app',
  data: () => ({
    type: 'month', //مشخص کننده نوع تقویم که در اینجا تقویم به صورت ماهانه است
    start: '2019-01-01',
    end: '2019-01-06',
    typeOptions: [
      { text: 'Day', value: 'day' },
      { text: '4 Day', value: '4day' },
      { text: 'Week', value: 'week' },
      { text: 'Month', value: 'month' },
      { text: 'Custom Daily', value: 'custom-daily' },
      { text: 'Custom Weekly', value: 'custom-weekly' }
    ]
  })
})





نظرات مطالب
ساخت ربات تلگرامی با #C
1. آپدیتهای دریافتی همیشه فقط شامل Message نمیشه و ممکنه آپدیت دریافتی از نوع CallbackQuery هم باشه
2. کمی بالاتر توضیح داده شده که برای دریافت آپدیت‌های جدید باید پارامتر offset رو هم ارسال کنی. مقدار این پارامتر باید رقم بعدی Id آخرین آپدیت دریافتی باشه یعنی update_id  +1
برای طراحی ربات تلگرام هم بهتر هست که از پکیج‌های آماده استفاده بشه که بالاتر عرض کردم.
یه پروژه کنسول ایجاد کن پکیج telegram.bot رو هم از Nuget به برنامه اضافه کن و کلاس program.cs  رو به صورت زیر پیاده کن
به جای BOT_TOKEN هم توکن ربات خودت رو کپی کن و برنامه رو اجرا کن
using System.Threading.Tasks;
using Telegram.Bot;

namespace Bot.Engine.Console
{
    public class Program
    {
        Api bot;
        string botToken = "BOT_TOKEN";

        public static void Main(string[] args)
        {
            Task.Run(() => RunBot(botToken));

            System.Console.ReadLine();
        }


        /// <summary>
        /// 
        /// </summary>
        public static async Task RunBot(string botToken)
        {
            #region راه اندازی ربات

             bot = new Api(botToken);
            var me = await bot.GetMe();
            if (me != null)
            {
                System.Console.WriteLine("bot started {0}", me.Username);
            }
            else
            {
                System.Console.WriteLine("get bot failed ");
            }

          
            #endregion

            #region شروع گوش دادن به درخواست‌ها var whileCount = 0;
            var offset = 0;

            while (true)
            {
                System.Console.WriteLine("while no {0}", whileCount);

                whileCount += 1;
                try
                {
                    var updates = await bot.GetUpdatesAsync(offset);
                    var updatesCount = updates.Count();
                    System.Console.WriteLine("updates count is {0}", updatesCount);
                    System.Console.WriteLine("================================================================");

                    if (updatesCount > 0)
                    {
                        foreach (var update in updates)
                        {
                            try
                            {
                                offset = update.Id + 1;
                                if (update.Message.Text!=null)
                                {
                                    //echo msg
                                    await bot.SendTextMessageAsync(update.Message.Chat.Id, update.Message.Text);
                                }
                                else
                                {
                                    await bot.SendTextMessageAsync(update.Message.Chat.Id, "لطفا یک پیام متنی بفرستید");
                                }

                            }
                            catch (Exception ex)
                            {
                                bot.SendTextMessage(update.Message.Chat.Id, ex.ToString());
                            }
                        }
                        continue;
                    }


                }
                catch (Exception ex)
                {
                    System.Console.WriteLine("Error Msg = {0}",ex.Message);
                }

            }

            #endregion
        }

    }
}

مطالب
آشنایی با Refactoring - قسمت 6

در ادامه بحث «حذف کدهای تکراری»، روش Refactoring دیگری به نام "Extract Superclass" وجود دارد که البته در بین برنامه نویس‌های دات نت به نام Base class بیشتر مشهور است تا Superclass. هدف آن هم انتقال کدهای تکراری بین چند کلاس، به یک کلاس پایه و سپس ارث بری از آن می‌باشد.

یک مثال:
در WPF و Silverlight جهت مطلع سازی رابط کاربری از تغییرات حاصل شده در مقادیر داده‌ها، نیاز است کلاس مورد نظر، اینترفیس INotifyPropertyChanged را پیاده سازی کند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.Before
{
public class User : INotifyPropertyChanged
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
raisePropertyChanged("Name");
}
}

public event PropertyChangedEventHandler PropertyChanged;
void raisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}


و نکته‌ی مهم این است که اگر 100 کلاس هم داشته باشید، باید این کدهای تکراری اجباری مرتبط با raisePropertyChanged را در آن‌ها قرار دهید. به همین جهت مرسوم است برای کاهش حجم کدهای تکرای، قسمت‌های تکراری کد فوق را در یک کلاس پایه قرار می‌دهند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

و سپس از آن ارث بری می‌کنند:

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class User : ViewModelBase
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
RaisePropertyChanged("Name");
}
}
}
}


به این ترتیب این کلاس پایه در ده‌ها و صدها کلاس قابل استفاده خواهد بود، بدون اینکه مجبور شویم مرتبا یک سری کد تکراری «اجباری» را copy/paste کنیم.

مثالی دیگر:
اگر با ORM های Code first کار کنید، نیاز است تا ابتدا طراحی کار توسط کلاس‌های ساده دات نتی انجام شود؛ که اصطلاحا به آن‌ها POCO یا Plain old CLR objects یا Plain old .NET Classes هم گفته می‌شود. در بین این کلاس‌ها، متداول است که یک سری از خصوصیات، تکراری و مشترک باشد؛ مثلا تمام کلاس‌ها تاریخ ثبت رکورد را هم داشته باشند به همراه نام کاربر و مشخصاتی از این دست. اینجا هم برای حذف کدهای تکراری، یک Base class طراحی می‌شود: (+)

نظرات مطالب
EF Code First #4
از توجهتون سپاسگزارم.
مهندس جان من اینکار را انجام دادم و پاورشل را هم ارتقاء دادم، ولی مشکل برطرف نشد.
یه پروژۀ جدید ایجاد کردم و همۀ فایل‌های کد پروژۀ قبلی (بغیر از app.config) را بهش اضافه کردم و بعد با استفاده از NuGet نسخۀ آخر EF را به پروژه اضافه کردم. کانکشن استرینگ را به پروژه اضافه نکردم (کلاً به app.config دست نزدم) و دستور enable-migrations را اجرا کردم. دستور با موفقیت اجرا شد و اون چیزهایی که فرمودین به پروژه اضافه شد.
دیتابیس را به صورت دستی Drop کردم و یه بار برنامه را اجرا کردم و بعد از اون هم تغییراتی که به مدل دادم به دیتابیس اعمال شد و رکوردهای مربوط بهش داخل جدول dbo.__MigrationHistoryثبت شد.

رفتم به درس پنجم، و دوباره به محض بازکردن کنسول همون پیغامی را که توی کامنت اولم نوشتم می‌نویسه و وقتی هم می‌خوام که دستور Add-Migration را اجرا کنم، همون چیزی را می‌نویسه که قبلاً برای enable-migrations می‌نوشت. جالب اینکه برای خود دستور enable-migrations هم همینو می‌نویسه!برای دستور Update-Database هم همینطور، در حالی که برای پروژۀ شما می‌نوشت که هم اکنون فعال هست.

فکرمی‌کنم مشکل از فایل ‪init.psd1 باشه که هنگام اضافه کردن EF در فولدر tools ایجاد میشه.
من فایل init.psd1 را که در پوشۀ tools مربوط به پروژۀ ایجاد شده توسط شما قرار داشت جایگزین فایل با همین نام که در پروژۀ خودم بود کردم و مشکل برطرف شد. نمی‌دونم ایراد کار چیه؟ من قدم به قدم همونطور که شما نوشتین پیش رفته‌م و چیزی را هم تغییر نداده‌م. آیا ممکنه توی پروژه‌هایی که می‌خوام از EF استفاده کنم این مسئله دردسرساز بشه؟
مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت سوم - ورود به سیستم
پس از ایجاد AuthService در قسمت قبل، اکنون می‌خواهیم از آن برای تکمیل صفحه‌ی ورود به سیستم و همچنین تغییر منوی بالای برنامه یا همان کامپوننت header استفاده کنیم.


ایجاد ماژول Dashboard و تعریف کامپوننت صفحه‌ی محافظت شده

قصد داریم پس از لاگین موفق، کاربر را به یک صفحه‌ی محافظت شده هدایت کنیم. به همین جهت ماژول جدید Dashboard را به همراه کامپوننت یاد شده، به برنامه اضافه می‌کنیم:
>ng g m Dashboard -m app.module --routing
>ng g c Dashboard/ProtectedPage
پس از اجرای این دستورات، ابتدا به فایل app.module.ts مراجعه کرده و تعریف این ماژول را که به صورت خودکار به قسمت imports اضافه شده‌است، به قبل از AppRoutingModule منتقل می‌کنیم تا مسیریابی‌های آن توسط catch all ماژول AppRouting لغو نشوند:
import { DashboardModule } from "./dashboard/dashboard.module";

@NgModule({
  imports: [
    //...
    DashboardModule,
    AppRoutingModule
  ]
})
export class AppModule { }
همچنین به فایل dashboard-routing.module.ts ایجاد شده مراجعه کرده و مسیریابی کامپوننت جدید ProtectedPage را اضافه می‌کنیم:
import { ProtectedPageComponent } from "./protected-page/protected-page.component";

const routes: Routes = [
  { path: "protectedPage", component: ProtectedPageComponent }
];


ایجاد صفحه‌ی ورود به سیستم

در قسمت اول این سری، کارهای «ایجاد ماژول Authentication و تعریف کامپوننت لاگین» انجام شدند. اکنون می‌خواهیم کامپوننت خالی لاگین را به نحو ذیل تکمیل کنیم:
export class LoginComponent implements OnInit {

  model: Credentials = { username: "", password: "", rememberMe: false };
  error = "";
  returnUrl: string;
مدل login را همان اینترفیس Credentials تعریف شده‌ی در قسمت قبل درنظر گرفته‌ایم. به همراه دو خاصیت error جهت نمایش خطاها در ذیل قسمت لاگین و همچنین ذخیره سازی returnUrl در صورت وجود:
  constructor(
    private authService: AuthService,
    private router: Router,
    private route: ActivatedRoute) { }

  ngOnInit() {
    // reset the login status
    this.authService.logout(false);

    // get the return url from route parameters
    this.returnUrl = this.route.snapshot.queryParams["returnUrl"];
  }
AuthService را به سازنده‌ی کامپوننت لاگین تزریق کرده‌ایم تا بتوان از متدهای login و Logout آن استفاده کرد. همچنین از سرویس Router برای استفاده‌ی از متد navigate آن استفاده می‌کنیم و از سرویس ActivatedRoute برای دریافت کوئری استرینگ returnUrl، در صورت وجود، کمک خواهیم گرفت.
اکنون متد ارسال این فرم چنین شکلی را پیدا می‌کند:
  submitForm(form: NgForm) {
    this.error = "";
    this.authService.login(this.model)
      .subscribe(isLoggedIn => {
        if (isLoggedIn) {
          if (this.returnUrl) {
            this.router.navigate([this.returnUrl]);
          } else {
            this.router.navigate(["/protectedPage"]);
          }
        }
      },
      (error: HttpErrorResponse) => {
        console.log("Login error", error);
        if (error.status === 401) {
          this.error = "Invalid User name or Password. Please try again.";
        } else {
          this.error = `${error.statusText}: ${error.message}`;
        }
      });
  }
در اینجا توسط وهله‌ی تزریق شده‌ی this.authService، کار فراخوانی متد login و ارسال شیء Credentials به سمت سرور صورت می‌گیرد. خروجی متد login یک Observable از نوع boolean است. بنابراین در صورت true بودن این مقدار و یا موفقیت آمیز بودن عملیات لاگین، کاربر را به یکی از دو صفحه‌ی protectedPage و یا this.returnUrl (در صورت وجود)، هدایت خواهیم کرد.
صفحه‌ی خالی protectedPage را در ابتدای بحث، در ذیل ماژول Dashboard ایجاد کردیم.
در سمت سرور هم در صورت شکست اعتبارسنجی کاربر، یک return Unauthorized صورت می‌گیرد که معادل error.status === 401 کدهای فوق است و در اینجا در قسمت خطای عملیات بررسی شده‌است.

قالب این کامپوننت نیز به صورت ذیل به model از نوع Credentials آن متصل شده‌است:
<div class="panel panel-default">
  <div class="panel-heading">
    <h2 class="panel-title">Login</h2>
  </div>
  <div class="panel-body">
    <form #form="ngForm" (submit)="submitForm(form)" novalidate>
      <div class="form-group" [class.has-error]="username.invalid && username.touched">
        <label for="username">User name</label>
        <input id="username" type="text" required name="username" [(ngModel)]="model.username"
          #username="ngModel" class="form-control" placeholder="User name">
        <div *ngIf="username.invalid && username.touched">
          <div class="alert alert-danger"  *ngIf="username.errors['required']">
            Name is required.
          </div>
        </div>
      </div>

      <div class="form-group" [class.has-error]="password.invalid && password.touched">
        <label for="password">Password</label>
        <input id="password" type="password" required name="password" [(ngModel)]="model.password"
          #password="ngModel" class="form-control" placeholder="Password">
        <div *ngIf="password.invalid && password.touched">
          <div class="alert alert-danger"  *ngIf="password.errors['required']">
            Password is required.
          </div>
        </div>
      </div>

      <div class="checkbox">
        <label>
          <input type="checkbox" name="rememberMe" [(ngModel)]="model.rememberMe"> Remember me
        </label>
      </div>

      <div class="form-group">
        <button type="submit" class="btn btn-primary" [disabled]="form.invalid ">Login</button>
      </div>

      <div *ngIf="error" class="alert alert-danger " role="alert ">
        {{error}}
      </div>
    </form>
  </div>
</div>
در اینجا خاصیت‌های نام کاربری، کلمه‌ی عبور و همچنین rememberMe مدل، از کاربر دریافت می‌شوند؛ به همراه بررسی اعتبارسنجی سمت کلاینت آن‌ها؛ با این شکل:


برای آزمایش برنامه، نام کاربری Vahid و کلمه‌ی عبور 1234 را وارد کنید.


تکمیل کامپوننت Header برنامه

در ادامه، پس از لاگین موفق شخص، می‌خواهیم صفحه‌ی protectedPage را نمایش دهیم:


در این صفحه، Login از منوی سایت حذف شده‌است و بجای آن Logout به همراه «نام نمایشی کاربر» ظاهر شده‌اند. همچنین توکن decode شده به همراه تاریخ انقضای آن نمایش داده شده‌اند.
برای پیاده سازی این موارد، ابتدا از کامپوننت Header شروع می‌کنیم:
export class HeaderComponent implements OnInit, OnDestroy {

  title = "Angular.Jwt.Core";

  isLoggedIn: boolean;
  subscription: Subscription;
  displayName: string;

  constructor(private authService: AuthService) { }
این کامپوننت وضعیت گزارش شده‌ی ورود شخص را توسط خاصیت isLoggedIn در اختیار قالب خود قرار می‌دهد. برای این منظور به سرویس AuthService تزریق شده‌ی در سازنده‌ی خود نیاز دارد.
اکنون در روال رخ‌دادگردان ngOnInit، مشترک authStatus می‌شود که یک BehaviorSubject است و از آن جهت صدور رخ‌دادهای authService به تمام کامپوننت‌های مشترک به آن استفاده کرده‌ایم:
  ngOnInit() {
    this.subscription = this.authService.authStatus$.subscribe(status => {
      this.isLoggedIn = status;
      if (status) {
        this.displayName = this.authService.getDisplayName();
      }
    });
  }
Status بازگشت داده شده‌ی توسط آن از نوع boolean است و در صورت true بودن، خاصیت isLoggedIn را نیز true می‌کند که از آن در قالب این کامپوننت برای نمایش و یا مخفی کردن لینک‌های login و logout استفاده خواهیم کرد.
همچنین اگر این وضعیت true باشد، مقدار DisplayName کاربر را نیز از سرویس authService دریافت کرده و توسط خاصیت this.displayName در اختیار قالب Header قرار می‌دهیم.
در آخر برای جلوگیری از نشتی حافظه، ضروری است اشتراک به authStatus، در روال رخ‌دادگردان ngOnDestroy لغو شود:
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }

همچنین در قالب Header، مدیریت دکمه‌ی Logout را نیز انجام خواهیم داد:
  logout() {
    this.authService.logout(true);
  }

با این مقدمات، قالب Header اکنون به صورت ذیل تغییر می‌کند:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" [routerLink]="['/']">{{title}}</a>
    </div>
    <ul class="nav navbar-nav">
      <li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
        <a class="nav-link" [routerLink]="['/welcome']">Home</a>
      </li>
      <li *ngIf="!isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" queryParamsHandling="merge" [routerLink]="['/login']">Login</a>
      </li>
      <li *ngIf="isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" (click)="logout()">Logoff [{{displayName}}]</a>
      </li>
      <li *ngIf="isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" [routerLink]="['/protectedPage']">Protected Page</a>
      </li>
    </ul>
  </div>
</nav>
در اینجا توسط ngIfها وضعیت خاصیت isLoggedIn این کامپوننت را بررسی می‌کنیم. اگر true باشد، Logoff به همراه نام نمایشی کاربر را در منوی راهبری سایت ظاهر خواهیم کرد و در غیراینصورت لینک به صفحه‌ی Login را نمایش می‌دهیم.


تکمیل کامپوننت صفحه‌ی محافظت شده

در تصویر قبل، نمایش توکن decode شده را نیز مشاهده کردید. این نمایش توسط کامپوننت صفحه‌ی محافظت شده، مدیریت می‌شود:
import { Component, OnInit } from "@angular/core";
import { AuthService } from "../../core/services/auth.service";

@Component({
  selector: "app-protected-page",
  templateUrl: "./protected-page.component.html",
  styleUrls: ["./protected-page.component.css"]
})
export class ProtectedPageComponent implements OnInit {

  decodedAccessToken: any = {};
  accessTokenExpirationDate: Date = null;

  constructor(private authService: AuthService) { }

  ngOnInit() {
    this.decodedAccessToken = this.authService.getDecodedAccessToken();
    this.accessTokenExpirationDate = this.authService.getAccessTokenExpirationDate();
  }
}
در اینجا به کمک سرویس تزریقی AuthService، یکبار با استفاده از متد getDecodedAccessToken آن، مقدار اصلی توکن را و بار دیگر توسط متد getAccessTokenExpirationDate آن، تاریخ انقضای توکن دریافتی از سمت سرور را نمایش می‌دهیم؛ با این قالب:
<h1>
  Decoded Access Token
</h1>

<div class="alert alert-info">
  <label> Access Token Expiration Date:</label> {{accessTokenExpirationDate}}
</div>

<div>
  <pre>{{decodedAccessToken | json}}</pre>
</div>


کدهای کامل این سری را از اینجا می‌توانید دریافت کنید.
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه‌ی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشه‌ی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.