اشتراکها
پس از تهیه ساختار اولیهی بلاگی مبتنی بر ember.js در قسمت قبل، در ادامه قصد داریم امکانات تعاملی را به آن اضافه کنیم. بنابراین کار را با تعریف کنترلرها که تعیین کنندهی رفتار برنامه هستند، ادامه میدهیم.
اضافه کردن دکمهی More info به صفحهی About و مدیریت کلیک بر روی آن
فایل Scripts\Templates\about.hbs را گشوده و سپس محتوای فعلی آن را به نحو ذیل تکمیل کنید:
در ember.js اگر قصد مدیریت عملی را که قرار است توسط کلیک بر روی المانی رخ دهد، داشته باشیم، میتوان از handlebar helper ایی به نام action استفاده کرد. سپس برای تهیه کدهای مرتبط با آن، این اکشن را باید در کنترلر متناظر با route جاری (مسیریابی about) اضافه کنیم.
به همین جهت فایل جدید Scripts\Controllers\about.js را در پوشهی کنترلرهای سمت کاربر اضافه کنید (نام آن با نام مسیریابی یکی است)؛ با این محتوا:
کنترلرها به صورت یک خاصیت جدید به شیء Application برنامه اضافه میشوند. مطابق اصول نامگذاری ember.js، نام خاصیت کنترلر با حروف بزرگ متناظر با route آن شروع میشود و به نام Controller ختم خواهد شد. به این ترتیب ember.js هرگاه قصد پردازش مسیریابی about را داشته باشد، میداند که باید از کدام شیء جهت پردازش اعمال کاربر استفاده کند.
در ادامه این خاصیت را با تهیه یک زیرکلاس از کلاس پایه Controller تهیه شده توسط ember.js مقدار دهی میکنیم. به این ترتیب به کلیه امکانات این کلاس پایه دسترسی خواهیم داشت؛ به علاوه میتوان ویژگیهای سفارشی را نیز به آن افزود. برای مثال در اینجا در قسمت actions آن، دقیقا مطابق نام اکشنی که در فایل about.hbs تعریف کردهایم، یک متد جدید اضافه شدهاست.
پس از تعریف کنترلر about.js نیاز است مدخل متناظر با آنرا به فایل index.html برنامه نیز در انتهای تعاریف موجود، اضافه کرد:
اکنون یکبار برنامه را اجرا کرده و در صفحهی about بر روی دکمهی more info کلیک کنید.
اضافه کردن دکمهی ارسال پیام خصوصی به صفحهی Contact و مدیریت کلیک بر روی آن
در ادامه به قالب فعلی Scripts\Templates\contact.hbs یک دکمه را جهت ارسال پیام خصوصی اضافه میکنیم.
سپس برای مدیریت اکشن جدید sendMessage نیاز است کنترلر آنرا نیز تعریف کنیم. با توجه به نام مسیریابی جاری، نام این کنترلر نیز contact خواهد بود. برای این منظور ابتدا فایل جدید Scripts\Controllers\contact.js را اضافه نمائید؛ با این محتوا:
همچنین مدخل متناظر با فایل contact.js نیز باید به صفحهی index.html اضافه شود:
نمایش تصویری تعاملی در صفحهی about
تا اینجا با نحوهی تعریف اکشنها در قالبها و مدیریت آنها توسط کنترلرهای متناظر آشنا شدیم. در ادامه قصد داریم با اصول binding اطلاعات در ember.js آشنا شویم. برای مثال فرض کنید میخواهیم دکمهای را در صفحهی about قرار داده و با کلیک بر روی آن، لوگوی ember.js را که به صورت یک تصویر مخفی در صفحه قرار دارد، نمایان کنیم. برای اینکار نیاز است خاصیتی را در کنترلر متناظر، تعریف کرده و سپس آنرا به template جاری bind کرد.
برای این منظور فایل Scripts\Templates\about.hbs را گشوده و تعاریف موجود آنرا به نحو ذیل تکمیل کنید:
در اینجا بر اساس مقدار خاصیت isAuthorShowing تصمیم گیری خواهد شد که آیا تصویر لوگوی ember.js نمایش داده شود یا خیر. همچنین دو اکشن نمایش و مخفی کردن تصویر نیز اضافه شدهاند که با کلیک بر روی هر کدام، سبب تغییر وضعیت خاصیت isAuthorShowing خواهیم شد.
کنترلر about (فایل Scripts\Controllers\about.js) جهت مدیریت این خاصیت جدید، به همراه دو اکشن تعریف شده، اینبار به نحو ذیل تغییر خواهد یافت:
ابتدا خاصیت isAuthorShowing به کنترلر اضافه شدهاست. از این خاصیت بار اولی که مسیر http://localhost:25918/#/about توسط کاربر درخواست میشود، استفاده خواهد شد.
سپس در دو متد showAuthor و hideAuthor که به اکشنهای دو دکمهی جدید تعریف شده در قالب about متصل خواهند شد، نحوهی تغییر مقدار خاصیت isAuthorShowing را توسط متد set ملاحظه میکنید.
این قسمت مهمترین تفاوت ember.js با jQuery است. در jQuery مستقیما المانهای صفحه در همانجا تغییر داده میشوند. در ember.js منطق مدیریت کنندهی رابط کاربری و کدهای قالب متناظر با آن از هم جدا شدهاند تا بتوان یک برنامهی بزرگ را بهتر مدیریت کرد. همچنین در اینجا مشخص است که هر قسمت و هر فایل، چه ارتباطی با سایر اجزای تعریف شده دارد و چگونه به هم متصل شدهاند و اینبار شاهد انبوهی از کدهای جاوا اسکریپتی مخلوط بین المانهای HTML صفحه نیستیم.
نمایش پیامی به کاربر پس از ارسال پیام خصوصی در صفحهی تماس با ما
قصد داریم ویژگی مشابهی را به صفحهی contact نیز اضافه کنیم. اگر کاربر بر روی دکمهی ارسال پیام کلیک کرد، پیام تشکری به همراه عددی ویژه به او نمایش خواهیم داد.
برای اینکار قالب Scripts\Templates\contact.hbs را به نحو ذیل تکمیل کنید:
در آن شرط بررسی if messageSent اضافه شدهاست؛ به همراه نمایش confirmationNumber در انتهای پیام تشکر.
برای تعریف منطق مرتبط با این خواص، به کنترلر contact واقع در فایل Scripts\Controllers\contact.js مراجعه کرده و آنرا به نحو ذیل تغییر میدهیم:
همانطور که مشاهده میکنید، مقدار اولیه خاصیت messageSent مساوی false است. بنابراین در قالب contact.hbs قسمت else شرط نمایش داده میشود. اگر کاربر پیامی را وارد کند، خاصیت confirmationNumber به یک عدد اتفاقی و خاصیت messageSent به true تنظیم خواهد شد. به این ترتیب اینبار به صورت خودکار پیام تشکر به همراه عددی اتفاقی، به کاربر نمایش داده میشود.
بنابراین به صورت خلاصه، کار کنترلر، مدیریت منطق نمایشی برنامه است و برای اینکار حداقل دو مکانیزم را ارائه میدهد: اکشنها و خواص. اکشنها بیانگر نوعی رفتار هستند؛ برای مثال نمایش یک popup و یا تغییر مقدار یک خاصیت. مقدار خواص را میتوان مستقیما در صفحه نمایش داد و یا از آنها جهت پردازش عبارات شرطی و نمایش قسمت خاصی از قالب جاری نیز میتوان کمک گرفت.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
EmberJS03_02.zip
اضافه کردن دکمهی More info به صفحهی About و مدیریت کلیک بر روی آن
فایل Scripts\Templates\about.hbs را گشوده و سپس محتوای فعلی آن را به نحو ذیل تکمیل کنید:
<h2>About Ember Blog</h2> <p>Bla bla bla!</p> <button class="btn btn-primary" {{action 'showRealName' }}>more info</button>
به همین جهت فایل جدید Scripts\Controllers\about.js را در پوشهی کنترلرهای سمت کاربر اضافه کنید (نام آن با نام مسیریابی یکی است)؛ با این محتوا:
Blogger.AboutController = Ember.Controller.extend({ actions: { showRealName: function () { alert("You clicked at showRealName of AboutController."); } } });
در ادامه این خاصیت را با تهیه یک زیرکلاس از کلاس پایه Controller تهیه شده توسط ember.js مقدار دهی میکنیم. به این ترتیب به کلیه امکانات این کلاس پایه دسترسی خواهیم داشت؛ به علاوه میتوان ویژگیهای سفارشی را نیز به آن افزود. برای مثال در اینجا در قسمت actions آن، دقیقا مطابق نام اکشنی که در فایل about.hbs تعریف کردهایم، یک متد جدید اضافه شدهاست.
پس از تعریف کنترلر about.js نیاز است مدخل متناظر با آنرا به فایل index.html برنامه نیز در انتهای تعاریف موجود، اضافه کرد:
<script src="Scripts/Controllers/about.js" type="text/javascript"></script>
اکنون یکبار برنامه را اجرا کرده و در صفحهی about بر روی دکمهی more info کلیک کنید.
اضافه کردن دکمهی ارسال پیام خصوصی به صفحهی Contact و مدیریت کلیک بر روی آن
در ادامه به قالب فعلی Scripts\Templates\contact.hbs یک دکمه را جهت ارسال پیام خصوصی اضافه میکنیم.
<h1>Contact</h1> <div class="row"> <div class="col-md-6"> <p> Want to get in touch? <ul> <li>{{#link-to 'phone'}}Phone{{/link-to}}</li> <li>{{#link-to 'email'}}Email{{/link-to}}</li> </ul> </p> <p> Or, click here to send a secret message: </p> <button class="btn btn-primary" {{action 'sendMessage' }}>Send message</button> </div> <div class="col-md-6"> {{outlet}} </div> </div>
Blogger.ContactController = Ember.Controller.extend({ actions: { sendMessage: function () { var message = prompt('Type your message here:'); } } });
<script src="Scripts/Controllers/contact.js" type="text/javascript"></script>
نمایش تصویری تعاملی در صفحهی about
تا اینجا با نحوهی تعریف اکشنها در قالبها و مدیریت آنها توسط کنترلرهای متناظر آشنا شدیم. در ادامه قصد داریم با اصول binding اطلاعات در ember.js آشنا شویم. برای مثال فرض کنید میخواهیم دکمهای را در صفحهی about قرار داده و با کلیک بر روی آن، لوگوی ember.js را که به صورت یک تصویر مخفی در صفحه قرار دارد، نمایان کنیم. برای اینکار نیاز است خاصیتی را در کنترلر متناظر، تعریف کرده و سپس آنرا به template جاری bind کرد.
برای این منظور فایل Scripts\Templates\about.hbs را گشوده و تعاریف موجود آنرا به نحو ذیل تکمیل کنید:
<h2>About Ember Blog</h2> <p>Bla bla bla!</p> <button class="btn btn-primary" {{action 'showRealName' }}>more info</button> {{#if isAuthorShowing}} <button class="btn btn-warning" {{action 'hideAuthor' }}>Hide Image</button> <p><img src="Content/images/ember-productivity-sm.png"></p> {{else}} <button class="btn btn-info" {{action 'showAuthor' }}>Show Image</button> {{/if}}
کنترلر about (فایل Scripts\Controllers\about.js) جهت مدیریت این خاصیت جدید، به همراه دو اکشن تعریف شده، اینبار به نحو ذیل تغییر خواهد یافت:
Blogger.AboutController = Ember.Controller.extend({ isAuthorShowing: false, actions: { showRealName: function () { alert("You clicked at showRealName of AboutController."); }, showAuthor: function () { this.set('isAuthorShowing', true); }, hideAuthor: function () { this.set('isAuthorShowing', false); } } });
سپس در دو متد showAuthor و hideAuthor که به اکشنهای دو دکمهی جدید تعریف شده در قالب about متصل خواهند شد، نحوهی تغییر مقدار خاصیت isAuthorShowing را توسط متد set ملاحظه میکنید.
این قسمت مهمترین تفاوت ember.js با jQuery است. در jQuery مستقیما المانهای صفحه در همانجا تغییر داده میشوند. در ember.js منطق مدیریت کنندهی رابط کاربری و کدهای قالب متناظر با آن از هم جدا شدهاند تا بتوان یک برنامهی بزرگ را بهتر مدیریت کرد. همچنین در اینجا مشخص است که هر قسمت و هر فایل، چه ارتباطی با سایر اجزای تعریف شده دارد و چگونه به هم متصل شدهاند و اینبار شاهد انبوهی از کدهای جاوا اسکریپتی مخلوط بین المانهای HTML صفحه نیستیم.
نمایش پیامی به کاربر پس از ارسال پیام خصوصی در صفحهی تماس با ما
قصد داریم ویژگی مشابهی را به صفحهی contact نیز اضافه کنیم. اگر کاربر بر روی دکمهی ارسال پیام کلیک کرد، پیام تشکری به همراه عددی ویژه به او نمایش خواهیم داد.
برای اینکار قالب Scripts\Templates\contact.hbs را به نحو ذیل تکمیل کنید:
<h1>Contact</h1> <div class="row"> <div class="col-md-6"> <p> Want to get in touch? <ul> <li>{{#link-to 'phone'}}Phone{{/link-to}}</li> <li>{{#link-to 'email'}}Email{{/link-to}}</li> </ul> </p> {{#if messageSent}} <p> Thank you. Your message has been sent. Your confirmation number is {{confirmationNumber}}. </p> {{else}} <p> Or, click here to send a secret message: </p> <button class="btn btn-primary" {{action 'sendMessage' }}>Send message</button> {{/if}} </div> <div class="col-md-6"> {{outlet}} </div> </div>
برای تعریف منطق مرتبط با این خواص، به کنترلر contact واقع در فایل Scripts\Controllers\contact.js مراجعه کرده و آنرا به نحو ذیل تغییر میدهیم:
Blogger.ContactController = Ember.Controller.extend({ messageSent: false, actions: { sendMessage: function () { var message = prompt('Type your message here:'); if (message) { this.set('confirmationNumber', Math.round(Math.random() * 100000)); this.set('messageSent', true); } } } });
بنابراین به صورت خلاصه، کار کنترلر، مدیریت منطق نمایشی برنامه است و برای اینکار حداقل دو مکانیزم را ارائه میدهد: اکشنها و خواص. اکشنها بیانگر نوعی رفتار هستند؛ برای مثال نمایش یک popup و یا تغییر مقدار یک خاصیت. مقدار خواص را میتوان مستقیما در صفحه نمایش داد و یا از آنها جهت پردازش عبارات شرطی و نمایش قسمت خاصی از قالب جاری نیز میتوان کمک گرفت.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید:
EmberJS03_02.zip
نظرات مطالب
ASP.NET MVC #21
نیاز هست بتونید برنامههای ایجکسی را دیباگ کنید:
نحوه استفاده از افزونه Firebug برای دیباگ برنامههای ASP.NET مبتنی بر jQuery
نحوه استفاده از افزونه Firebug برای دیباگ برنامههای ASP.NET مبتنی بر jQuery
نظرات مطالب
خلاصهای کوتاه در مورد WinRT
معنای WinRT برای برنامه نویسهای دات نت چیست؟
WinRT ، دات نت نیست. برای درک این موضوع باید CLR و دات نت فریم ورک را از هم جدا کرد. برنامههای دات نت نوشته شده برای WinRT برفراز CLR اجرا میشوند اما از دات نت فریم ورک استفاده نمیکنند. بجای آن از توانمندیهای مشابه موجود در WinRT ، در پشت صحنه استفاده خواهند کرد.
در اینجا برنامههای C++ + XAML بدون دخالت CLR و مستقیما برفراز WinRT کار خواهند کرد. برنامههای سبک مترو HTML/CSS/JavaScript هم به همین صورت متکی به CLR نیستند و مستقیما برفراز WinRT اجرا میشوند.
WinRT فقط قادر است برنامههای سبک مترو ویندوز 8 را هاست کند. اگر نیاز دارید سیستم عاملهای قدیمی را پشتیبانی کنید یا اینکه اصلا کارتان ساخت برنامههای سمت کاربر و دسکتاپ نیست، اصلا این تغییرات به کار شما مرتبط نخواهند شد.
الگوی اصلی تعاملی برنامههای سبک مترو با تمرکز بر برنامههای لمسی (touch focused) و مبتنی بر محتوا (content-oriented) است. به این معنا که تمام برنامههای تجاری موجود که دارای 10 ها و صدها صفحهی ورود اطلاعات هستند، اصلا برای این نوع سبک ارائه محتوا طراحی نشدهاند و نخواهند شد. کاربردهای مهم این سبک، استفاده از آن در برنامههای مخصوص تمام صفحه tablets است یا حداکثر در حد داشبردهای ارائه خلاصه گزارشات یک برنامه میتوانند اهمیت داشته باشند. بنابراین اگر کارتان در حیطهی ساخت برنامههای مخصوص tablets و برنامههای لمسی قرار نمیگیرد، کماکان به ساخت برنامههای دسکتاپ نوشته شده با WPF/WinForms میتوانید مشغول باشید.
انتقال قسمت عمدهای از برنامههای موجودWPF و یا Silverlight مبتنی بر الگوهایی مانند MVVM و امثال آن با توجه به عدم گره خوردگی آنها به لایه نمایشی ، به WinRT میسر است.
در سمت سرور، تمرکز اصلی هنوز همان دات نت فریم ورک است. قرار نیست ASP.NET یا WCF و سایر مؤلفههای اصلی دات نت به WinRT منتقل شوند. حتی اگر WinRT به سرورهای بعدی هم راه پیدا کند در حد همان لایه نمایشی مترو است و تاثیری بر روی سرویسهای NT با دسترسی بالای ویندوز ، نخواهد داشت. مثلا قرار نیست SQL Server را با WinRT پیاده سازی کنند.
WinRT ، دات نت نیست. برای درک این موضوع باید CLR و دات نت فریم ورک را از هم جدا کرد. برنامههای دات نت نوشته شده برای WinRT برفراز CLR اجرا میشوند اما از دات نت فریم ورک استفاده نمیکنند. بجای آن از توانمندیهای مشابه موجود در WinRT ، در پشت صحنه استفاده خواهند کرد.
در اینجا برنامههای C++ + XAML بدون دخالت CLR و مستقیما برفراز WinRT کار خواهند کرد. برنامههای سبک مترو HTML/CSS/JavaScript هم به همین صورت متکی به CLR نیستند و مستقیما برفراز WinRT اجرا میشوند.
WinRT فقط قادر است برنامههای سبک مترو ویندوز 8 را هاست کند. اگر نیاز دارید سیستم عاملهای قدیمی را پشتیبانی کنید یا اینکه اصلا کارتان ساخت برنامههای سمت کاربر و دسکتاپ نیست، اصلا این تغییرات به کار شما مرتبط نخواهند شد.
الگوی اصلی تعاملی برنامههای سبک مترو با تمرکز بر برنامههای لمسی (touch focused) و مبتنی بر محتوا (content-oriented) است. به این معنا که تمام برنامههای تجاری موجود که دارای 10 ها و صدها صفحهی ورود اطلاعات هستند، اصلا برای این نوع سبک ارائه محتوا طراحی نشدهاند و نخواهند شد. کاربردهای مهم این سبک، استفاده از آن در برنامههای مخصوص تمام صفحه tablets است یا حداکثر در حد داشبردهای ارائه خلاصه گزارشات یک برنامه میتوانند اهمیت داشته باشند. بنابراین اگر کارتان در حیطهی ساخت برنامههای مخصوص tablets و برنامههای لمسی قرار نمیگیرد، کماکان به ساخت برنامههای دسکتاپ نوشته شده با WPF/WinForms میتوانید مشغول باشید.
انتقال قسمت عمدهای از برنامههای موجودWPF و یا Silverlight مبتنی بر الگوهایی مانند MVVM و امثال آن با توجه به عدم گره خوردگی آنها به لایه نمایشی ، به WinRT میسر است.
در سمت سرور، تمرکز اصلی هنوز همان دات نت فریم ورک است. قرار نیست ASP.NET یا WCF و سایر مؤلفههای اصلی دات نت به WinRT منتقل شوند. حتی اگر WinRT به سرورهای بعدی هم راه پیدا کند در حد همان لایه نمایشی مترو است و تاثیری بر روی سرویسهای NT با دسترسی بالای ویندوز ، نخواهد داشت. مثلا قرار نیست SQL Server را با WinRT پیاده سازی کنند.
اشتراکها
انتشار TypeScript 4.8
Here’s a quick list of what’s new in TypeScript 4.8!
Improved Intersection Reduction, Union Compatibility, and Narrowing
Improved Inference for infer Types in Template String Types
--build, --watch, and --incremental Performance Improvements
Errors When Comparing Object and Array Literals
Improved Inference from Binding Patterns
File-Watching Fixes (Especially Across git checkouts)
Find-All-References Performance Improvements
Exclude Specific Files from Auto-Imports
Correctness Fixes and Breaking Changes
با گسترش استفاده از کامپیوتر در بسیاری از امور روزمره انسانها سازگار بودن برنامهها با سلیقه کاربران به یکی از نیازهای اصلی برنامههای کامپیوتری تبدیل شده است. بدون شک زبان و فرهنگ یکی از مهمترین عوامل در ایجاد ارتباط نزدیک بین برنامه و کاربر به شمار میرود و نقشی غیر قابل انکار در میزان موفقیت یک برنامه به عهده دارد. از این رو در این نوشته تلاش بر آن است تا یکی از سادهترین و در عین حال کاراترین راههای ممکن برای ایجاد برنامههای چند زبانه با استفاده از تکنولوژی WPF آموزش داده شود.
مروری بر روشهای موجود
همواره روشهای مختلفی برای پیاده سازی یک ایده در دنیای نرم افزار وجود دارد که هر روش را میتوان بر حسب نیاز مورد استفاده قرار داد. در برنامههای مبتنی بر WPF معمولا از دو روش عمده برای این منظور استفاده میشود:
1-استفاده از فایلهای resx
در این روش که برای Win App نیز استفاده میشود، اطلاعات مورد نیاز برای هر زبان به شکل جدول هایی دارای کلید و مقدار در داخل یک فایل .resx نگهداری میشود و در زمان اجرای برنامه بر اساس انتخاب کاربر اطلاعات زبان مورد نظر از داخل فایل resx خوانده شده و نمایش داده میشود. یکی از ضعف هایی که این روش در عین ساده بودن دارد این است که همه اطلاعات مورد نیاز داخل assembly اصلی برنامه قرار میگیرد و امکان افزودن زبانهای جدید بدون تغییر دادن برنامه اصلی ممکن نخواهد بود.
2-استفاده از فایلهای csv که به فایلهای dll تبدیل میشوند
در این روش با استفاده از ابزارهای موجود در کامپایلر WPF برای هر کنترل یک property به نام Uid ایجاد شده و مقدار دهی میشود. سپس با ابزار دیگری ( که جزو ابزارهای کامپایلر محسوب نمیشود ) از فایل csproj پروژه یک خروجی اکسل با فرمت csv ایجاد میشود که شامل Uidهای کنترلها و مقادیر آنها است. پس از ترجمه متون مورد نظر به زبان مقصد با کمک ابزار دیگری فایل اکسل مورد نظر به یک net assembly تبدیل میشود و داخل پوشه ای با نام culture استاندارد ذخیره میشود. ( مثلا برای زبان فارسی نام پوشه fa-IR خواهد بود ). زمانی که برنامه اجرا میشود بر اساس culture ای که در سیستم عامل انتخاب شده است و در صورتی که برای آن culture فایل dll ای موجود باشد، زبان مربوط به آن culture را load خواهد کرد. با وجود این که این روش مشکل روش قبلی را ندارد و بیشتر با ویژگیهای WPF سازگار است اما پروسه ای طولانی برای انجام کارها دارد و به ازای هر تغییری باید کل مراحل هر بار تکرار شوند. همچنین مشکلاتی در نمایش برخی زبانها ( از جمله فارسی ) در این روش مشاهده شده است.
روش سوم!
روش سوم اما کاملا بر پایه WPF و در اصطلاح WPF-Native میباشد. ایده از آنجا ناشی شده است که برای ایجاد skin در برنامههای WPF استفاده میشود. در ایجاد برنامههای Skin-Based به این شیوه عمل میشود که skinهای مورد نظر به صورت style هایی در داخل ResourceDictionary ها قرار میگیرند. سپس آن ResourceDictionary به شکل dll کامپایل میشود. در برنامه اصلی نیز همه کنترلها style هایشان را به شکل dynamic resource از داخل یک ResourceDictionary مشخص شده load میکنند. حال کافی است برای تغییر skin فعلی، ResourceDictionary مورد نظر از dll مشخص load شود و ResourceDictionary ای که در حال حاضر در برنامه از آن استفاده میشود با ResourceDictionary ای که load شده جایگزین شود. کنترلها مقادیر جدید را از ResourceDictionary جدید به شکل کاملا خودکار دریافت خواهند کرد.
به سادگی میتوان از این روش برای تغییر زبان برنامه نیز استفاده کرد با این تفاوت که این بار، به جای Style ها، Stringهای زبانهای مختلف را درون resourceها نگهداری خواهیم کرد.
یک مثال ساده
در این قسمت نحوه پیاده سازی این روش با ایجاد یک نمونه برنامه ساده که دارای دو زبان انگلیسی و فارسی خواهد بود آموزش داده میشود.
ابتدا یک پروژه WPF Application در Visual Studio 2010 ایجاد کنید. در MainWindow سه کنترل Button قرار دهید و یک ComboBox که قرار است زبانهای موجود را نمایش دهد و با انتخاب یک زبان، نوشتههای درون Buttonها متناسب با آن تغییر خواهند کرد.
توجه داشته باشید که برای Buttonها نباید به صورت مستقیم مقداری به Content شان داده شود. زیرا مقدار مورد نظر از داخل ResourceDictionary که خواهیم ساخت به شکل dynamic گرفته خواهد شد. پس در این مرحله یک ResourceDictionary به پروژه اضافه کرده و در آن resource هایی به شکل string ایجاد میکنیم. هر resource دارای یک Key میباشد که بر اساس آن، Button مورد نظر، مقدار آن Resource را load خواهد کرد. فایل ResourceDictionary را
Culture_en-US.xaml نامگذاری کنید و مقادیر مورد نظر را به آن اضافه نمایید.
دقت کنید که namespace ای که کلاس string در آن قرار دارد به فایل xaml اضافه شده است و پیشوند system به آن نسبت داده شده است.
با افزودن یک ResourceDictionary به پروژه، آن ResourceDictionary به MergedDictionary کلاس App اضافه میشود. بنابراین فایل App.xaml به شکل زیر خواهد بود:
برای اینکه بتوانیم محتوای Buttonهای موجود را به صورت داینامیک و در زمان اجرای برنامه، از داخل Resourceها بگیریم، از DynamicResource استفاده میکنیم.
بسیار خوب! اکنون باید شروع به ایجاد یک ResourceDictionary برای زبان فارسی کنیم و آن را به صورت یک فایل dll کامپایل نماییم.
برای این کار یک پروژه جدید در قسمت WPF از نوع User control ایجاد میکنیم و نام آن را Culture_fa-IR_Farsi قرار میدهیم. لطفا شیوه نامگذاری را رعایت کنید چرا که در ادامه به آن نیاز خواهیم داشت.
پس از ایجاد پروژه فایل UserControl1.xaml را از پروژه حذف کنید و یک ResourceDictionary با نام Culture_fa-IR.xaml اضافه کنید. محتوای آن را پاک کنید و محتوای فایل Culture_en-US.xaml را از پروژه قبلی به صورت کامل در فایل جدید کپی کنید. دو فایل باید ساختار کاملا یکسانی از نظر key برای Resourceهای موجود داشته باشند. حالا زمان ترجمه فرا رسیده است! رشتههای دلخواه را ترجمه کنید و پروژه را build نمایید.
پس از ترجمه فایل Culture_fa-IR.xaml به شکل زیر خواهد بود:
خروجی این پروژه یک فایل با نام Culture_fa-IR_Farsi.dll خواهد بود که حاوی یک ResourceDictionary برای زبان فارسی میباشد.
در ادامه میخواهیم راهکاری ارئه دهیم تا بتوان فایلهای dll مربوط به زبانها را در زمان اجرای برنامه اصلی، load کرده و نام زبانها را در داخل ComboBox ای که داریم نشان دهیم. سپس با انتخاب هر زبان در ComboBox، محتوای Buttonها بر اساس زبان انتخاب شده تغییر کند.
برای سهولت کار، نام فایلها را به گونه ای انتخاب کردیم که بتوانیم سادهتر به این هدف برسیم. نام هر فایل از سه بخش تشکیل شده است:
پوشه ای با نام Languages در کنار فایل اجرایی برنامه اصلی ایجاد کنید و فایل Culture_fa-IR_Farsi.dll را درون آن کپی کنید. تصمیم داریم همه dllهای مربوط به زبانها را داخل این پوشه قرار دهیم تا مدیریت آنها سادهتر شود.
برای مدیریت بهتر فایلهای مربوط به زبانها یک کلاس با نام CultureAssemblyModel خواهیم ساخت که هر instance از آن نشانگر یک فایل زبان خواهد بود. یک کلاس با این نام به پروژه اضافه کنید و propertyهای زیر را در آن تعریف نمایید:
اکنون باید لیست cultureهای موجود را از داخل پوشه languages خوانده و نام آنها را در ComboBox نمایش دهیم.
برای خواندن لیست cultureهای موجود، لیستی از CultureAssmeblyModelها ایجاد کرده و با استفاده از متد LoadCultureAssmeblies، آن را پر میکنیم.
پس از دریافت اطلاعات cultureهای موجود، زمان نمایش آنها در ComboBox است. این کار بسیار ساده است، تنها کافی است ItemsSource آن را با لیستی از CultureAssmeblyModelها که ساختیم، مقدار دهی کنیم.
البته لازم به ذکر است که برای نمایش فقط نام هر CultureAssemblyModel در ComboBox، باید ItemTemplate مناسبی برای ComboBox ایجاد کنیم. در مثال ما ItemTemplate به شکل زیر خواهد بود:
توجه داشته باشید که با وجود اینکه فقط نام را در ComboBox نشان میدهیم، اما باز هم هر آیتم از ComboBox یک instance از نوع CultureAssemblyModel میباشد.
در مرحله بعد، قرار است متدی بنویسیم که اطلاعات زبان انتخاب شده را گرفته و با جابجایی ResourceDictionary ها، زبان برنامه را تغییر دهیم.
متدی با نام LoadCulture در کلاس App ایجاد میکنیم که یک CultureAssemblyModel به عنوان ورودی دریافت کرده و ResourceDictionary داخل آن را load میکند و آن را با ResourceDictionary فعلی موجود در App.xaml جابجا مینماید.
با این کار، Button هایی که قبلا مقدار Content خود را از Resourceهای موجود دریافت میکردند، اکنون از Resourceهای جابجا شده خواهند گرفت و به این ترتیب زبان انتخاب شده بر روی برنامه اعمال میشود.
برای ارسال زبان انتخاب شده به این متد، باید رویداد SelectionChanged را برای ComboBox مدیریت کنیم:
کار انجام شد!
از مزیتهای این روش میتوان به WPF-Native بودن، سادگی در پیاده سازی، قابلیت load کردن هر زبان جدیدی در زمان اجرا بدون نیاز به کوچکترین تغییر در برنامه اصلی و همچنین پشتیبانی کامل از نمایش زبانهای مختلف از جمله فارسی اشاره کرد.
مروری بر روشهای موجود
همواره روشهای مختلفی برای پیاده سازی یک ایده در دنیای نرم افزار وجود دارد که هر روش را میتوان بر حسب نیاز مورد استفاده قرار داد. در برنامههای مبتنی بر WPF معمولا از دو روش عمده برای این منظور استفاده میشود:
1-استفاده از فایلهای resx
در این روش که برای Win App نیز استفاده میشود، اطلاعات مورد نیاز برای هر زبان به شکل جدول هایی دارای کلید و مقدار در داخل یک فایل .resx نگهداری میشود و در زمان اجرای برنامه بر اساس انتخاب کاربر اطلاعات زبان مورد نظر از داخل فایل resx خوانده شده و نمایش داده میشود. یکی از ضعف هایی که این روش در عین ساده بودن دارد این است که همه اطلاعات مورد نیاز داخل assembly اصلی برنامه قرار میگیرد و امکان افزودن زبانهای جدید بدون تغییر دادن برنامه اصلی ممکن نخواهد بود.
2-استفاده از فایلهای csv که به فایلهای dll تبدیل میشوند
در این روش با استفاده از ابزارهای موجود در کامپایلر WPF برای هر کنترل یک property به نام Uid ایجاد شده و مقدار دهی میشود. سپس با ابزار دیگری ( که جزو ابزارهای کامپایلر محسوب نمیشود ) از فایل csproj پروژه یک خروجی اکسل با فرمت csv ایجاد میشود که شامل Uidهای کنترلها و مقادیر آنها است. پس از ترجمه متون مورد نظر به زبان مقصد با کمک ابزار دیگری فایل اکسل مورد نظر به یک net assembly تبدیل میشود و داخل پوشه ای با نام culture استاندارد ذخیره میشود. ( مثلا برای زبان فارسی نام پوشه fa-IR خواهد بود ). زمانی که برنامه اجرا میشود بر اساس culture ای که در سیستم عامل انتخاب شده است و در صورتی که برای آن culture فایل dll ای موجود باشد، زبان مربوط به آن culture را load خواهد کرد. با وجود این که این روش مشکل روش قبلی را ندارد و بیشتر با ویژگیهای WPF سازگار است اما پروسه ای طولانی برای انجام کارها دارد و به ازای هر تغییری باید کل مراحل هر بار تکرار شوند. همچنین مشکلاتی در نمایش برخی زبانها ( از جمله فارسی ) در این روش مشاهده شده است.
روش سوم!
روش سوم اما کاملا بر پایه WPF و در اصطلاح WPF-Native میباشد. ایده از آنجا ناشی شده است که برای ایجاد skin در برنامههای WPF استفاده میشود. در ایجاد برنامههای Skin-Based به این شیوه عمل میشود که skinهای مورد نظر به صورت style هایی در داخل ResourceDictionary ها قرار میگیرند. سپس آن ResourceDictionary به شکل dll کامپایل میشود. در برنامه اصلی نیز همه کنترلها style هایشان را به شکل dynamic resource از داخل یک ResourceDictionary مشخص شده load میکنند. حال کافی است برای تغییر skin فعلی، ResourceDictionary مورد نظر از dll مشخص load شود و ResourceDictionary ای که در حال حاضر در برنامه از آن استفاده میشود با ResourceDictionary ای که load شده جایگزین شود. کنترلها مقادیر جدید را از ResourceDictionary جدید به شکل کاملا خودکار دریافت خواهند کرد.
به سادگی میتوان از این روش برای تغییر زبان برنامه نیز استفاده کرد با این تفاوت که این بار، به جای Style ها، Stringهای زبانهای مختلف را درون resourceها نگهداری خواهیم کرد.
یک مثال ساده
در این قسمت نحوه پیاده سازی این روش با ایجاد یک نمونه برنامه ساده که دارای دو زبان انگلیسی و فارسی خواهد بود آموزش داده میشود.
ابتدا یک پروژه WPF Application در Visual Studio 2010 ایجاد کنید. در MainWindow سه کنترل Button قرار دهید و یک ComboBox که قرار است زبانهای موجود را نمایش دهد و با انتخاب یک زبان، نوشتههای درون Buttonها متناسب با آن تغییر خواهند کرد.
توجه داشته باشید که برای Buttonها نباید به صورت مستقیم مقداری به Content شان داده شود. زیرا مقدار مورد نظر از داخل ResourceDictionary که خواهیم ساخت به شکل dynamic گرفته خواهد شد. پس در این مرحله یک ResourceDictionary به پروژه اضافه کرده و در آن resource هایی به شکل string ایجاد میکنیم. هر resource دارای یک Key میباشد که بر اساس آن، Button مورد نظر، مقدار آن Resource را load خواهد کرد. فایل ResourceDictionary را
Culture_en-US.xaml نامگذاری کنید و مقادیر مورد نظر را به آن اضافه نمایید.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> <system:String x:Key="button1">Hello!</system:String> <system:String x:Key="button2">How Are You?</system:String> <system:String x:Key="button3">Are You OK?</system:String> </ResourceDictionary>
دقت کنید که namespace ای که کلاس string در آن قرار دارد به فایل xaml اضافه شده است و پیشوند system به آن نسبت داده شده است.
با افزودن یک ResourceDictionary به پروژه، آن ResourceDictionary به MergedDictionary کلاس App اضافه میشود. بنابراین فایل App.xaml به شکل زیر خواهد بود:
<Application x:Class="BeRMOoDA.WPF.LocalizationSample.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Culture_en-US.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application>
برای اینکه بتوانیم محتوای Buttonهای موجود را به صورت داینامیک و در زمان اجرای برنامه، از داخل Resourceها بگیریم، از DynamicResource استفاده میکنیم.
<Button Content="{DynamicResource ResourceKey=button1}" /> <Button Content="{DynamicResource ResourceKey=button2}" /> <Button Content="{DynamicResource ResourceKey=button3}" />
بسیار خوب! اکنون باید شروع به ایجاد یک ResourceDictionary برای زبان فارسی کنیم و آن را به صورت یک فایل dll کامپایل نماییم.
برای این کار یک پروژه جدید در قسمت WPF از نوع User control ایجاد میکنیم و نام آن را Culture_fa-IR_Farsi قرار میدهیم. لطفا شیوه نامگذاری را رعایت کنید چرا که در ادامه به آن نیاز خواهیم داشت.
پس از ایجاد پروژه فایل UserControl1.xaml را از پروژه حذف کنید و یک ResourceDictionary با نام Culture_fa-IR.xaml اضافه کنید. محتوای آن را پاک کنید و محتوای فایل Culture_en-US.xaml را از پروژه قبلی به صورت کامل در فایل جدید کپی کنید. دو فایل باید ساختار کاملا یکسانی از نظر key برای Resourceهای موجود داشته باشند. حالا زمان ترجمه فرا رسیده است! رشتههای دلخواه را ترجمه کنید و پروژه را build نمایید.
پس از ترجمه فایل Culture_fa-IR.xaml به شکل زیر خواهد بود:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Culture_fa-IR_Farsi.xaml"/> </ResourceDictionary.MergedDictionaries> <system:String x:Key="button1">سلام!</system:String> <system:String x:Key="button2">حالت چطوره؟</system:String> <system:String x:Key="button3">خوبی؟</system:String> </ResourceDictionary>
در ادامه میخواهیم راهکاری ارئه دهیم تا بتوان فایلهای dll مربوط به زبانها را در زمان اجرای برنامه اصلی، load کرده و نام زبانها را در داخل ComboBox ای که داریم نشان دهیم. سپس با انتخاب هر زبان در ComboBox، محتوای Buttonها بر اساس زبان انتخاب شده تغییر کند.
برای سهولت کار، نام فایلها را به گونه ای انتخاب کردیم که بتوانیم سادهتر به این هدف برسیم. نام هر فایل از سه بخش تشکیل شده است:
Culture_[standard culture notation]_[display name for this culture].dll
یعنی
اگر فایل Culture_fa-IR_Farsi.dll را در نظر بگیریم، Culture نشان دهنده
این است که این فایل مربوط به یک culture میباشد. fa-IR نمایش استاندارد
culture برای کشور ایران و زبان فارسی است و Farsi هم مقداری است که میخواهیم در ComboBox برای این زبان نمایش داده شود.پوشه ای با نام Languages در کنار فایل اجرایی برنامه اصلی ایجاد کنید و فایل Culture_fa-IR_Farsi.dll را درون آن کپی کنید. تصمیم داریم همه dllهای مربوط به زبانها را داخل این پوشه قرار دهیم تا مدیریت آنها سادهتر شود.
برای مدیریت بهتر فایلهای مربوط به زبانها یک کلاس با نام CultureAssemblyModel خواهیم ساخت که هر instance از آن نشانگر یک فایل زبان خواهد بود. یک کلاس با این نام به پروژه اضافه کنید و propertyهای زیر را در آن تعریف نمایید:
public class CultureAssemblyModel { //the text will be displayed to user as language name (like Farsi) public string DisplayText { get; set; } //name of .dll file (like Culture_fa-IR_Farsi.dll) public string Name { get; set; } //standar notation of this culture (like fa-IR) public string Culture { get; set; } //name of resource dictionary file inside the loaded .dll (like Culture_fa-IR.xaml) public string XamlFileName { get; set; } }
برای خواندن لیست cultureهای موجود، لیستی از CultureAssmeblyModelها ایجاد کرده و با استفاده از متد LoadCultureAssmeblies، آن را پر میکنیم.
//will keep information about loaded assemblies public List<CultureAssemblyModel> CultureAssemblies { get; set; } //loads assmeblies in languages folder and adds their info to list void LoadCultureAssemblies() { //we should be sure that list is empty before adding info (do u want to add some cultures more than one? of course u dont!) CultureAssemblies.Clear(); //creating a directory represents applications directory\languages DirectoryInfo dir = new DirectoryInfo(System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\languages"); //getting all .dll files in the language folder and its sub dirs. (who knows? maybe someone keeps each culture file in a seperate folder!) var assemblies = dir.GetFiles("*.dll", SearchOption.AllDirectories); //for each found .dll we will create a model and set its properties and then add to list for (int i = 0; i < assemblies.Count(); i++) {
string name = assemblies[i].Name;
CultureAssemblyModel model = new CultureAssemblyModel() { DisplayText = name.Split('.', '_')[2], Culture = name.Split('.', '_')[1], Name = name , XamlFileName =name.Substring(0, name.LastIndexOf(".")) + ".xaml" }; CultureAssemblies.Add(model); } }
comboboxLanguages.ItemsSource = CultureAssemblies;
<ComboBox HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" MinWidth="100" Name="comboboxLanguages"> <ComboBox.ItemTemplate> <DataTemplate> <Label Content="{Binding DisplayText}"/> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox>
در مرحله بعد، قرار است متدی بنویسیم که اطلاعات زبان انتخاب شده را گرفته و با جابجایی ResourceDictionary ها، زبان برنامه را تغییر دهیم.
متدی با نام LoadCulture در کلاس App ایجاد میکنیم که یک CultureAssemblyModel به عنوان ورودی دریافت کرده و ResourceDictionary داخل آن را load میکند و آن را با ResourceDictionary فعلی موجود در App.xaml جابجا مینماید.
با این کار، Button هایی که قبلا مقدار Content خود را از Resourceهای موجود دریافت میکردند، اکنون از Resourceهای جابجا شده خواهند گرفت و به این ترتیب زبان انتخاب شده بر روی برنامه اعمال میشود.
//loads selected culture public void LoadCulture(CultureAssemblyModel culture) { //creating a FileInfo object represents .dll file of selected cultur FileInfo assemblyFile = new FileInfo("languages\\" + culture.Name); //loading .dll into memory as a .net assembly var assembly = Assembly.LoadFile(assemblyFile.FullName); //getting .dll file name var assemblyName = assemblyFile.Name.Substring(0, assemblyFile.Name.LastIndexOf(".")); //creating string represents structure of a pack uri (something like this: /{myassemblyname;component/myresourcefile.xaml} string packUri = string.Format(@"/{0};component/{1}", assemblyName, culture.XamlFileName); //creating a pack uri Uri uri = new Uri(packUri, UriKind.Relative); //now we have created a pack uri that represents a resource object in loaded assembly //and its time to load that as a resource dictionary (do u remember that we had resource dictionary in culture assemblies? don't u?) var dic = Application.LoadComponent(uri) as ResourceDictionary; dic.Source = uri; //here we will remove current merged dictionaries in our resource dictionary and add recently-loaded resource dictionary as e merged dictionary var mergedDics = this.Resources.MergedDictionaries; if (mergedDics.Count > 0) mergedDics.Clear(); mergedDics.Add(dic); }
void comboboxLanguages_SelectionChanged(object sender, SelectionChangedEventArgs e) { var selectedCulture = (CultureAssemblyModel)comboboxLanguages.SelectedItem; App app = Application.Current as App; app.LoadCulture(selectedCulture); }
کار انجام شد!
از مزیتهای این روش میتوان به WPF-Native بودن، سادگی در پیاده سازی، قابلیت load کردن هر زبان جدیدی در زمان اجرا بدون نیاز به کوچکترین تغییر در برنامه اصلی و همچنین پشتیبانی کامل از نمایش زبانهای مختلف از جمله فارسی اشاره کرد.
اشتراکها
بررسی C# Object Notation
«... مدتی است یک سرویس مبتنی بر هوش مصنوعی تازه به نام NotebookLM توسط گوگل در اختیار کاربران قرار گرفته که کاربردهای متعددی دارد. یکی از مهمترین کاربردهای آن، برای افرادی است که با منابع علمی و پژوهشی متعدد سر و کار دارند. شما میتوانید کتابها و فایلهای پی دی اف خودتان را در NotebookLM آپلود کنید. سپس هر پرسشی که داشتید، مطرح کنید تا بر اساس منابع آپلود شدهی خودتان به آنها پاسخ داده شود...»