یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

یادداشتهای یک برنامه نویس

بیان تجربیات و دیدگاه های یک برنامه نویس در مورد نرم افزار , طراحی و تولید برنامه با استفاده از تکنولوژی های مایکروسافت

يكشنبه, ۲۰ بهمن ۱۳۹۲، ۱۰:۳۴ ب.ظ

ایجاد یک کنترل Guage در WPF (قسمت اول)

حقیقتا تعیین یک چارچوب محدود برای بیان یک مطلب خاص در یکی از زمینه های برنامه نویسی (حداقل برای من) بسیار دشوار می باشد. به نظر من این انتخاب باید به گونه ای باشد که مطلب به خودی خود و مستقل دارای یک بار علمی قابل توجه بوده و کمترین نیاز را به سایر مباحث داشته باشد که متاسفانه دستیابی به این هدف تقریبا غیر ممکن می نماید.
انگیزه اصلی من در این مطلب بیان تجربه ایجاد یک کنترل خاص به صورت یک
Guage (مطابق تصویر سه) و شرح امکانات مورد نیاز جهت ایجاد یک چنین کنترلی در WPF و توصیف آسان ترین راه ممکن برای استفاده از آن در یک برنامه کاربردیست.  در تلاش برای رسیدن به این هدف، لاجرم توصیف برخی کلاسهای پایه و نقش آنها در ساختار سلسله مراتبی کلاسهای WPF اجتناب ناپذیر می باشد. در آخرین قسمت حتما پروژه کامل این مثال را برای دانلود و استفاده عزیزان ضمیمه خواهم نمود.

 

یک نگاه دقیق به ساختار توارث کلاسها در WPF و آنچه که Visual Hierarchy Of Classes نامیده شده است،  به وضوح نشان میدهد که موقعیت کلاس FrameworkElement بسیار ویژه و استثناییست. در واقع از همین روست که کلیه کلاسهای اصلی کاربردی در UI و حتی Layout نظیر Control, TextBlock,Image  و.... مستقیم و غیر مستقیم از این کلاس منشعب میشوند.
به عبارت ساده تر کلیه ویژگیها
(Fields And Attributes) و قابلیتهایی (Methods) که از کلاسهای Parent کلاس FrameworkElement در اختیار این کلاس گذاشته شده است، زمینه را برای یک ایفای نقش مهم در WPF فراهم نموده است.

در بالاترین سطح DispatcherObject که در System.Windows.Threading قرار دارد فراهم کننده کلیه قابلیتهای Messaging (ارتباط داخلی کلاسها با یکدیگر با استفاده از مکانیسم ارسال و دریافت Message) و استفاده بهینه از Threadها در کلیه Objectهای WPF تلقی میشود. وجود این کلاس در صدر این ساختار تضمین کننده این موضوع مهم است که بوسیله ویژگی Dispatcher می توان از تواناییهای تدارک دیده شده این کلاس استفاده نمود. شاید مهمترین قابلیت Dispatcher توانایی Listening به انواع متفاوت Messageها و پیام هاییست که تقریبا در همه سناریوهای یک برنامه WPF (نظیر پرس و جوی بین یک Container مثل Panel برای اطلاع از اندازه و موقعیت مکانی کنترل های فرزند جهت تخصیص بهترین و مناسب ترین فضای ممکن در مبحث Layout که یکی از نقاط قوت WPF محسوب میشود ) نظیر آن را می توان یافت.
این کلاس به جز مواردی که ذکر شد هیچ دخل و تصرفی به طور مستقیم در گرافیک ندارد (منظور اینکه یک کلاس
Visual و قابل دید نیست) و صرفا به طور تخصصی عهدا دار اموریست که به اجمال توضیح داده شد.

 
کلاس DependencyObject یکی دیگر از کلاسهای کلیدی در ساختار سلسله مراتبی کلاسهای WPF بوده و یکی از مهمترین وظایف آن ایجاد یک مکانیسم بسیار توانا و کاربردی در انتشار پیام هایی مبنی بر بروز تغییرات در فیلدهای یک Object (به شرطی که چنانچه در ادامه خواهیم دید معیارها  و اصول Dependency Property را تدارک دیده باشد) می باشد. در واقع مکانیسم و توانایی انتقال پیام از کلاس بالاتر به ارث برده شده و وظیفه آن در این کلاس بسیار تخصصی تر گردیده است. این کلاس در System.Windows قرار دارد و باز مجددا باید توجه داشت که هیچ دخل و تصرفی در گرافیک ندارد.
کمی پایین تر (مطابق تصویر یک) کلاسهای Visual و DrawingVisual قرار دارند که چنانچه از نام کلاس Visual  نیز مشخص می باشد نقطه آغاز بسیاری از تواناییهای WPF در گرافیک و رسم اشکال گرافیکی ، همین جا قرار دارد.عملیات Rendering و حتی Caching  (جهت به حداقل رساندن استفاده از تواناییهای پردازنده در رسم اشکال گرافیکی) بخشی از تواناییهای ذاتی این کلاس قدرتمند در WPF می باشد. با وجود این همه تواناییهای بالقوه، به دلیل آنکه از نوع Abstract می باشد، به طور مستقیم امکان نمونه سازی (Instantiate) از این کلاس در WPF وجود ندارد و به طور معمول از یکی از کلاسهای سطوح پایین تر نظیر  UIElement (که ریشه اصلی کلاسهای کنترل در WPF محسوب میشود) یا Viewport3DVisual (که برای رسم اشکال گرافیکی سه بعدی پیش بینی شده است ) و یا ContainerVisual (که صرفا یک محتوا برای تجمع اشکال گرافیکی را فراهم میکند) استفاده میشود. اما از دیدگاه Performance مفید ترین و بهترین انتخاب کلاس DrawingVisual می باشد که خود از ContainerVisual منشعب شده و توانایی رسم کلیه اشیاء گرافیکی تجمیع شده در یک نمونه از کلاس ContainerVisual را به بهترین نحو ممکن دارا می باشد.
چنانچه در کد مثال اول بوضوح مشخص شده است فراخوانی متد
RenderOpen یک DrawingContext ایجاد میکند که این نیز به نوبه خود با فراخوانی متد DrawGeometry می تواند توده ای از ژئومتری ها یا اشکال گرافیکی تجمیع شده در یک Stream را با قلم (Pen) و Brush (رنگ گرافیک برای رنگ آمیزی اشکال تو پر) دلخواه ترسیم نماید.

استفاده از این کلاس صرفا برای مقاصد رسم اشکال گرافیکی و بدون نیاز به تواناییهایی نظیر تنظیم اتوماتیک چیدمان (Layout) ، رویدادها (Events) و Data Binding از دیدگاه Performance بسیار مقرون به صرفه ، سریع و کارآمد است.
اصولا یکی از رموز موفقیت در
WPF اتخاذ استراتژی "همواره ابزار درست را برای انجام وظیفه مناسب انتخاب کن" می باشد و این یک اصل بسیار مهم و ضروری در ترمینولوژی WPF است که باید همواره به آن توجه داشت . به عبارت ساده تر اگر به عنوان مثال نیاز به رسم یک نمودار از نوع خطی، ستونی، منحنی، کلوچه ای و  یا هر شکل گرافیکی دیگر و در نهایت با امکان قابلیت Hit Testing (بررسی این واقعیت که آیا دو یا چند شکل گرافیکی با یکدیگر سطح مشترک و یا همپوشانی دارند یا خیر؟) داشته باشیم ، انتخاب این کلاس بسیار مقرون به صرفه  بوده و یک انتخاب هوشمندانه تلقی میشود.

در این زمینه لطفا به مثال زیر توجه نمایید . در حقیقت این کد سریعترین و کارآمدترین روش رسم اشکال در WPF را با استفاده از ایجاد یک کلاس ساده منشعب از DrawingVisual نمایش میدهد. خروجی حاصل از این کد در تصویر شماره دو نشان داده شده است.

 

 

 

       public class SectorVisual : DrawingVisual

       {

              public SectorVisual()

              {

                     StreamGeometry geometry = new StreamGeometry();

                     using (StreamGeometryContext c = geometry.Open())

                     {

                           c.BeginFigure(new Point(200, 200),true , true );

 

                           // First line

                           c.LineTo(new Point(175, 50), true, true);

 

                           // Bottom arc

                           c.ArcTo(new Point(50, 150), new Size(1, 1), 0, true,

                                  SweepDirection.Counterclockwise, true , true);

 

                           // Second line

                           c.LineTo(new Point(200, 200),true , true);

                     }

           

                     // Draw the geometry

                     using (DrawingContext context = RenderOpen())

                     {

                        Pen pen = new Pen(Brushes.Black, 1);

                        context.DrawGeometry(Brushes.CornflowerBlue, pen, geometry);

                     }

             }

      }

 

 

 

قطعا همه همکارانی که در گذشته تجربه کار با یکی از Platform های گرافیکی مثل GDI+ یا OpenGL و یا حتی Direct X را داشته باشند از قبل کاملا با دستورات کد مثال آشنا می باشند.

همانطور که قبلا اشاره شد کلیه ویژگیهای Caching قبلا در Visual در نهایت هوشمندی تدارک دیده شده است. اصولا WPF مجهز به یک سیستم نوع Retained Graphic می باشد (بر خلاف Windows Form و GDI که Immediate Mode می باشد) که معنای دقیق این گفته این است که بر خلاف Windows Form و تواناییهایی که DC یا همان Drawing Context در Windows Form دارا می باشد نیازی نیست که نگران ReDraw یا رسم مجدد اشکال گرافیکی باشیم. قابلیت Caching به صورت خودکار و بدون دخالت برنامه نویس با دقت بسیار قابل توجهی این موضوع را کنترل می کند.

همانگونه که انتظار داریم DrawingContext در WPF قابلیت رسم اشکال گرافیکی پایه نظیر خط (Line)، مستطیل (Rectangle)، کمان (Arc) و حتی موارد پیشرفته تر مثل تصاویر متحرک (Video) را در کیفیت بسیار مطلوب دارا می باشد.
                                                                                                              ادامه دارد...

 

ساختار سلسله مراتبی کلاسهای WPF

 خروجی مثال استفاده مستقیم از DrawingVisual

خروجی نهایی ایجاد یک کنترل Guage

 

موافقین ۰ مخالفین ۰ ۹۲/۱۱/۲۰
مهران حسین نیا

Visual

DispatcherObject

Guage

WPF

DrawingVisual

نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی