بازخوردهای دوره
صفحات مودال در بوت استرپ 3
با فرض اینکه شما در فراخوانی مودال اول مشکلی نداشته باشید . فرض میکنیم شما عکس رو با ajax فراخوانی کرده و در مودال دوم نمایش می‌دهید . فقط کافیست شما در مودال اول یک دکمه قرا دهید و با اون دکمه مودال دوم رو فراخوانی کنید . یک فایل نمونه پیوست شد.
testmodal-(2).html

من خودم اکثر اوقات با json مقادیر مورد نظر رو در مودال نشون میدم ، چیزی شبیه به این :
  function gettData(code) {
        $.ajax({

            url: '@Url.Action(MVC.X.Y())',
            data: { code: code },
            type: "POST",
            success: function (data) {
                $("#title").html(data.Title);
                $("#user-image").html(data.imagePath); 
            },
            error: function (response) {

            }

        });
    }
رویداد کلیک ردیف‌های لیست داخل مودال اول :
     $(document).on('click', '.item', function () {
            
            getData($(this).data("code"));
            $("#SecondModal").modal('toggle');
        });
نظرات مطالب
راه اندازی StimulSoft Report در ASP.NET MVC
در مورد خطا، اگر فایل گزارش و دیتای آن موجود باشد شاید بتوان بررسی کرد.
در مورد متد SynchronizeBusinessObjects  و یا  Synchronize - برای RegData - هم برای ایجاد ساختار دیتای ارسالی به گزارش در قسمت Dictionary استفاده می‌شود و اجباری به استفاده از آن نیست. می‌توان بر اساس پارامتر maxLevel  تعداد لایه‌های نمایش را مقداردهی کرد.
در مثال بالا به صورت زیر نتایج خروجی آن را با هم مقایسه کنید:
report.RegBusinessObject("CustomerServiceReport", data);

report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportNoSync.mrt"));

report.Dictionary.SynchronizeBusinessObjects();
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync.mrt"));

report.Dictionary.SynchronizeBusinessObjects(0);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync0.mrt"));

report.Dictionary.SynchronizeBusinessObjects(1);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync1.mrt"));

report.Dictionary.SynchronizeBusinessObjects(2);
report.Save(StiNetCoreHelper.MapPath(this, "Reports/CustomerServiceReportSync2.mrt"));

NoSync

Sync

 Sync0

Sync1

Sync2


نظرات مطالب
VS Code برای توسعه دهندگان ASP.NET Core - قسمت سوم - گردش کاری‌های متداول
یک نکته‌ی تکمیلی: انتخاب Target Framework ترجیح داده شده در VSCode

اگر در حال تهیه‌ی یک کتابخانه‌ی چند Target ای هستید، برای مثال تنظیمات فایل csproj آن چنین شکلی را پیدا می‌کند:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;netstandard2.1;netstandard1.3;net451;net461;</TargetFrameworks>
اما مشکلی که با VSCode وجود دارد این است که نمی‌توان Target Framework ترجیح داده شده‌ی در حین توسعه را انتخاب کرد. برای مثال VSCode را وادار کرد که صرفا بر اساس netstandard1.3 در این لیست، کار نمایش خطاهای موجود را انجام دهد. روش انتخاب Target Framework غالب در اینجا، ذکر آن «در ابتدای لیست» است. برای نمونه در مثال فوق، VSCode و افزونه‌ی #C آن بر اساس netstandard2.0 محیط توسعه را آماده می‌کند. اگر می‌خواهید بر اساس netstandard1.3 کار کند، آن‌را در ابتدای لیست قرار دهید.

البته Rider در این زمینه امکانات بیشتری را دارد. برای مثال در status bar آن، امکان انتخاب Target Framework ترجیح داده شده از طریق منویی وجود دارد:
 

نظرات مطالب
دادن «حق فراموش شدن» به کاربران در ASP.NET Core Identity 2.1
سلام و تشکر
در مورد "قسمتی را هم در آن درنظر بگیرید که کاربر با فشردن یک کلیک، بتواند اکانت فعلی خودش را برای همیشه محو و نابود کند؛ بدون اینکه اثری از آن باقی بماند "
دو سوال داشتم.
ممکنه آی دی کاربر به صورت کلید خارجی در جداول دیگر استفاده شده باشه. در اینصورت وقتی بخواهیم اطلاعات کاربر را حذف کنیم چطور باید این مشکل فعالیت‌های کاربر که به صورت کلید خارجی در جداول دیگر داریم رو حذف نکنیم؟ مثلا در همین سایت اگر من مطالبی ثبت کرده باشم و بخواهم اکانتم رو حذف کنم باید اون مطالبی هم که قبلا در سایت ثبت کردم کاملا حذف بشه؟ اگر اینطوری باشه به نظرم یه ایرادت دیگری به وجود میاد.

همینطور در مورد "
با کلیک بر روی یک دکمه، کلیه اطلاعات شخصی خودش را دانلود و ذخیره کند."

این اطلاعات به صورت Json برای کاربر نمایش داده میشه تا دانلود کنه. خوب امکان برعکسش هم باید داشته باشیم؟ یعنی مثلا من در سایتی  اکانتم رو حذف کرده بودم ولی حالا به دلایلی میخوام اکانتم رو دوباره برگردانم [شبیه اینستاگرام] باید اون فایل Json رو دوباره روی سایت آپلود کنم تا همان اطلاعات کاربری قبلی ام را برگردان کند؟

نظرات مطالب
بررسی ویجت Kendo UI File Upload
- «نمایش حداکثر اندازه مجاز فایل قابل آپلود به کاربر، در ASP.Net»  
- اگر قرار هست تعدادی read و write از و به بانک اطلاعاتی به صورت یک atomic operation عمل کنند، باید از تراکنش‌ها به صورت صریح استفاده کنید:
using (var transaction = new TransactionScope(TransactionScopeOption.Required,
 new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
    var database = new DatabaseContext();

    // DBC: BEGIN TRANSACTION

    var userA = database.Users.Find(1);
    var userB = database.Users.Find(2);
    userA.Name = "Admin";

    database.SaveChanges();

    userB.Age = 28;
    database.SaveChanges();


    // DBC: COMMIT TRANSACTION

    transaction.Complete();
}
اطلاعات بیشتر
«اصول پایگاه داده - تراکنش ها»
«آشنایی با TransactionScope»  
«مفهوم READ_COMMITTED_SNAPSHOT در EF 6»    
« بررسی Transactions و Locks در SQL Server»  
نظرات مطالب
حذف فضاهای خالی در خروجی صفحات ASP.NET MVC
با تشکر. من چندبار سعی کردم از روش حذف فواصل خالی استفاده کنم ولی هربار از خیرش گذشتم به این دلایل:
- در مرورگرهای قدیمی گاها باعث کرش و بسته شدن آنی برنامه می‌شد.
- کدهای جاوا اسکریپت یا CSS اگر داخل صفحه قرار داشتند، مشکل پیدا می‌کردند.
- گاهی از همین فضاهای خالی برای اندکی ایجاد فاصله بین عناصر ممکن است استفاده شود. این‌ها با حذف فواصل خالی به هم می‌ریزند.
- بعضی مرورگرها علاقمند هستند که doctype ابتدای یک فایل HTML، حتما در یک سطر مجزا ذکر شود.
- زمانیکه شما code‌ایی در صفحه تعریف می‌کنید، برای پردازش صحیح تگ PRE توسط مرورگر، مهم است که سطر جدیدی وجود داشته باشد، یا فاصله بین عناصر حفظ شود. در غیراینصورت کد نمایش داده شده به هم می‌ریزد.
- الگوریتم‌های فشرده سازی اطلاعات مانند GZIP یا Deflate، حداقل کاری را که به خوبی انجام می‌دهند، فشرده سازی فواصل خالی است.
نظرات مطالب
با ASP.MVC چه مزایایی را به دست خواهیم آورد
ممنون از راهنمایی شما. هنوز تصمیم قطعی گرفته نشده. در حقیقت چند تستی که در خصوص سیلورلایت انجام دادم کمی من رو ناامید کرد. چون بستر ارتباطی اینترنت و اینترانت هست پس حجم داده‌های ارسال شده به کاربر باید حداقل مقدار ممکن باشد که در مورد سیلورلایت با یک صفحه ساده و 4 عدد کنترل telerik حجم فایل xap به 2 مگ رسد؟! این یعنی فاجعه.
بین سیلورلایت و MVC تردید داشتیم تا حدی جواب سوالم رو گرفتم.
منظورم از سریعتر و راحت‌تر بیشتر تاکید بر روی امکان استفاده مجدد از یک کار تکراری در قسمتهای مختلف برنامه هست. مثلا نمایش لیست مقادیر بر اساس خصوصیات یک یا چند entity با شرطی خاص که ممکن هست بارها بارها در صفحات تکرار بشه. نگرانی من بیشتر در خصوص حذف کارهای تکراری برای برنامه نویس‌های معمولی هست.
و هنوز هم شک دارم IIS بتونه 10000 کاربر همزمان رو جوابگو باشه. آیا شما تجربه عملی با این حجم کاربر دارید؟
نظرات مطالب
ASP.NET MVC #11
با سلام
در صورتی که برای ایجاد فرم‌ها از خاصیت Scaffold template استفاده کنیم دستور BeginForm  مقادیر controller و Action  مقداری ندارند . هنگامی که فرم submit  می‌شود از بر چه اساسی Action  کنترلر مربوطه اجرا می‌شود ؟
کنترلر براساس مدل تعریف شده در ابتدای View انتخاب می‌شود ؟
Action  بر اساس نام فایل ؟
2- در تمامی مثالها از برای نمایش View‌ها از Model‌های ساده استفاده شده بود . در صورتی که بخواهم در یک view از Modeli  استفاده کنم که یک Property آن لیستی از یک Model  دیگر باشد به چه صورت است ؟
آیا باید view‌ها را همیشه به Model‌های جدا تفکیک کنیم ؟
3- هنگام سفارشی سازی model Binder در مثال شما مربوط به ورودی تاریخ شمسی و تبدیل آن به تاریخ میلادی کدی که در application_start اضافه شد مشخص می‌کرد در هر model  که  type فقط  از نوع Datetime  باشد اعمال گردد ؟
با توجه به مثال در صورتی که بخواهم فقط بر روی Create اعمال گردد در صورتی که معرفی binder را از Application_start حذف کنم بر روی تمامی Property ‌ها اعمال میگردد . در صورتی هم که حذف نکنم باز هم بر روی تمامی Action ‌ها اعمال می‌شود .

با تشکر
 
مطالب
Build Events
در ویژوال استودیو یک ویژگی جالب با عنوان Pre/Post-Build Event وجود دارد. این ویژگی به رویدادهای «قبل از بیلد» و «بعد از بیلد» اشاره دارد. از این ویژگی برای اجرای یکسری دستورات، قبل (Pre-build) یا بعد (Post-build) از عملیات بیلد استفاده میشود. دستوراتی که در این قسمت قابل اجرا هستند دقیقا همانند دستورات موجود در یک batch فایل میباشند. حتی میتوان یک فایل bat. را در این قسمت فراخوانی کرد. بطور خلاصه هرگونه دستوری که درون Command Prompt ویندوز یا در یک bat. فایل قابل اجرا باشد در این قسمت نیز قابل استفاده است. درنهایت تمام این دستورات توسط برنامه Cmd.exe اجرا میشوند.
نکته: قبل از ادامه بهتر است به این نکته اشاره کنم که مجموعه این دستورات چیزی فراتر از فراخوانی ساده یکسری فایل exe. هستند. درواقع کدی که در این قسمت به آن اشاره میشود، دارای ساختاری به صورت یک زبان برنامه نویسی ساده است. یعنی متن نهایی‌ای که برای اجرا به cmd.exe ارسال میشود میتواند شامل دستورات ساده و اولیه برنامه نویسی چون if .. then .. else و حلقه for و از این قبیل نیز باشد. برای آشنایی بیشتر با زبان این نوع دستورات به منابع زیر مراجعه کنید:

تنظیم رویدادهای بیلد (Build Events)
برای تنظیم این رویدادها باید به تب Build Events در صفحه پراپرتی‌های پروژه موردنظر مراجعه کنید. همانند تصویر زیر در یک پروژه کنسول #C:

البته در پروژه‌های VB.NET مسیر منتهی به این قسمت کمی فرق میکند که در تصویر زیر نشان داده شده است:

در پروژه‌های مربوط به زبانهای دیگر هم مسیر رسیدن به این رویدادها کمی متفاوت است. برای کسب اطلاعات بیشتر به اینجا مراجعه کنید.
در این قسمت میتوان همانند یک فایل batch دستورات موردنظر را در خطوط مجزا برای اجرا اضافه کرد. از این دستورات معمولا برای مدیریت عملیات بیلد، کپی فایلهای موردنیاز قبل یا بعد از بیلد، پاک کردن فولدرها، تغییر برخی تنظیمات با توجه به نوع کانفیگ بیلد (Debug یا Release)، ثبت یک اسمبلی در GAC و یا حتی اجرای برخی آزمونهای واحد و ... استفاده میشود.

نکته: درصورتیکه پروژه به روز باشد (یعنی ویژوال استودیو نیازی به تولید فایل اسمبلی نهایی پروژه به دلیل عدم وجود تغییری در کد برنامه نبیند) بدلیل عدم اجرای عملیات بید، دستورات قسمت Pre-build اجرا نمیشوند. اجرای دستورات قسمت Post-build نیز بستگی به تنظیمات قسمت :Run the post-build events همانند تصویر زیر دارد:

برای استفاده راحتتر از این ویژگی فرمی مخصوص وارد کردن این دستورات در ویژوال استودیو وجود دارد. برای دیدن این فرم بر روی دکمه ...Edit Pre-build یا ...Edit Post-build کلیک کنید. پنجره زیر نمایش داده میشود:

در این پنجره میتوان دستورات مورد نظر را وارد کرد. با اینکه هیچ امکان خاصی برای کمک به اضافه و ویرایش دستورات در این پنجره وجود ندارد! اما تنها ویژگی موجود در این فرم کمک بسیاری برای تکمیل دستورات موردنظر میکند. قبل از توضیح این ویژگی بهتر است با مفهوم Macro در این قسمت آشنا شویم.
 
Macro
در Build Events ویژوال استودیو یکسری متغیرهای ازقبل تعریف شده وجود دارد که به آنها Macro گفته میشود. برای مشاهده لیست این ماکروها روی دکمه << Macro کلیک کنید. پنجره مربوطه به صورت زیر گسترش می‌یابد تا جدولی به نام Macro Table را نمایش دهد:

همانطور که مشاهده میکنید تعداد 19 ماکرو به همراه مقادیرشان در این قسمت به نمایش گذاشته شده است. برای استفاده از این ماکروها کافی است تا روی یکی از آنها دابل کلیک کنید یا پس از انتخاب ماکروی موردنظر روی دکمه Insert کلیک کنید. دقت کنید که نحوه نمایش این ماکروها در متن دستورات به صورت زیر است:
$(<Macro_Name>)
که به جای عبارت <Macro_Name> عنوان ماکرو قرار میگیرد. مثلا:
$(OutDir)   یا   $(ProjectName)
نکته: نام این ماکروها case-sensitive نیست.

نحوه اجرای دستورات توسط ویژوال استودیو
ویژوال استودیو برای اجرای دستورات کار خاصی به صورت مستقیم انجام نمیدهد! وظیفه اصلی برعهده MSBuild (^) است. این ابزار پس از جایگزین کردن مقادیر ماکروها، محتوای کل دستورات موجود در هر یک از رویدادها را در یک فایل batch ذخیره میکند و فایل مربوط به هر رویداد را در زمان خودش به اجرا میگذارد. مثلا دستور زیر را درنظر بگیرید:
Copy $(OutDir)*.* %WinDir%
پس از ذخیره در فایل batch نهایی به صورت زیر در خواهد آمد:
Copy bin\Debug\*.* %WinDir%
نکته: در این زبان برنامه نویسی، عبارتی چون %WinDir% معرف یک متغیر است. در این مورد خاص این عبارت یک متغیر محیطی (Environment Variable) است. اطلاعات بیشتر در اینجا.

MSBuild عملیات اجرای این batch فایلهای تولیدی را زیر نظر دارد و هرگونه خطای موجود در این دستورات را به عنوان خطای زمان بیلد گزارش میدهد. اما از آنجاکه کل دستورات مربوط به هر رویداد درون یک فایل batch اجرا میشود، امکان گزارش محل دقیق خطای رخداده وجود ندارد. یعنی درصورتیکه مثلا تنها یکی از صدها خط دستور نوشته شده در این قسمت خطا بدهد تنها یک خطا و برای تمام دستورات نمایش داده میشود. البته همانطور که حدس میتوان حدس زد اجرای این دستورات ترنزکشنال نیست و اجرای تمامی دستورات تا قبل از وقوع خطا برگشت ناپذیر خواهند بود. برای نمونه به تصویر زیر و خطای نمایش داده شده دقت کنید:

نمونه اصلاح شده دستور فوق به صورت زیر است:
Copy "$(ProjectDir)$(OutDir)*.*" c:\test
نکته: به دلیل استفاده از کاراکتر فاصله به عنوان جداکننده آرگومانها در دستورات DOS، وجود فاصله در مسیرهای مورد استفاده در این دستورات عملیات را دچار خطا خواهد کرد. راه‌حل استفاده از کاراکتر " در ابتدا و انتهای رشته‌های مربوط به مسیرها همانند دستور بالاست.
نکته: درصورت استفاده از یک فایل bat. برای ذخیره دستورات، امکان استفاده مستقیم از ماکروهای ویژوال استودیو درون آن وجود نخواهد داشت! یکی از راه‌حلها پاس کردن این متغیرها به صورت پارامتر در زمان فراخوانی فایل bat. است. مثلا:
"$(ProjectDir)postBuild.bat"   "$(SolutionPath)"
برای دریافت این پارامترهای پاس شده درون batch فایل باید از عبارات 1% برای پارامتر اول و 2% برای پارامتر دوم و ... تا 9% برای پارامتر نهم است. برای کسب اطلاعات بیشتر به منابع معرفی شده در ابتدای مطلب مخصوصا قسمت Using batch parameters مراجعه کنید.
 
حال مجموعه دستورات زیر و خطای رخ داده را درنظر بگیرید:

با بررسی مطلب متوجه میشویم با اینکه خط اول مجموعه دستورات فوق درست بوده و کاملا صحیح اجرا میشود اما خطای رخ داده به کل دستورات اشاره دارد و مشخص نشده است که کدام دستور مشکل دارد. دقت کنید که دستور اول کاملا اجرا میشود!
راه حل ساده ای در اینجا برای حل این مشکل ارائه شده است. در این راه حل با استفاده از قابلیتهای این زبان، کل عملیات و مخصوصا خطاهای رخ داده در این مجموعه دستورات هندل میشود تا کنترل بهتری در این مورد بر روی فرایند وجود داشته باشد. نمونه این راه حل به صورت زیر است:
echo ---------------------------------------------------------------------------
echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --Starting...
Copy "$(ProjectDir)$(OutDir)*.*" c:\test
if errorlevel 1 goto error
echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --DONE!
echo ---------------------------------------------------------------------------
echo ---------------------------------------------------------------------------
echo Copy $(OutDir)*.* c:\test --Starting...
Copy $(OutDir)*.* c:\test
if errorlevel 1 goto error
echo Copy $(OutDir)*.* c:\test --DONE!
echo ---------------------------------------------------------------------------
goto ok
:error
echo POSTBUILDSTEP for $(ProjectName) FAILED
notepad.exe
exit 1
:ok
echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK
با استفاده از مجموعه دستوراتی شبیه دستورات بالا میتوان لحظه به لحظه اجرای عملیات را بررسی کرد.
نکته: خروجی تمام این دستورات و نیز خروجی دستورات echo در پنجره Output ویژوال استودیو به همراه سایر پیغامهای بیلد نمایش داده میشود.
نکته: در اسکرپیت فوق برای درک بیشتر مسئله با استفاده از دستور notepad.exe در قسمت error: از وقوع خطا اطمینان حاصل میشود. دقت کنید تا زمانیکه برنامه اجرا شده Notepad بسته نشود فوکس به ویژوال استودیو برنمیگردد و عملیات بیلد تمام نمیشود.
نکته: درصورت استفاده از دستور exit 0 در انتهای قسمت error: (به جای دستور exit 1 موجود) به دلیل اعلام خروج موفق از عملیات، ویژوال استودیو خطایی نمایش نخواهد داد و عملیات بیلد بدون نمایش خطا و با موفقیت به پایان خواهد رسید. درواقع استفاده از هر عددی غیر از صفر به معنی خروج با خطا است که این عدد غیرصفر کد خطا یا error level را مشخص میکند (^ و ^).

یکی از دستورات جالبی که میتوان در این رویدادها از آن استفاده کرد، دستور نصب نسخه ریلیز برنامه در GAC است. نحوه استفاده از آن میتواند به صورت زیر باشد:
if $(ConfigurationName) == Release (
gacutil.exe /i "$(SolutionDir)$(OutDir)$(TargetFileName)"
)

نکته: درصورتیکه در دستورات مربوط به رویداد قبل از بیلد یعنی Pre-build خطایی رخ بدهد عملیات بیلد متوقف خواهد شد و برای پروژه فایلی تولید نمیشود. اما اگر این خطا در رویداد بعد از بیلد یعنی Post-build رخ دهد با اینکه ویژوال استودیو وقوع یک خطا را گزارش میدهد اما فایلهای خروجی پروژه حاصله از عملیات بیلد تولید خواهند شد.
نکته: توجه داشته باشید که در استفاه از این ویژگی زیاده‌روی نباید کرد. استفاده زیاد و بیش از حد (و با تعداد زیاد دستورات) از این رویدادها ممکن است عملیات بیلد را دچار مشکلاتی پیچیده کند. دیباگ این رویدادها و دستورات موجود در آنها بسیار مشکل خواهد بود. اگر تعداد خطوط دستورات موردنظر زیاد باشد بهتر است کل دستورات را درون یک فایل bat. ذخیره کنید و این فایل را بطور جداگانه مدیریت کنید که کار راحتتری است.
نکته: بهتر است قبل از وارد کردن دستورات درون این رویدادها، ابتدا تمام دستورات را در یک پنجره cmd آزمایش کنید تا از درستی ساختار و نتیجه آن‌ها مطمئن شوید.

رویدادهای بیلد و MSBuild
همانطور که در اینجا توضیح داده شده است، ویژوال استودیو از ابزار MSBuild برای تولید اپلیکیشنها استفاده میکند. عملیات مدیریت رویدادهای بیلد نیز توسط این ابزار انجام میشود. اگر به فایل پروژه مربوط به مثال قبل مراجعه کنید به محتوایی شبیه خطوط زیر میرسید:
...
<PropertyGroup>
<PostBuildEvent>echo ---------------------------------------------------------------------------
echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --Starting...
Copy "$(ProjectDir)$(OutDir)*.*" c:\test
if errorlevel 1 goto error
echo Copy "$(ProjectDir)$(OutDir)*.*" c:\test --DONE!
echo ---------------------------------------------------------------------------
echo ---------------------------------------------------------------------------
echo Copy $(OutDir)*.* c:\test --Starting...
Copy $(OutDir)*.* c:\test
if errorlevel 1 goto error
echo Copy $(OutDir)*.* c:\test --DONE!
echo ---------------------------------------------------------------------------
goto ok
:error
echo POSTBUILDSTEP for $(ProjectName) FAILED
notepad.exe
exit 1
:ok
echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK</PostBuildEvent>
</PropertyGroup>
...
همانطور که میبینید در ویژوال استودیو تنها ذخیره این تنظیمات در فایل پروژه انجام میشود و کلیه عملیات توسط ابزار MSBuild مدیریت میگردد. امکان بهر برداری از این رویدادها با استفاده مستقیم از ابزار MSBuild نیز وجود دارد اما به دلیل مفصل بودن بحث، جستجوی بیشتر به خوانندگان واگذار میشود.
منابع برای مطالعه بیشتر:
مطالب
آشنایی با WPF قسمت چهارم: کنترل ها
WPF همانند Windows Form شامل ابزارها یا کنترل‌های داخلی است که می‌توانند در تهیه‌ی یک برنامه بسیار کارآمد باشند. در این بخش به بررسی تعدادی از این کنترل‌ها می‌پردازیم و مابقی آن‌ها را در قسمت‌های آینده بررسی خواهیم کرد. در این نوشتار سعی بر این است که یک فرم ساده را با آن ایجاد کرده و مورد استفاده قرار دهیم.
این فرم دارای اطلاعاتی شامل : نام، جنسیت ، زمینه‌های کاری، کشور، تاریخ تولد و تصویر می‌باشد.

TextBlock

همان Label قدیمی خودمان است که برای نمایش متون کاربر دارد. متن داخل آن بین دو تگ قرار می‌گیرد و یا از خاصیت Text آن کمک گرفته خواهد شد. حتما از خاصیت Width و height آن برای مقداردهی کمک بگیرید، زیرا در غیر آن صورت کل Container خود را خواهد پوشاند. در صورتی که متنی در مکان خود جا نشود می‌توان از دو ویژگی استفاده کرد. آن را برش داد یا به خطوط بعدی شکست. برای حذف یا برش باقی مانده متن می‌توان از خصوصیت TextTrimming استفاده کرد که سه مقدار می‌گیرد:

None مقدار پیش فرض

CharacterEllipsis با نزدیک شدن به آخر پهنای کار از ... استفاده می‌نماید. در صورتی که لیستی یا مورد مشابهی دارید میتواند بسیار کاربردی باشد.

WordEllipsis این گزینه هم مانند مورد بالاست با این تفاوت که سعی دارد تا آنجا که ممکن است خود را به آخرین حرف کلمه برساند تا شکستگی در وسط کلمه اتفاق نیفتد و آخرین کلمه کامل دیده شود و بعد ... قرار بگیرد؛ هر چند در تست‌های خودم تفاوتی مشاهده نکردم.

گزینه TextWrapping جهت شکستن یک خط به خطوط است؛ موقعی که متن شما به انتهای صفحه می‌رسد، این ویژگی باعث می‌شود متن به بیرون از پنجره نرفته و یک خط به سمت پایین حرکت کند. این گزینه سه مقدار را دارد:

تصویر زیر حالت اصلی نمایش بدون نیاز به Wrap شدن است:

None: مقدار پیش فرض که خصوصیت Wrap را به همراه ندارد.

Wrap: فعال سازی ویژگی TextWrapping

WrapWithOverflow: فرق این گزینه با گزینه بالا در این است که ، گزینه بالا در هر موقعیتی که پیش بیاید عمل خط شکن را روی عبارت یا حتی آن کلمه انجام می‌دهد. ولی در این گزینه فرصت خط شکنی مثل بالا فراهم نیست و اگر روی کلمه‌ای خط شکنی رخ دهد، مابقی آن کلمه از ناحیه‌ی خودش خارج شده و از کلمه‌ی بعدی، خط شکنی صورت می‌گیرد.

خصوصیت LineStackingStrategy:

این خصوصیت فاصله‌ی بین خطوط را با استفاده از یک واحد منطقی dp مشخص می‌کند. هر چند دو گزینه دیگر هم دارد که دو تصویر زیر را در این صفحه به شما نمایش می‌دهد:

برای ساخت فرم از یک گرید با سه ستون و 6 سطر استفاده می‌کنم.

<Grid Margin="5">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        
        </Grid.RowDefinitions>
</Grid>
در ستون اول نام فیلدهای مورد نظر را می‌نویسیم و در ستون دوم هم کنترل‌های مد نظر هر فیلد را قرار خواهیم داد. در صورتی که دوست دارید کار از راست به چپ پشتیبانی کند از گزینه OverflowDirection در تگ پنجره Window استفاده نمایید.
در داخل گرید بعد از تعریف سطر و ستون، همانطور که قبلا توضیح دادیم کنترل‌های TextBlock را اضافه می‌کنیم:
<TextBlock Grid.Column="0"  Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Left" >Name</TextBlock>
        <TextBlock Grid.Column="0"  Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Left" >Gender</TextBlock>
        <TextBlock Grid.Column="0"  Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Left" >Field Of Work</TextBlock>
        <TextBlock Grid.Column="0"  Grid.Row="3" VerticalAlignment="Center" HorizontalAlignment="Left" >Country</TextBlock>
        <TextBlock Grid.Column="0"  Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" >Birth Date</TextBlock>

<TextBox Grid.Row="0" Grid.Column="1" Name="Txtname" HorizontalAlignment="Left" Margin="5" Width="200" ></TextBox>
برای فیلد نام، از کنترل TextBox استفاده کردم که با محدود کردن Width آن اندازه ثابت به آن دادم. در صورتی که width ذکر نشود یا به Auto ذکر شود، در صورتی که متنی که کاربر تایپ می‌کند، بیش از اندازه تعیین شده کنترل Textbox باشد، کنترل هم همراه متن بزرگتر خواهد شد و تا پایان محدوده سلولی اش در گرید کش خواهد آمد.

Buttons 
برای فیلد جنسیت Gender هم از RadioButton کمک گرفتم که با استفاده از خاصیت GroupName می‌توان دسته‌ای از این کنترل‌ها را با هم مرتبط ساخت تا با انتخاب یک آیتم جدید از همان گروه، آیتم قبلی که انتخاب شده بود از حالت انتخاب خارج شده و آیتم جدیدی انتخاب شود. از خاصیت IsChecked می‌توان برای انتخاب یک آیتم بهره برد.

به صورت کلی دکمه‌ها به چند دسته زیر تقسیم می‌شوند:

  • Button
  • ToggleButton
  • CheckBox
  • RadioButton

که همگی این عناصر از کلاسی به نام ButtonBase مشتق شده اند. کد زیر RadioButton‌ها را به صورت عمودی چینش کرده است:

<StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="1" Margin="10">
            <RadioButton GroupName="Gender" Name="RdoMale" IsChecked="True" >Male</RadioButton>
            <RadioButton GroupName="Gender" Name="RdoFemale" Margin="0 5 0 0" >Female</RadioButton>
        </StackPanel>
برای فیلد زمینه کاری ، لیست کشورها و تاریخ تولد از کدهای زیر کمک گرفتم:
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1" Margin="10">
            <CheckBox Name="ChkActor" >Actor/Actress</CheckBox>
            <CheckBox Name="ChkDirector" >Director</CheckBox>
            <CheckBox Name="ChkProducer" >Producer</CheckBox>
        </StackPanel>

        <ListBox Grid.Row="3" Grid.Column="1" Margin="10"  Height="80">
        <ListBoxItem>
                <TextBlock>UnitedStates</TextBlock>
            </ListBoxItem>
            <ListBoxItem>
                <TextBlock >UK</TextBlock>
            </ListBoxItem>
            <ListBoxItem>
                <TextBlock >France</TextBlock>
            </ListBoxItem>
            <ListBoxItem>
                <TextBlock >Japan</TextBlock>
            </ListBoxItem>
        </ListBox>

        <Calendar Grid.Row="4" Grid.Column="1" HorizontalAlignment="Left" Margin="10"></Calendar>
برای لیست کشورها می‌توان از یک ListBox یا ComboBox استفاده کرده که هر آیتم داخل آنها در یک تگ ListBoxItem یا ComboBoxItem قرار می‌گیرد. اگر از حالت ListBox استفاده می‌کنید، در صورتی که آیتم‌ها از ارتفاع لیست بیشتر شود به طور خودکار یک Scrollbar برای آن‌ها در نظر گرفته خواهد شد و نیازی نیست که آن را دستی اضافه کنید.
برای تصویر شخص، قصد دارم آن را در گوشه‌ی سمت راست و بالا قرار دهم. برای همین محل ستون آن را ستون سوم یا اندیس دوم انتخاب کرده و از آنجا که این عکس حالت پرسنلی دارد، می‌تواند چند سطر را به خود اختصاص دهد که با کمک خاصیت Rowspan، چهار سطر، کنترل را ادامه دادم. برای ستون‌ها هم می‌توان از خاصیت ColumnSpan استفاده کرد. همچنین دوست دارم یک دکمه هم روی تصویر در گوشه‌ی سمت چپ و پایین قرار داده که کاربر با انتخاب آن به انتخاب عکس یا تغییر آن بپردازد. برای همین از یک پنل گرید استفاده کردم و کنترل دکمه را روی تصویر قرار دادم. همپوشانی کنترل‌ها در اینجا صورت گرفته است.

 <Grid Grid.Row="0" Grid.Column="2" Grid.RowSpan="4">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Image HorizontalAlignment="Right" Source="man.jpg" Stretch="UniformToFill" VerticalAlignment="Top" Width="100" Height="150"></Image>
            <Button Width="25" Height="15"    Padding="0"  HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="0,0,0,83">
                 <TextBlock VerticalAlignment="Center" Margin="0 -7 0 0">...</TextBlock>
            </Button>
        </Grid>
خاصیت Stretch کنترل Image در بالا، نحوه‌ی نمایش تصویر را نشان می‌دهد که چهار مقدار دارد:
None: تصویر، اندازه‌ی اصلی خود را حفظ کرده و هر مقدار آن که در کنترل جا شود، نمایش می‌یابد و بسته به سایز تصویر ممکن است گوشه هایی از تصویر نمایش نیابد.
Fill: تصویر را داخل کنترل به زور جا داده تا پهنا و ارتفاع عکس، هم اندازه کنترل می‌شود.
Uniform: تصویر بزرگ را با در نظر گرفتن نسبت پهنا و ارتفاع تصویر، با یکدیگر در کنترل جا می‌دهد.
UniformToFill: تصویر، کل کنترل را می‌گیرد ولی نسبت پهنا و عرض را حفظ کرده ولی قسمت هایی از تصویر در کنترل دیده نمی‌شود.

همانطور که قبلا هم گفتیم، خود کنترل دکمه شامل زیر کنترل‌هایی می‌شود که یکی از آن‌ها TextBlock است و از طریق خصوصیت *.TextBlock دیگر خصوصیات آن قابل تنظیم است و البته برای خصوصی سازی بیشتر هم می‌توان یک TextBlock را به صورت Nested یعنی داخل تگ Button تعریف کنید که ما همین کار را کرده ایم.
فرم نهایی ما به صورت زیر است:


در صورتی که دوست دارید جهت ListBox را از عمودی به افقی تغییر دهید می‌توانید از پنل‌های Stack یا Wrap استفاده کنید که تعریف آن به شکل زیر است:
<ListBox>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

بدین صورت ListBox به شکل زیر تغییر می‌یابد:



و در صورتی که می‌خواهید Scroll حذف شود و از Wrap استفاده کنید، کد را به شکل زیر تعریف کنید. فراموش نکنید که اسکرول افقی را غیرفعال کنید؛ وگرنه نتیجه کار به شکل بالا خواهد بود.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>
نتیجه:

Calendar
تقویم یکی دیگر از کنترل‌های موجود است که شامل خصوصیات زیر است:
DisplayDate: تاریخ پیش فرض و اولیه تقویم را مشخص می‌کند؛ در صورتی که ذکر نشود تاریخ جاری درج می‌شود.
<Calendar DisplayDate="01.01.2010" />
از خصوصیات دیگر در این زمینه می‌توان به DisplayDateStart و DisplayDateEnd اشاره کرد که محدوه‌ی نمایش تاریخ تقویم را مشخص می‌کند. کد زیر تنها تاریخ‌های روز اول ماه ابتدای سال 2015، تا روز اول ماه پنجم 2015 را نمایش می‌دهد:
<Calendar DisplayDateStart="01.01.2015" DisplayDateEnd="05.01.2015" />


SelectionMode: نحوه‌ی انتخاب تاریخ را مشخص می‌کند:
SingleDate: فقط یک تاریخ قابل انتخاب است.
SingleRange: می‌توانید از یک تاریخ تا تاریخ دیگر را انتخاب کنید. ولی نمی‌توانید مجددا چند انتخاب دیگر را در جای جای تقویم داشته باشید. مثلا از تاریخ 5 آپریل تا 10 آپریل را انتخاب کرده‌اید؛ ولی دیگر نمی‌توانید تاریخ 15 آپریل یا محدوده‌ی 15 آپریل تا 20 آپریل را انتخاب کنید. چون تنها قادر به انتخاب یک رنج یا محدوده تاریخی هستید.
MultipleRanges: بر خلاف گزینه‌ی بالایی هر محدوده تاریخی قابل انتخاب است.
<Calendar SelectionMode="MultipleRange" />

نکته بعدی در مورد غیرفعال کردن بعضی از تاریخ هاست که شما قصد ندارید به کاربر اجازه دهید آن‌ها را انتخاب کند. برای مثال تاریخ‌های 1 آپریل تا 10 آپریل را از دسترس خارج کنید. برای همین از خصوصیت BlackoutDates استفاده می‌کنیم که نحوه‌ی تعریف آن به شرح زیر است که در این کد دو محدوده‌ی تاریخی غیر فعال شده اند:
<Calendar>
    <Calendar.BlackoutDates>
        <CalendarDateRange Start="01/01/2010" End="01/06/2010" />
        <CalendarDateRange Start="05/01/2010" End="05/03/2010" />
    </Calendar.BlackoutDates>
</Calendar>



DisplayMode
: به طور پیش فرض، تقویم ماه‌ها را نشان می‌دهد ولی میتوانید آن را توسط این خصوصیت روی سال یا دهه و ماه هم تنظیم کنید.
با انتخاب سال Year، تقویم ماه‌های یک سال را نمایش می‌دهد.
با انتخاب دهه Decades سال‌های یک دهه‌ی تعیین شده را نشان می‌دهد و با انتخاب ماه Month روز‌های هر ماه در آن نمایش داده می‌شود.
در هنگام انتخاب این گزینه، به داخل تقویم نگاه نکنید، بلکه به سر تیتر آن نگاه کنید.
<Calendar DisplayMode="Year" />