ASP.NET MVC #13
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- بهبود در توابع Table-Valued
- ظاهر جدید برای ویژوال استودیو 2010
- سورس نرم افزار اشتراک
- فریم ورک های سی اس اس را بهتر بشناسیم
- غزال مایکروسافت در راه است
- بررسی سایت ماهواره امید
- مروری بر سافاری 4
- صدا زدن یک Web service از طریق jquery
- نصب OTRS روی ویندوز ویستا
- آموزش کامل اسکریپت نویسی nsis - ساخت برنامه نصب
- توضیحی اجمالی در مورد singleton pattern
- MySQL Storage Engines
- مشکل بهم ریختگی متون فارسی انگلیسی در کامپیوتر
- ۴۸ نکته و اصل مهم در برنامه نویسی پی اچ پی
- فشرده سازی صفحات در ASP.net
امنیت
Visual Studio
ASP. Net
طراحی و توسعه وب
- ورود به دنیای jQuery plugins
- لیستی از 240 افزونهی جیکوئری
- و همچنین 20 مورد دیگر
- معرفی 13 screengrab webservices
- jQuery Chart Plugins
- برگههای تقلب وبی!
- خودتان را برای IE8 آماده کنید!
PHP
اسکیوال سرور
سی شارپ
عمومی دات نت
- Maestro ، زبان جدید دات نتی برای برنامه نویسی موازی
- چگونه تشخیص دهیم که یک اسمبلی دات نت به چه زبانی نوشته شده است؟
- ADO.NET Data Services v1.5 CTP1
ویندوز
مسایل اجتماعی و انسانی برنامه نویسی
متفرقه
وبلاگها ، سایتها و مقالات ایرانی (داخل و خارج از ایران)
- مروری بر Delphi 2009 و مهاجرت به آن
- تصحیح خودکار کلمات فارسی در برنامه اپنآفیس
- دانلود عکس از سایت
- ۱۰ قانون برای نوشتن در وب
- استفاده از SQL Server در PHP
- بلوهاست و ایران
- سورس و گزارش پروژه فارسی نت منتشر شد
- درون یک ISP چه می گذرد؟
- تابعهای جادویی PHP 5
- راهنمای کوچک همکاری در پروژههای بازمتن
- nsis قدرتمندتر از آنچه یک نصب کننده نرم افزار نیاز دارد!
- چند اشتباه رایج در طراحی وب
امنیت
Visual Studio
ASP. Net
طراحی و توسعه وب
PHP
اسکیوال سرور
سی شارپ
VB
CPP
عمومی دات نت
مسایل اجتماعی و انسانی برنامه نویسی
- چه برنامهنویسهایی را استخدام نکنید؟
- چرا خانمها کمتر در پروژههای سورس باز ظاهر میشوند؟!
- IE6 را نمیخواهیم!
متفرقه
وبلاگها و سایتهای ایرانی
امنیت
Visual Studio
ASP. Net
طراحی وب
PHP
- Aptana PHP 1.0 منتشر شد (اگر قبلا این IDE بسیار قابل توجه را دریافت کرده بودید فقط کافی است به منوی aptana و گزینه my aptana مراجعه کرده و از قسمت plugins ، این پلاگین 18 مگابایتی را دریافت کنید.)
اسکیوال سرور
سی شارپ
عمومی دات نت
ویندوز
متفرقه
- مزایای مهاجرت از ویژوال سورس سیف مایکرسافت به SVN
- افزونهای برای فایرفاکس جهت GUI prototyping (خیلی کار جالبی کرده ولی چرا به صورت یک افزونه؟)
IIS Hosting:
if (request.Properties.ContainsKey["MS_HttpContext"]) { var ctx = request.Properties["MS_HttpContext"] as HttpContextWrapper; if (ctx != null) { var ip = ctx.Request.UserHostAddress; } }
public static class HttpRequestMessageExtensions { private const string HttpContext = "MS_HttpContext"; public static string GetClientIpAddress(this HttpRequestMessage request) { if (request.Properties.ContainsKey(HttpContext)) { dynamic ctx = request.Properties[HttpContext]; if (ctx != null) { return ctx.Request.UserHostAddress; } } return null; } }
Self Hosting:
if (request.Properties.ContainsKey[RemoteEndpointMessageProperty.Name]) { var remote = request.Properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty; if (remote != null) { var ip = remote.Address; } }
ترکیب حالتهای قبلی:
»در این صورت دیگر نیازی به اضافه کردن اسمبلی System.ServiceModel نیست.
public static class HttpRequestMessageExtensions { private const string HttpContext = "MS_HttpContext"; private const string RemoteEndpointMessage = "System.ServiceModel.Channels.RemoteEndpointMessageProperty"; public static string GetClientIpAddress(this HttpRequestMessage request) { if (request.Properties.ContainsKey(HttpContext)) { dynamic ctx = request.Properties[HttpContext]; if (ctx != null) { return ctx.Request.UserHostAddress; } } if (request.Properties.ContainsKey(RemoteEndpointMessage)) { dynamic remoteEndpoint = request.Properties[RemoteEndpointMessage]; if (remoteEndpoint != null) { return remoteEndpoint.Address; } } return null; } }
public class MyHandler : DelegatingHandler { private readonly HashSet<string> deniedIps; protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (deniedIps.Contains(request.GetClientIpAddress())) { return Task.FromResult( new HttpResponseMessage( HttpStatusCode.Unauthorized ) ); } return base.SendAsync(request, cancellationToken); } }
Owin :
زمانی که از Owin برای هاست سرویسهای Web Api خود استفاده میکنید کمی روال انجام کار متفاوت خواهد شد. در این مورد نیز میتوانید از DelegatingHandlerها استفاده کنید. معرفی DelegatingHandler طراحی شده به Asp.Net PipeLine به صورت زیر خواهد بود:
public class Startup { public void Configuration( IAppBuilder appBuilder ) { var config = new HttpConfiguration(); var routeHandler = HttpClientFactory.CreatePipeline( new HttpControllerDispatcher( config ), new DelegatingHandler[] { new MyHandler(), } ); config.Routes.MapHttpRoute( name: "Default", routeTemplate: "{controller}/{action}", defaults: null, constraints: null, handler: routeHandler ); config.EnsureInitialized(); appBuilder.UseWebApi( config ); } }
public class IpMiddleware : OwinMiddleware { private readonly HashSet<string> _deniedIps; public IpMiddleware(OwinMiddleware next, HashSet<string> deniedIps) : base(next) { _deniedIps = deniedIps; } public override async Task Invoke(OwinRequest request, OwinResponse response) { var ipAddress = (string)request.Environment["server.RemoteIpAddress"]; if (_deniedIps.Contains(ipAddress)) { response.StatusCode = 403; return; } await Next.Invoke(request, response); } }
در نهایت برای معرفی این Middleware طراحی شده به Application، مراحل زیر را انجام دهید.
public class Startup { public void Configuration( IAppBuilder appBuilder ) { var config = new HttpConfiguration(); var deniedIps = new HashSet<string> {"192.168.0.100", "192.168.0.101"};
app.Use(typeof(IpMiddleware), deniedIps); appBuilder.UseWebApi( config ); } }
وارد کردن کتابخانه ها
این کتابخانه شامل دو فایل css و JS میباشد که بسته به محیطی که در آن کار میکنید متفاوت هستند. در این صفحه شما میتوانید برای 4 محیط Jquery ,JqueryUI , Bootstrap2 و Bootsrap3 بستهی مخصوصش را یا به صورت دانلود فایلها یا از طریق CDN دریافت نمایید. در اینجا هم یک دمو از قابلیتهای آن قابل مشاهده است.
برای شروع، کتابخانهی مورد نظر خود را دریافت و آنها را به صفحهی خود اضافه نمایید. در صورتیکه از Bootstrap استفاده میکنید، ابتدا فایلهای زیر را اضافه کنید:
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"> <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
اولین حالتیکه میتوانید با این کتابخانه کار کنید، استفاده از خاصیت *-data است. نمونه زیر را در نظر بگیرید:
<a href="#" id="favsite" data-type="text" data-pk="1" data-url="@Url.Action(MVC.Categories.EditCategory())" data-title="Enter your favorite site">dotnettips.info</a>
$(document).ready(function () { $('#favsite').editable(); });
$(document).ready(function () { $.fn.editable.defaults.mode = 'inline'; $('#favsite').editable(); });
حالت بعدی که میتوان استفاده کرد به شکل زیر است:
<a href="#" id="favsite" >dotnettips.info</a>
$.fn.editable.defaults.mode = 'inline'; $(document).ready(function () { $('#favsite').editable({ type: 'text', pk: 1, url: '@Url.Action(MVC.Categories.EditCategory())', title: 'Enter your favorite site' }); });
خوبی این روش این است که میتوان اطلاعات بیشتری چون رویدادها را به آن پاس داد. تا الان با نحوهی انتساب آن به اشیاء آشنا شدیم. اجازه دهید تا با خصوصیات آن آشنا شویم.
AjaxOptions | همانطور
که متوجه شدید به طور خودکار اطلاعات ویرایش شده، به سمت آدرس داده شده،
به شیوه Post ارسال میگردند. در صورتیکه قصد دست بردن
در نوع درخواست را دارید، میتوانید از این ویژگی استفاده کنید: ajaxOptions: { type: 'put', dataType: 'json' } |
Anim | این
ویژگی که تنها در حالت inline پاسخ میدهد، میتواند زمان بسته شدن
x-editable را تغییر دهد که به طور پیش فرض با false مقداردهی شده است. جهت
تغییر زمان بسته شدن، کد زیر را وارد نمایید: anim:'false' //or anim: { duration: 2000 } |
autotext | در انتهای جدول آمده است. |
defaultValue | در
صورتیکه عنصر مورد نظر محتوایی نداشته باشد و این خصوصیت را
مقداردهی کنید، موقع ویرایش، این عبارت تعیین شده نمایش مییابد. در مثال
بالا باید متن تگ a را حذف کرده تا نتیجه را ببینید: (البته فیلد value نباید مقداری داشته باشد) defaultValue: 'default val' //or defaultValue: undefined //or defaultValue: null |
disabled | false کردن این ویژگی باعث غیرفعال شدن x-editable بر روی کنترل جاری میگردد. |
display | خاصیت
display یا مقدار بولین false را دریافت میکند، یا نال، یا یک تابع
callback را میتوان به آن پاس داد. این خصوصت زمانی صدا زده میشود که
اطلاعات به سمت آدرس سرور رفته و با موفقیت بازگشت داده میشوند (در صورتی
که این ویژگی غیرفعال باشد، بلافاصه بعد از تایید کاربر، از اطلاعات وارد شده
صدا زده میشود) و سپس متن جدید عنصر تغییر مییابد. حال اگر این خاصیت نال
که مقدار پیش فرض آن است باشد، متن تغییر مییابد. ولی اگر false باشد، متن سابق
باقی خواهد ماند و در صورتیکه تابعی به آن پاس داده باشید، طبق تابع شما
عمل خواهد کرد. پارامترهایی که تابع شما میتواند داشته باشد به شرح زیر است: value : مقدار جدید response : پاسخ سرور ( در صورتی که ارسال از طریق Ajax صورت گرفته باشد) و در صورتیکه عنصر شما checlklist یا select باشد که حاوی منبعی از مقادیر هست، مقادیرشان در قالب یک آرایه با نام sourceData بازگشت خواهد خورد. برای دسترسی به آیتمهای انتخابی هم از کد زیر استفاده میکنیم: $.fn.editableutils.itemsByValue(value, sourceData) |
emptyclass | معرفی یک کلاس css برای موقعیکه عنصر خالی است. |
emptytext | در صورتی خالی بودن عنصر، این متن را برای عنصر نمایش بده. |
highlight | بعد از به روز رسانی متن عنصر، آن را با این رنگ highlight خواهد کرد و کد رنگی باید در مبنای هگز باشد. مقدار پیش فرض آن false است. |
mode | دو
حالت نمایشی دارد که پیش فرض آن popup است و با باز کردن یک پنجره، مقدار
جدید را دریافت میکند. مورد بعدی inline است که به جای باز کردن پنجره،
متن عنصر را به حالت ویرایش تغییر میدهد. |
name | نام فیلدی که مقدارش تغییر میکند. |
onblur | زمانی که کاربر فوکوس را از ویرایشگر میگیرد، ویرایشگر چه پاسخی باید به آن بدهد، باز بماند؟ ignore ، بسته شود؟ cancel و یا مقدار داده شده را تایید کند؟submit |
params | پارامترهای درخواست ایجکسی که کنترل در حالت پیش فرض ارسال میکند؛ شامل Pk که آن را با id
رکورد پر میکنیم. name نام فیلدی که تغییر یافته است و value که مقدار جدید
است. در صورتیکه دوست دارید اطلاعات اضافیتری نیز ارسال شوند،
میتوانید از این خاصیت استفاده کنید و پارامترها را در قالب Object به آن
پاس کنید. ولی اگر بخواهید در کل همهی پارامترها را رونویسی کنید باید یک
تابع را به آن پاس کنید: params: function(params) { //در این حالت پارامترهای پیش فرض ارسال نشده و تنها پارامترهای معرفی شده در این تابع ارسال میشوند params.a = 1; return params; } |
pk | کلید
اصلی رکورد شما در دیتابیس یا هان id است. در صورتی که از کلیدهای ترکیبی
استفاده میکنید، نگران نباشید فکر آن را هم کرده اند.//کلید عدد pk:1, //کلید رشته ای pk:'dp123' //کلید ترکیبی pk:{id: 1, lang: 'en'} //معرفی یک تابع به آن و بازگشت |
Placement | این ویژگی فقط به درد حالت Popup میخورد که پنجره را کجای عنصر نمایش دهد و شامل چهار مقدار left,right,top,bottom میشود. |
saveonchange | زمانی
که مقدار جدید، برابر مقدار فعلی باشد و این خاصیت false باشد، هیچ تغییری رخ
نخواهد داد. ولی اگر برابر true باشد ،مقدار جدید اسال و جایگزین مقدار فعلی
خواهد شد. مقدار پیش فرض آن false است. |
selector |
با استفاده از این خصوصیت در عنصر انتخابی به دنبال عناصری که در selector
تعیین شده میگردد و حالت ویرایش را روی آنها فعال میکند. در این حالت استفاده از خصوصیات emptytext و autotext در ابتدای امر ممکن نیست و بعد از اولین کلیک قابل استفاده هستند. نکته بعدی اینکه شما باید کلاسهای زیر را دستی اضافه کنید. کلاس editable-click برای همه کنترلها وکلاس editable-empty به کنترلهای بدون مقدار و برای مقداردهی کنترلهای بدون مقدار میتوان از خاصیت ''=data-value استفاده کرد. <div id="user"> <!-- empty --> <a href="#" data-name="username" data-type="text" data-value="" title="Username">Empty</a> <!-- non-empty --> <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a> </div> <script> $('#user').editable({ selector: 'a', url: '/post', pk: 1 }); </script> |
send | سه
مقدار auto,always و never را دریافت میکند. موقعی که شما آن را روی auto
تنظیم کنید؛ در صورتی مقادیر به سمت سرور ارسال میشوند که دو خاصیت url و
pk تعریف شده باشند. در غیر این صورت ویرایش فقط در حالت محلی و روی سیستم
کاربر رخ خواهد داد. |
showbuttons | در
صورتیکه با false مقداردهی شود، تایید فرم به طور خودکار انجام میگیرد و
اگر با یکی از مقادیر left یا Bottom پر شود، دکمهها را در آن قسمت نشان
میدهد. |
success | اطلاعات به سمت سرور
رفته و با موفقیت با کد 200 بازگشت داده شدهاند. در مستندات نوشته است، هر
کد وضعیتی غیر از 200 بازگشت داده شود، به سمت خاصیت error هدایت میشو.د ولی
آن طور که من با httpresponsemessage
تست کردم، چنین چیزی را مشاهده نکردم و مجددا success صدا زده شد. پس بهتر
هست دادهای را که به سمت کنترل برگشت میدهید، خودتان کنترل کنید. به خصوص
اگر انتقال اطلاعات صحیح باشد. ولی اگر در دیتابیس، تغییر با خطا روبرو گردد
بهتر است نتیجهی آن ارسال شده و از تغییر مقدار فعلی ممانعت به عمل آورید. success: function(response, newValue) { if(!response.success) return response.msg; } |
toggle | اگر قصد دارید که باز و بسته کردن ویرایشگر را بر عهدهی مثلا یک دکمهی روی صفحه بگذارید، این خصوصیت به شما کمک میکند:$('#edit-button').click(function(e) { e.stopPropagation(); $('#favsite').editable('toggle'); }); |
type | نوع
ویرایشی را که قرار است انجام گیرد، مشخص میکند. text برای متن، date برای
تاریخ، textarea جهت متون طولانیتر نسبت به text و بسیاری از موارد دیگر |
unsavedclass | این
کلاس موقعی اعمال میگردد که اطلاعاتی را ویرایش کردهاید، ولی اطلاعاتی
به سمت سرور ارسال نشده است. مثلا pk مقداردهی نشده یا send=never قرار
داید و یا اینکه به صورت محلی ذخیره میکنید و میخواهید در آخر همهی اطلاعات را
ارسال کنید. این خاصیت به طور پیش فرض با کلاس editable-unsaved مقداردهی شده که میتوانید با نال کردن، از شرش خلاص شوید. |
url | این
خاصیت با آدرس سمت سرور پر میشود. ولی میتوان به آن یک تابع هم پاس کرد که
این تابع جایگزین درخواست ایجکسی خودش خواهد شد و برای بازگشت دادن نتیجهی این درخواست به سمت تابعهای callback خودش میتوانید یک deferred object را برگشت دهید: url: function(params) { var d = new $.Deferred; if(params.value === 'abc') { //returning error via deferred object return d.reject('error message'); } else { //async saving data in js model someModel.asyncSaveMethod({ ..., success: function(){ d.resolve(); } }); return d.promise(); } } |
validate | مقدار
پیش فرض آن نال است و میتوان به آن یک تابع را جهت اعتبارسنجی سمت کلاینت پاس
داد. به عنوان آرگومان، مقدار جدیدی را ارسال کرده و در آن به اعتبارسنجی
میپردازید. در صورتی که مقدار، نامعتبر باشد، میتوانید یک پیام خطا از نوع
رشتهای را برگردانید. در صورتی که از نسخهی 1.5.1 به بعد استفاده میکنید، دریافت یک object با مقادیر زیر نیز ممکن شده است: newValue: مقدار جدید و جایگزین مقدار غیر معتبر. msg : پیام خطا. به کدهای زیر در سه حالت نگاه کنید: validate: function (value) { if ($.trim(value) == '') { //در تمامی نسخههای یک پیام متنی باز میگردد return 'This field is required'; //1.5.1 //یک مقدار جدید برگشت میدهد که بلافاصله آن را // تایید میکند و متن عنصر به روز میشود return { newValue: 'validated' }; //متن جدید ار ارسال کرده و پیام هشدار را نشان میدهد //ولی تایید نمیکند و منتظر تایید کاربر است return { newValue: 'validated', |
value | مقدار پیش فرضی که در ویرایشگر نشان میدهد و اگر مقداردهی نشود، از متن عنصر استفاده میکند. |
autotext | سه مقدار دارد auto (پیش فرض)،always و never. موقعیکه عنصر شما متنی نداشته باشد و روی auto تنظیم شده باشد، مقدار value را به عنوان متن عنصر نمایش میدهد. always کاری ندارد که عنصر شما متن دارد یا خالی است؛ مقدار value به آن انتساب داده خواهد شد. never هیچگاه. |
در قسمت بعدی که قسمت پایانی است مطالب را ادامه میدهیم.
Command چیست ؟
اینترفیس ICommand:
نام عضو | توضیحات |
Bool CanExecute(object parameter) | این تابع پارامتری از نوع object را دریافت میکند و یک مقدار bool را به خروجی تابع میفرستد. اگر مقدار خروجی متد، true باشدcommand اجرا خواهد شد و در غیر اینصورت اتفاقی رخ نخواهد داد. اغلب ازDelegate ها به عنوان پارامتر این تابع استفاده میشود؛Delegate های پیش ساختهای همچون Action,Predicate,Func |
Event EventHandler CanExecuteChanged | این رویداد برای آگاه سازی UI که به Command متصل است، استفاده میشود .بر اساس خروجی تابع CanExecute، این رویداد اتفاق میافتد. |
Void Execute(Object parameter) | این متد کار اصلی را در Commandها انجام میدهد. این متد تنها زمانی اجرا میشود که متدCanExecute مقدار true را بازگرداند. این تابع پارامتری را از نوع object دریافت میکند، اما عموما ما یکDelegate را به آن ارسال میکنیم. Delegate ارجاعی را به متدی، در خود نگاه میدارد که انتظار داریم در صورت اجرایcommand ، اجرا شود. |
چرا اینترفیس ICommand :
ساخت UI مورد نیاز :
گام دوم:
<Grid DataContext="{Binding Source={StaticResource calculatorVM}}" Background="#FFCCCC"> <Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition/> <RowDefinition Height="80"/> <RowDefinition Height="44"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" FontSize="25" VerticalAlignment="Top" HorizontalAlignment="Center" Foreground="Blue" Content="ICommand Sample"/> <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,0,0,0" VerticalAlignment="Bottom" FontSize="20" Content="First Input"/> <Label Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Margin="10,0,0,0" VerticalAlignment="Bottom" FontSize="20" Content="Second Input"/> <TextBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,0,0,0" FontSize="20" HorizontalAlignment="Left" Height="30" Width="150" TextAlignment="Center" Text="{Binding FirstValue, Mode=TwoWay}"/> <TextBox Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Margin="10,0,0,0" FontSize="20" HorizontalAlignment="Left" Height="30" Width="150" TextAlignment="Center" Text="{Binding SecondValue, Mode=TwoWay}"/> <Rectangle Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="4" Fill="LightBlue"/> <Button Grid.Row="2" Grid.Column="0" Content="+" Margin="10,0,0,0" HorizontalAlignment="Left" Height="50" Width="50" FontSize="30" Command="{Binding AddCommand}"/> <Label Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" FontSize="25" Margin="10,0,0,0" HorizontalAlignment="Left" Height="50" Content="Result : "/> <TextBlock Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2" FontSize="20" Margin="10,0,0,0" Background="BlanchedAlmond" TextAlignment="Center" HorizontalAlignment="Left" Height="36" Width="150" Text="{Binding Output}"/> </Grid>
xmlns:myview="clr-namespace:ICommnadSample.Views"
<Grid> <myview:CalculatorView/> </Grid>
گام سوم :
private double firstValue; private double secondValue; private double output; public double FirstValue { get { return firstValue; } set { firstValue = value; OnPropertyChanged("FirstValue"); } } public double SecondValue { get { return secondValue; } set { secondValue = value; OnPropertyChanged("SecondValue"); } } public double Output { get { return output; } set { output = value; OnPropertyChanged("Output"); } }
گام چهارم:
public class PlusCommand : ICommand { private CalculatorViewModel calculatorViewModel; public PlusCommand(CalculatorViewModel vm) { calculatorViewModel = vm; } public bool CanExecute(object parameter) { return true; } public void Execute(object parameter) { calculatorViewModel.Add(); } public event EventHandler CanExecuteChanged; }
گام پنجم:
private PlusCommand plusCommand; public CalculatorViewModel() { plusCommand = new PlusCommand(this); }
گام ششم:
xmlns:vm="clr-namespace:ICommnadSample.ViewModels"
<UserControl.Resources> <vm:CalculatorViewModel x:Key="calculatorVM" /> </UserControl.Resources>
گام هفتم:
public void Add() { Output = firstValue + secondValue; }
گام هشتم:
public ICommand AddCommand { get { return plusCommand; } }
نحوهی استفاده:
Command="{Binding AddCommand}"
public class ViewModelBase:INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
گام دهم:
public class CalculatorViewModel : ViewModelBase
OnPropertyChanged("FirstValue");
public class CalculatorViewModel : ViewModelBase { private double firstValue; private double secondValue; private double output; private PlusCommand plusCommand; public CalculatorViewModel() { plusCommand = new PlusCommand(this); } public double FirstValue { get { return firstValue; } set { firstValue = value; OnPropertyChanged("FirstValue"); } } public double SecondValue { get { return secondValue; } set { secondValue = value; OnPropertyChanged("SecondValue"); } } public double Output { get { return output; } set { output = value; OnPropertyChanged("Output"); } } public ICommand AddCommand { get { return plusCommand; } } internal void Add() { Output = firstValue + secondValue; } }
حال میتوانید برنامه را اجرا و تست کنید:
برای درک بهتر عملیات انقیاد دادها میتوانید به این مقاله مراجعه کنید.
پی نوشت: برای داشتن نیوگت شخصی سایتهای نظیر Nuget Server و Myget ( به همراه پشتیبانی از مخازن npm و Bower ) هم این سرویس را ارائه میکنند. ولی باید هزینهی آن را پرداخت کنید. البته سایت GemFury مخازن رایگان مختلفی چون Nuget را نیز پشتیبانی میکند.
نصب بر روی یک سیستم شخصی یا لوکال
در اولین قدم، شما باید یک دایرکتوری را در سیستم خود درست کنید تا پکیجهای خود را داخل آن قرار دهید. پنجرهی Package manager Settings را باز کنید و در آن گزینهی Package Sources را انتخاب کنید. سپس در کادر باز شده، بر روی دکمهی افزودن، در بالا کلیک کنید تا در پایین کادر، از شما نام محل توزیع بسته و آدرس آن را بپرسد:
بعد از ورود اطلاعات، بر روی دکمهی Update کلیک کنید. از این پس این دایرکتوری، منبع پکیجهای شماست و برای دریافت پکیجها از این آدرس میتوانید از طریق منوی کشویی موجود در کنسول، پکیج جدید خودتان را انتخاب کنید:
اگر میخواهید میتوانید این دایرکتوری را به اشتراک بگذارید تا دیگر افراد حاضر در شبکهی محلی هم بتوانند آن را به عنوان منبع توزیع خود به سیستم اضافه کنند.
مرحلهی بعدی این است که از طریق ابزار خط فرمان نیوگت نسخه 3.3 پکیجهایتان را به دایرکتوری مربوطه انتقال دهید. نحوهی صدا زدن این دستور به شکل زیر است:
nuget init e:\nuget\ e:\nuget\test
nuget add GMap.Net.1.0.1.nupkg -source e:\nuget\test
ساخت منبع راه دور (اینترنت)
شما با استفاده از ویژوال استودیو و انجام چند عمل ساده میتوانید پکیجهای خودتان را مدیریت کنید. برای شروع، یک پروژهی تحت وب خام (Empty) را ایجاد کنید و در کنسول Nuget دستور زیر را وارد کنید:
Install-Package NuGet.Server
<appSettings> <!-- Set the value here to specify your custom packages folder. --> <add key="packagesPath" value="×\Packages" /> </appSettings>
(هر محلی که نصب کنید طبق الگوی مسیریابی، آدرس nuget را در انتها وارد کنید)
Dotnettips.info/nuget
در صورتی که قصد دارید مستقیما بستهای را به سمت سرور push کنید، از یک رمز عبور قدرتمند که آن را میتوانید در web.config، بخش apiKey وارد نمایید، استفاده کنید. اگر هم نمیخواهید، میتوانید در تگ RequiredApiKey در خصوصیت Value، مقدار false را وارد نمایید.
برای اینکار میتوانید از دستور زیر استفاده کنید:
nuget push GMap.Net.1.0.1.nupkg -source http://Dotenettips.info/nuget (ApiKey)
nuget setapikey -source https://www.dntips.ir/Nuget (ApiKey)
> ng generate <blueprint> <options>
ایجاد کامپوننتهای جدید توسط Angular CLI
دستور ایجاد یک کامپوننت جدید توسط Angular CLI به نحو زیر است:
> ng generate component customer
> ng g c customer
گزینههای ایجاد کدهای جدید در Angular CLI
اگر به اولین دستور بحث جاری دقت کنید، قسمت <options> نیز برای آن درنظر گرفته شدهاست. تعدادی از مهمترین گزینههایی را که در اینجا میتوان ذکر کرد به شرح زیر هستند:
گزینه | Alias (میانبر/نام مستعار) | توضیح |
flat-- | آیا باید برای آن پوشهای ایجاد نشود؟ (flat = بدون پوشه در اینجا) (پیش فرض آن ایجاد یک پوشهی جدید است). اگر میخواهیم ایجاد نشود، باید flat true-- را ذکر کرد. | |
inline-template-- | it- | آیا قالب کامپوننت، درون فایل ts. آن قرار گیرد؟ (پیش فرض آن، false است) |
inline-style-- | is- | آیا شیوه نامهی کامپوننت، داخل فایل ts. آن قرار گیرد؟ (پیش فرض آن، false است) |
spec-- | آیا فایل spec نیز تولید شود؟ (پیش فرض آن true است) اگر میخواهیم این فایل ایجاد نشود باید spec false-- را ذکر کرد. | |
view-encapsulation-- | ve- | تعیین نوع استراتژی view encapsulation مورد استفاده (مانند Emulated). |
change-detection-- | cd- | تعیین استراتژی change detection مورد استفاده (مانند OnPush). |
dry-run-- | d- | گزارش فایلهای تولیدی، بدون نوشتن و تولید آنها (پیش فرض آن false است) |
prefix-- | تعیین صریح prefix مورد استفادهی در حین مقدار دهی selectorها که در قسمت قبل در مورد آن بحث شد. |
برای مثال اگر خواستیم کامپوننتی را به همراه قالبها و شیوهنامههای inline (قرار گرفتهی داخل فایل ts. آن) تولید کنیم، میتوان از دستور ذیل کمک گرفت:
>ng generate component customer --inline-template --inline-style
>ng g c customer –it -is
اگر صرفا دستور ng generate component customer را اجرا کنیم (بدون هیچ گزینهی اضافهتری)، فایلهای ts (کلاس کامپوننت)، css (فایل شیوه نامه)، html (فایل قالب) و spec (فایل آزمون واحد کامپوننت) به صورت خودکار تولید خواهند شد.
همانطور که پیشتر نیز عنوان شد، اگر مطمئن نیستید که دستور درحال فراخوانی، چه فایلها و پوشههایی را ایجاد میکند، با ذکر پرچم dry-run-- و یا به صورت خلاصه d-، دستور مدنظر را شبیه سازی کنید تا صرفا گزارشی را از فایلهایی که قرار است تولید شوند، ارائه دهد.
نکتهی مهم دیگری که به همراه دستورات Angular CLI هستند، به روز رسانی خودکار فایل app.module.ts است:
>ng g c customer installing component create src\app\customer\customer.component.css create src\app\customer\customer.component.html create src\app\customer\customer.component.spec.ts create src\app\customer\customer.component.ts update src\app\app.module.ts
import { CustomerComponent } from './customer/customer.component'; @NgModule({ declarations: [ AppComponent, CustomerComponent ]})
مشاهدهی تغییرات انجام شدهی توسط Angular CLI به کمک سورس کنترل
همانطور که در قسمت قبل نیز عنوان شد، دستور ng new، کار آغاز یک مخزن Git را نیز به صورت خودکار انجام میدهد. در اینجا هر دستوری که توسط Angular CLI اجرا شود، به این مخزن کد commit خواهد شد.
برای مثال اگر کل پوشهی برنامه را توسط VSCode باز کنیم (کلیک راست در داخل ریشهی اصلی پروژه و انتخاب گزینهی Open With Code)، با مراجعهی به لیست تغییرات و بررسی diff آنها، به سادگی میتوان تشخیص داد که چه تغییراتی بر روی فایلها اعمال شدهاند.
ایجاد سایر اجزای جدید برنامه توسط Angular CLI
نام جزء | Alias | دستور |
service | s | ng g service customer-data |
pipe | p | ng g pipe init-caps |
class | cl | ng g class customer-model |
directive | d | ng g directive search |
interface | i | ng g interface orders |
enum | e | ng g enum gender |
module | m | ng generate module sales |
نکات تکمیلی
- در حین ایجاد یک directive جدید، پوشهای را برای آن ایجاد نمیکند. اگر میخواهید اینکار به صورت flat (بدون پوشه در اینجا) انجام نشود، گزینهی flat false-- را نیز قید کنید.
- در حین ایجاد یک سرویس جدید، اخطار «WARNING Service is generated but not provided, it must be provided to be used» را دریافت خواهید کرد. علت اینجا است که Angular CLI نمیداند که این سرویس را باید به کامپوننت خاصی اضافه کند یا به ماژول برنامه. به همین جهت یا باید به صورت دستی فایل src\app\app.module.ts را ویرایش و قسمت providers آنرا بر اساس نام این سرویس جدید تکمیل کرد و یا توسط سوئیچ m میتوان ماژول مدنظر را دقیقا ذکر کرد:
> ng g s sales -m app.module
این نکته در مورد تمام اجزایی که فایل app.module را به روز رسانی میکنند نیز صادق است. اگر برای مثال کامپوننتی قرار است ماژول جدید دیگری را به روز رسانی کند، میتوانید به صورت صریح نام ماژول آنرا قید کنید؛ در غیراینصورت از همان app.module پیش فرض استفاده خواهد شد.
- همانطور که مشاهده میکنید امکان تولید کلاس، اینترفیس و enum تایپاسکریپتی نیز در اینجا پیش بینی شدهاست. اگر خواستید کلاسی را درون پوشهی خاصی قرار دهید میتوانید محل پوشهی آنرا دقیقا ذکر کنید (در مورد اینترفیسها و enums و سایر اجزاء نیز به همین صورت):
> ng g cl models/customer
تغییر تنظیمات پیش فرض تولید کد پروژهی جاری
در قسمت قبل «تغییر پیش فرضهای عمومی Angular CLI» را بررسی کردیم. در اینجا نیز میتوان یکسری از خواص فایل angular-cli.json. را بازنویسی کرد؛ در قسمت defaults آن:
"defaults": { "styleExt": "css", "component": {} }
> ng set defaults.component.flat false > ng set defaults.directive.flat false > ng set defaults.styleExt sass
"defaults": { "styleExt": "sass", "component": { "flat": false }, "directive": { "flat": false } }
و اگر VSCode استفاده میکنید، به همراه intellisense کاملی در مورد اجزای مختلف این فایل json است (این intellisense را به صورت خودکار بر اساس اسکیمای این فایل و سرویس زبان Angular تهیه میکند).