‏نمایش پست‌ها با برچسب wpf. نمایش همه پست‌ها
‏نمایش پست‌ها با برچسب wpf. نمایش همه پست‌ها

۱۳۹۳ دی ۲, سه‌شنبه

ایجاد ViewModel و View برای Caliburn.Micro به روش scaffolding

در صورتی که با فریم ورک Caliburn آشنایی ندارید مستندات آن را مطالعه کنید.
برای شروع ابتدا یک پروژه WPF معمولی بسازید و دستورات زیر را در پنجره Package Manager Console وارد کنید. در صورتی که این پنجره را نمی بینید از منوی View گزینه Other Windows و سپس Package Manager Console را انتخاب کنید.

Install-package Caliburn.Micro.Scaffolder

سپس صبر کنید تا همه پیش نیاز های آن که شامل EntityFramework، Caliburn.Micro، T4Scaffolding و T4Scaffolding.Core است نصب شود.

حالا یک پوشه جدید در پروژه خود بسازید و آن را Models نامگذاری کنید. سپس کلاس جدیدی به نام Person به این پوشه اضافه کنید و کد زیر را به جای کلاس خالی Person قرار دهید:

    public class Person
    {
        public int Id { get; set; }

        public string FullName { get; set; }
    }

پروژه را Build کنید.
اکنون در Package Manager Console فرمان زیر را وارد کنید:

Scaffold Add-ViewAndViewModel Person -Force

و Enter بزنید.
خب با توجه به خروجی ارائه شده باید متوجه شده باشید که چه اتفاقی افتاده. فایل های ViewModel و View متناظر درحالی که یک TextBox در آن قرار دارد و به کمک قائده نامگذاری به FullName در ViewModel متناظر Bind شده، ایجاد شده است.

این Package در واقع نسخه دست کاری شده Caliburn.Micro.Scaffolding است که به آن قابلیت هایی اضافه کرده ام. مثلا ViewModel شما تمامی Property های کلاس Model شما را پیاده سازی می کند. همینطور View تولید شده دارای فیلدهایی Bind شده به ViewModel شماست.
البته باید توجه داشته باشید که کدهای تولید شده جهت استفاده نهایی در پروژه های تجاری شما کامل نیست. مزیت استفاده از Scaffolding در این است که شما مجبور نیستید کدهای خسته کننده تکراری را برای ViewModel ها و طراحی اولیه View را انجام دهید. در عوض، با تولید خودکار این کدها به ازای هر Model، شما فقط به سفارشی کردن ViewModel و View متناظر با آن می پردازید.

۱۳۹۱ بهمن ۷, شنبه

آشنایی با Prism - Bootstrapper

در پست قبل اندکی با Bootstrapper، آشنا شدیم. اما این کلاس حرف های بسیاری برای گفتن دارد. در ادامه با برخی جزئیات این سلسله تنظیمات بیشتر آشنا می شویم.


شکل بالا گام های لازم برای مقدار دهی اولیه ی یک برنامه Prism را مشخص می کند. 

ایجاد یک ILoggerFacade: با override کردن تابع CreateLogger در Bootstrapper، شما این اختیار را دارید تا یک logger برای خود تهیه کنید. این logger در طول کل برنامه در دسترس شما خواهد بود. همچنین خود کتابخانه Prism، گزارش های خود را به این logger می فرستد تا شما در جریان روند امور باشید. برای طراحی یک logger کافیست یک کلاس تعریف کرده و واسط ILoggerFacade را در آن پیاده سازی کنید. ما برای سادگی کار گزارش ها را بر روی فایل ذخیره می کنیم. شما می توانید هر نوع پیاده سازی (ذخیره در بانک اطلاعاتی، ارسال ایمیل و ...) دلخواه خود را انجام دهید.
using System;
using System.IO;
using System.Text;
using Microsoft.Practices.Prism.Logging;

namespace PrismApp.Shell
{
    public class TextFileLogger : ILoggerFacade
    {
        private readonly string _fileName = Path.Combine(Environment.CurrentDirectory, "PrismApp.log");
        private readonly object _syncLock = new object();

        public TextFileLogger()
        {
        }

        public void Log(string message, Category category, Priority priority)
        {
            WriteToFile(category.ToString(), message);
        }


        private void WriteToFile(string type, string message)
        {
            lock (_syncLock)
            {
                using (var writer = new StreamWriter(_fileName, true, Encoding.UTF8))
                {
                    writer.WriteLine(string.Format("{0:G}\t{1}:{2}", DateTime.Now, type, message));
                }
            }
        }
    }
}

واسط ILoggerFacade فقط یک تابع Log دارد که در آن پیام، وضعیت و اولویت آن مشخص شده است. شما می توانید مطابق میل خود، اطلاعات لازم را دریافت و ذخیره کنید. در این مثال، فقط وضعیت و متن را ذخیره می شود. اکنون برای مقدار دهی اولیه logger در کلاس Bootstrapper به این شکل عمل می کنیم:

        protected override Microsoft.Practices.Prism.Logging.ILoggerFacade CreateLogger()
        {
            return new TextFileLogger();
        }
با ایجاد یک نمونه از کلاس ساخته شده و بازگرداندن آن، Prism، از این نمونه برای عملیات گزارش استفاده می کند.

ایجاد Module Catalog: در صورتی که برنامه ی شما ماژولار است، با پیاده سازی تابع CreateModuleCatalog می توانید نحوه بارگذاری ماژول های برنامه را مشخص کنید. برای مثال اگر می خواهید ماژول ها را از مسیر خاصی بارگذاری کنید می توانید یک نمونه از DirectoryModuleCatalog استفاده کنید.

        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new DirectoryModuleCatalog()
            {
                ModulePath = Environment.CurrentDirectory;
            };
        }

بدین وسیله، هنگام اجرای برنامه، Prism در مسیر مشخص شده (در این مثال، مسیر اجرای برنامه) به دنبال ماژول ها می گردد و در صورت پیدا کردن، آن ها را بارگذاری می کند. در پست های آینده با نحوه ایجاد ماژول بیشتر آشنا خواهیم شد.

پیکربندی Container: تزریق وابستگی نقش کلیدی را در برنامه های مبتنی بر Prism ایفا می کند. در این مرحله، سرویس های اصلی (Core Services)، بطور پیشفرض توسط Prism در Container ثبت می شوند. اما می توان این کار را بطور دستی نیز انجام داد. اضافه بر این، سرویس های برنامه کاربردی نیز باید توسط برنامه نویس در Container ثبت شوند. از انجایی که فعلا فقط قصد تکمیل اسکلت کامل یک برنامه ماژولار Prism را داریم، سرویس خاصی در Container ثبت نمی کنیم. Prism بطور پیش فرض، بسیاری از سرویس های مورد نیاز را ثبت می کند. در پست های بعدی به این قسمت برمیگردیم. فعلا تابع ConfigureContainer ما در حد ثبت MainWindow باقی می ماند.

        protected override void ConfigureContainer()
        {
            // Register default core services
            base.ConfigureContainer();
            // Register Shell (main window)
            Container.RegisterType<MainWindow>();
        }
پیکربندی RegionAdapterMapping و RegionBehavior پیش فرض: در برنامه های مبتنی بر Prism، مدیریت View ها اندکی متفاوت از برنامه های WPF عادی است. در یک برنامه ماژولار ممکن است View خاصی در یک ماژول تعریف شده باشد و با اجرای فرمانی نیاز باشد آن View به کاربر نمایش داده شود.  بدین منظور از قبل باید در View اصلی برنامه (همان Shell که در اینجا منظور MainWindow است) قسمت هایی تعریف شوند که قادر به نمایش View ها باشند. به این قسمت ها Region گفته می شود. بدین صورت شما می توانید در Shell برنامه، یک یا چند Region با نام های منحصر به فرد طراحی کنید تا ماژول ها بتوانند در آن View های خود را نمایش دهند. در پست های بعدی بیشتر به این قسمت خواهیم پرداخت و فعلا توابع مربوط به آن را override نمی کنیم.

ثبت FrameworkExceptions: شما می توانید استثنا (Exception) های خاصی را در Prism ثبت کنید تا هنگام رخ دادن، استثنای اصلی توسط تابع GetRootException پیدا شده و نمایش داده شود. استثناهای ثبت شده، به سادگی در یک لیست Static ذخیره می شوند. در صورتی که یک Exception، در این لیست ثبت نشده باشد، پس از رخ دادن بدون جستجو برای InnerException اصلی، دوباره throw می شود. از این امکان بسیار به ندرت استفاده می شود.

CreateShell: همانطور که در پست قبل خواندید، از این تابع برای ایجاد View اصلی برنامه (که Shell نامیده می شود) استفاده می شود. در ادامه تابع دیگری به نام InitializeShell وجود دارد که پس از این تابع فراخوانی می شود و پیاده سازی آن نیز اختیاری است. در صورتی که برنامه شما WPF باشد می توانید در این تابع، کد مربوط به مقدار دهی اولیه و نمایش پنجره اصلی را بنویسید. بدین جهت، تغییرات زیر در کلاس Bootstrapper انجام می شود:

        protected override System.Windows.DependencyObject CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }

        protected override void InitializeShell()
        {
            Application.Current.MainWindow = (Window)this.Shell;
            Application.Current.MainWindow.Show();
        }
بدین صورت، ما با override کردن تابع CreateShell، فقط یک نمونه سازی از کلاس MainWindow انجام می دهیم و سپس در تابع InitializeShell، پنجره اصلی برنامه WPF را مشخص کرده و آن را به کاربر نمایش می دهیم.

InitializeModules: همان طور که قبلا ذکر شد، در تابع ConfigureContainer، برخی سرویس های اصلی Prism با کلاس های پیش فرض ثبت می شوند. یکی از آن سرویس ها، IModuleManager است. این سرویس (واسط) وظیفه بارگذاری ماژول های برنامه را بر عهده دارد. در این سرویس، رویداد هایی به نام ModuleDownloadProgressChanged و LoadModuleCompleted وجود دارد که با subscribe شدن، می توان پیشرفت بارگذاری ماژول ها و زمان اتمام بارگذاری آن ها را مشاهده کرد. کتابخانه Prism یک پیاده سازی پیشفرض (کلاس) از این سرویس به عنوان ModuleManager دارد که در تابع ConfigureContainer، آن را ثبت کرده است. در صورتی که پس از override کردن این تابع، تابع base آن فراخوانی شده باشد، این سرویس بطور پیش فرض در Container ثبت شده است و نیازی نیست چیزی را تغییر بدهید.

خب، تا به این جا به اندازه کافی با Bootstrapper آشنا شده ایم. در پست بعدی به سراغ ماژول نویسی رفته و بخشی از مفاهیم Region ها را در کنار آن می آموزیم.

دریافت سورس کد کامل با تگ Step 2

۱۳۹۱ بهمن ۵, پنجشنبه

آشنایی با Prism - شروع به کار

Prism یک فریم ورک برای طراحی نرم افزارهای قوی، قابل انعطاف با نگه داری آسان در WPF، Silverlight و Windows Phone است.
به کمک Prism شما می توانید برنامه هایی با اجزای وابسته ضعیف طراحی کنید که می توانند مستقل باشند ولی به آسانی به برنامه اصلی مجتمع شوند. این گونه برنامه ها عموما برنامه های ترکیبی (Composite Application) گفته می شود.

برای شروع می توانید Prism را به همراه سورس آن دانلود کرده و بر روی سیستم خود extract کنید. کتابخانه Prism برای برنامه های Desktop (و WPF) در مسیر bin\Desktop قرار دارد. 
Prism جهت تزریق وابستگی ها، بطور پیشفرض با Unity کار می کند. ولی در صورتی که با فریم ورک های دیگر تزریق وابستگی کار می کنید، دست شما باز است. مثلا برای Ninject یک extension ویژه استفاده در Prism تهیه شده است. همینطور Structure Map هم دارای کلاس های ویژه Prism است.

در صورتی که با مفاهیم تزریق وابستگی آشنایی ندارید، می توانید از اینجا شروع کنید.

ابزار کار لازم
  • Visual Studio نسخه 2010 یا 2012 (البته نسخه های قدیمی تر هم قابل استفاده هستند ولی نسخه Prism 4.1 فقط با دات نت 4 کار می کند. در صورتی که از Visual Studio 2008 استفاده می کنید باید نسخه Prism 2.1 را نصب کنید.)
  • اگر تمایلی به دستکاری source code کتابخانه Prism ندارید می توانید براحتی آن را توسط Nuget نصب کنید. پس درصورتی که آن را ندارید، دانلود کرده و نصب کنید.


آماده سازی محیط کار
ویژوال استودیو را اجرا کنید و یک پروژه جدید از نوع WPF Application ایجاد کنید. دقت کنید، دات نت 4 در لیست Target Framework انتخاب شده باشد. حالا در پنجره Package Manager Console دستور زیر را وارد کنید:

Install-Package Prism

و در ادامه برای نصب Unity، دستور زیر را وارد کنید:

Install-Package Unity

حالا نوبت نصب Unity Extension برای Prism است:

Install-Package Prism.UnityExtensions

برنامه های WPF از فایلی به نام App.xaml شروع می شوند. بطور پیش فرض، در این فایل، مسیر پنجره اصلی برنامه در یک مشخصه به نام StartupUri تعیین شده است ولی برنامه های مبتنی بر Prism، راه حلی متفاوت برای شروع دارند.

کلاس Bootstrapper
Bootstrapper نام کلاسی است که تمامی پیکربندی اولیه شروع برنامه در آن مشخص می شود. در Solution Explorer بر روی پروژه راست کلیک کرده و کلاس جدید به آن اضافه کنید. آن را Bootstrapper بنامید و کد زیر را در آن بنویسید.

    public class Bootstrapper : UnityBootstrapper
    {
        protected override System.Windows.DependencyObject CreateShell()
        {
            var mainView = new MainWindow();
            mainView.Show();
            return mainView;
        }
    }

کلاس Bootstrapper ما از کلاس UnityBootstrapper ارث برده است. تنها تابع که پیاده سازی آن اجباری است، تابع CreateShell است. این تابع، مقداری از نوع DependencyObject برمیگرداند که نقطه شروع نمایش برنامه را مشخص می کند. از آنجایی که برنامه ما یک WPF Application است، و در آن، MainWindow.xaml قرار است فرم اصلی باشد، ما یک نمونه از ان را ایجاد کرده، نمایش داده و برمی گردانیم. البته این روش new کردن به صورت دستی چندان مناسب نیست، بهتر است به کمک Unity این نمونه سازی را انجام دهیم:

    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Prism.UnityExtensions;

    public class Bootstrapper : UnityBootstrapper
    {
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            Container.RegisterType<MainWindow>();
        }

        protected override System.Windows.DependencyObject CreateShell()
        {
            var mainView = Container.Resolve<MainWindow>();
            mainView.Show();
            return mainView;
        }
    }

اکنون یک تابع دیگر از UnityBootstrapper را override کرده ایم و در آن علاوه بر فراخوانی ConfigureContainer کلاس والد، پنجره اصلی (کلاس MainWindow) را در Unity Container ثبت کردیم. از تابع ConfigureContainer جهت رجیستر کردن سرویس ها، اینترفیس ها و کلاس های مورد نیاز در برنامه استفاده می شود. (using ها فراموش نشود!)
به App.xaml برمیگردیم. ابتدا مشخصه StartupUri و مقدار آن را پاک کرده و سپس به code behind آن رفته و تغییرات زیر را ایجاد کنید:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            var bootstrapper = new Bootstrapper();
            bootstrapper.Run();

        }
    }
ما تابع OnStartup را override کردیم. این تابع با شروع برنامه، اجرا می شود. در آن یک نمونه از Bootstrapper ساختیم و تابع Run آن را اجرا کردیم.
اکنون پروژه را کامپایل نموده و اجرا کنید. در صورتی که تمام مراحل را بدون خطا سپری کرده باشید، پنجره برنامه را مشاهده خواهید کرد.

سورس کامل این قسمت را تحت عنوان Step 1 از اینجا دانلود کنید.

۱۳۹۱ بهمن ۱, یکشنبه

رفع مشکل باگ مربوط به Adorner


چند وقت پیش درباره باگ مربوط به Adorner  پستی رو نوشتم. بلاخره راه حل درست و حسابی براش پیدا کردم. اینبار به جای استفاده از تصویر از قابلیت RotateTransform مربوط به کنترل های WPF استفاده می کنیم. به این صورت:

 <TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Width="200">
            <local:WatermarkService.Watermark>
                <TextBlock Name="watermarkTextBox" Text="جستجو" RenderTransformOrigin="0.5,0.5" TextAlignment="Right">
                    <TextBlock.RenderTransform>
                        <ScaleTransform ScaleX="-1" />
                    </TextBlock.RenderTransform>
                </TextBlock>
            </local:WatermarkService.Watermark>
        </TextBox>
همون طور که می بینید ScaleTransform یه خصوصیت به اسم ScaleX داره که اگه به اون عدد منفی بدین، کنترل به صورت معکوس نمایش داده میشه. دقیقا" همون چیزی که می خواستیم! ولی ممکنه متن داخل کنترل دچار جابجایی بشه که به کمک RenderTransformOrigin می تونیم موقعیتش رو تنظیم کنیم.

۱۳۹۱ دی ۳۰, شنبه

باگ در کلاس Adorner مربوط به WPF 4.0

چند وقت پیش داشتم روی یه کادر متن جستجو برای یه برنامه حسابداری کار می کردم که به یک رفتار عجیب برخوردم. برای نمایش کلمه جستجو درون یک کادر متن از adorner استفاده کردم. در کمال تعجب دیدم وقتی FlowDirection پنجره فعلی روی RightToLeft تنظیم باشه، کلمه جستجو بصورت کاملا معکوس نمایش داده می شه. ینی همون طور که شما کلمه آنبولانس رو به صورت معکوس روی جلو ماشین آمبولانس می بینید! :)

خب مشکل از WPF بود که بلاخره بعد از مدتی مکاتبه، مایکروسافت تایید کرد که این باگ وجود داره. البته من یه جایگزین برای این مشکل پیدا کردم. استفاده از تصویر! متاسفانه این مشکل برای تصویر ها هم وجود داشت و اونا رو به صورت معکوس نمایش می داد که این بار دست به دامن فتوشاپ شدم و تصویر کلمه جستجو رو برعکس کردم!

خلاصه امیدوارم این مشکل در NET 4.5 رفع شده باشه.

شما می تونید گذارش خطای ارسالی به همراه یه نمونه برنامه ارسالیم رو اینجا ببینید.

به روز رسانی: راه حل این مشکل رو می تونید توی این پست بخونید.