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

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

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

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

۲ مطلب با کلمه‌ی کلیدی «DrawingVisual» ثبت شده است

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

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

         در کد ارائه شده مثال ، به منظور توصیف (لطفا توجه داشته باشید که عمدا از عبارت ترسیم استفاده نشده است، زیرا که این کلاس اساسا برای توصیف (Describe) ویژگیهای ظاهری یک شکل دو بعدی کاربرد دارد و عملیات ترسیم (Drawing) و استفاده از یک میزبان بصری (Visual Container) و قابل دید مراحل دیگری وجود دارد که در ادامه توضیح داده خواهد شد) ویژگیهای  مورد نیاز جهت ترسیم یک شکل ساده دو بعدی از Geometry استفاده شده است. کلاس Geometry و همه مشتقات آن از جمله ElipseGeomtry، PathGeometry و .... صرفا برای توصیف یک یا مجموعه ای از موجودیت های گرافیکی و رصد نقطه تلاقی اشاره گر Mouse با این اشکال (Hit Testing) و تعیین دریچه های برش نما (Clip Regions) استفاده میشوند. در بحث تواناییهای WPF برای ایجاد یک انیمیشن نیز، که اتفاقا یکی از نقاط قوت WPF نیز محسوب میگردد از Geometry به عنوان مسیر (Path) یک یا چند سکانس انیمیشن استفاده میشود.

در یک جمع بندی کلی، به نظر میرسد که کلاسهای Geometry و Shape در ظاهر با تواناییهایی کمابیش یکسان برای توصیف اشکال دو بعدی استفاده میگردند. به عنوان مثال در یک نگاه کلی کلاسهای EllipseGeometry و Ellipse که اولی از Geometry و دومی از Shape منشعب میشوند برای توصیف/ترسیم یک دایره/بیضی کاملا یکسان به نظر میرسند اما حقیقتا تفاوتهای مهمی وجود دارد که باید به آن توجه داشت.
در ابتدا کلاس Shape انشعابی (Derived) از کلاس بسیار مهم و کلیدی FrameworkElement می باشد. در واقع یک موجودیت گرافیکی ایجاد شده توسط Shape علاوه بر خواص گرافیکی از دیدگاه WPF یک Element با تواناییهای بسیار خاص تلقی میگردد. از مهمترین این تواناییها میتوان به مواردی مانند قابلیت ذاتی ترسیم و مشارکت در مکانیسم های بعضا پیچیده چیدمان یا Layout اشاره کرد که Geometry و مشتقات آن فاقد این تواناییها می باشد.
دقیقا به همین دلیل است که در اغلب منابع معتبر هنگام معرفی Geometry یا برخی دیگر از کلاسهای سطوح پایین تری که در گرافیک دخیل هستند از اصطلاح Lightweight استفاده میشود که اشاره به بار بسیار اندکیست که در مقایسه با Shape در هنگام استفاده از آنها به سیستم تحمیل میگردد.
به نظرم تا حدودی از اصل موضوع فاصله گرفته ایم و بنابراین قبل از بازگشت به موضوع اصلی به نظرم توجه به این نکته خالی از لطف نیست که یکی از کلاسهای منشعب از Shape یعنی کلاس Path در واقع از Geometry برای توصیف و ترسیم اشکال گرافیکی استفاده میکند که اتفاقا در مبحث رسم اشکال گرافیکی از تنوع قابلیت شگفت انگیزی برخوردار است.

در کد مثالی که در قسمت اول ارائه شد، ما به سادگی با استفاده از ترکیب سه Geometry ساده (دو قطعه خط و یک کمان) شکل ساده مورد نظر خودمان را توصیف نموده ایم. (مجددا تکرار میکنم که عملیات فیزیکی ترسیم هنوز انجام نشده است) . مجموعه دستورات این توصیف ساده در یک Stream خاص منظوره که کلاس StreamGeometry می باشد قرار میگیرد. با محاصره کردن این توصیف در داخل یک بلاک using تلاش میکنیم که مدیریت استفاده از منابع تخصیص یافته برای انجام این کار را تا حد امکان به عهده گرفته و آن را بهینه نماییم.

اینک برای ترسیم اطلاعاتی که قبلا توصیف شده نیاز به یک DrawingContext ضروریست.

از آنجاییکه کلاس ایجاد شده ما یعنی SectorVisual کلاسی منشعب از DrawingVisual است توصیه میشود برای به دست آوردن یک DrawingContext از متد RenderOpen که برای همین منظور در DrawingVisual پیش بینی شده است استفاده نماییم. چنانچه در مثال مشاهده میشود مجددا برای مدیریت بهتر منابع سیستم استفاده از DrawingContext در یک بلاک using محصور شده است که بلافاصله بعد از فراخوانی DrawGeometry و خروج از بلاک using ، اجبار به آزادسازی حافظه انجام میشود. نام متد DrawGeometry  به روشنی نشان میدهد که این متد، توصیف مرتبط با یک موجودیت گرافیکی که در قالب یک Geometry ذخیره شده است را دریافت نموده و گرافیک مذکور با قلم (Pen) و Brush دلخواه را ترسیم میکند.
اما متاسفانه هنوز کل پروسه به اتمام نرسیده است. برای رویت و چیدمان این موجودیت گرافیکی نیاز به یکی از کلاسهای آگاه به پروسه Layout الزامیست. به عبارت ساده تر این کلاسها، کلاسهایی هستند که در ساختار سلسله مرتبی کلاسهای WPF از UIElement منشعب شده اند. برای رفع این مشکل دو راه حل  اصلی وجود دارد . یکی از آنها Override کردن متد OnRender و یا اساسا ایجاد یک کلاس سفارشی منشعب از UIElement است . چنانچه در انتهای همین قسمت مشاهده خواهیم نمود، برای ایجاد یک کنترل Guage ، که هدف نهایی ما از کل این مباحث دو قسمتی بوده، از روش اول استفاده شده است. در کد زیر روش استفاده از راه دوم نیز نشان داده شده است:

 

    public class VisualContainer : UIElement

       {

        private VisualCollection _children;

        private SectorVisual _visual = new SectorVisual();

 

        protected override Visual GetVisualChild(int index)

        {

            return _visual;

        }

 

        protected override int VisualChildrenCount

        {

            get { return 1; }

        }

 

       }

 

 

در ساده ترین شکل ممکن فقط کافیست دو متد GetVisualChild و VisualChildCount را به ترتیبی که در مثال نشان داده شده است Override نماییم.

اگر در Visual Studio این کار را انجام بدهیم، بعد از یک بار Compile کردن کد مذکور، یک نمونه قابل استفاده از VisualContainer را در جعبه ابزارها (Toolbox) مشاهده خواهیم کرد که به سادگی با Drag نمودن آن در داخل پنجره یا Page میزبان ، یک نمونه از این کلاس ایجاد می شود که نمایش تصویر مورد نظر ما میسر خواهد شد. به جای این روش بدیهیست که با نمونه سازی این کلاس در کد یا در XAML به نتیجه مشابهی دست خواهیم یافت.

این مثال بهانه ای بود برای یک بررسی اجمالی گرافیک در WPF که همانطور که در قسمت اول این بحث مطرح شد هدف اصلی من ایجاد یک کنترل گرافیکی سفارشی (Guage) است که کد آن را در زیر مشاهده میکنید:

 

public class Guage : FrameworkElement

       {

              public int Ticks { get; set; }

              public Size TickSize { get; set; }

              public Brush TickBrush { get; set; }

 

              protected override void OnRender(DrawingContext dc)

              {

                     double radius = Math.Min((RenderSize.Width – TickSize.Width) / 2,

                                                                     (RenderSize.Height – TickSize.Height) / 2);

                     Point center = new Point((RenderSize.Width – TickSize.Width) / 2,

                                                                     (RenderSize.Height – TickSize.Height) / 2);

            Point center2 = new Point(RenderSize.Width / 2,

                            RenderSize.Height / 2);

            if (Ticks > 50)

            {

                dc.DrawEllipse(Brushes.Transparent, new Pen(Brushes.Black, 2), center2, 15, 15);

            }

                     for (int i = 0; i <= Ticks; i++)

                     {

                           double ratio = (double)I / Ticks;

                           double x = center.X + radius * Math.Cos(Math.PI * ratio);

                           double y = center.Y – radius * Math.Sin(Math.PI * ratio);

                Typeface tf = new Typeface(“Tahoma”);

                CultureInfo ci = new CultureInfo(“fa-IR”);

                int adjust = 100 / Ticks;

                int result = Math.Abs(100 – (i * adjust));

                FormattedText ft = new FormattedText((result).ToString(), ci, FlowDirection.LeftToRight, tf, 8.5, new SolidColorBrush(Colors.Black));

                           Rect currRect = new Rect(TickSize);

                           currRect.Offset(x, y);

                Point strokeCenter = new Point(currRect.X + 0.5 * currRect.Width,

                                                currRect.Y + 0.5 * currRect.Height);

 

                           RotateTransform rotation = new RotateTransform(90 – 180 * ratio,

                                                                                         strokeCenter.X, strokeCenter.Y);

                           dc.PushTransform(rotation);

                           dc.DrawRectangle(TickBrush, null, currRect);

               

                if (Ticks < 50)

                {

                    dc.DrawText(ft, new Point(x-3, y+(TickSize.Height)));

 

                }

                           dc.Pop();

                     }

              }

       }

 

برای دستیابی به نتیجه بهتر پس از ایجاد یک پروژه WPF در یک Name Space اختصاصی (مثلا MyControls) کد کلاس Guage را قرار داده و در آخرین مرحله تمهیدات لازم در XAML برای میزبانی این کنترل به همراه یک کنترل Slider جهت آزمایش عملکرد Guage قرار میدهیم. کد XAML مورد نیاز برای انجام این کار به شرح زیر میباشد:

 

<Window

        xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

        xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

        xmlns:Controls=”clr-namespace: UsingGuage.MyControls” x:Class=”UsingGuage.MainWindow”

        Title=”MainWindow” Height=”450” Width=”825”>

    <Window.Resources>

        <DrawingBrush x:Key=”DrawingBrush1”

                                    Viewbox=”0,0,21,121”

                                    ViewboxUnits=”Absolute”>

            <DrawingBrush.Drawing>

                <GeometryDrawing>

                    <GeometryDrawing.Brush>

                        <RadialGradientBrush>

                            <GradientStop Color=”#FFF51212”

                                                                       Offset=”0” />

                            <GradientStop Color=”#FFE45808”

                                                                       Offset=”1” />

                        </RadialGradientBrush>

                    </GeometryDrawing.Brush>

                    <GeometryDrawing.Geometry>

                        <PathGeometry Figures=”M149.50001,79.499991 C149.50001,79.499991 139.49997,189.49994 139.49997,189.49994 139.49997,189.49994 149.50001,199.49994 149.50001,199.49994 L159.50005,189.49994 z”>

                            <PathGeometry.Transform>

                                <MatrixTransform Matrix=”0.999996185317286,0,0,1.00000044504821,-138.9994373343,-79.000027751935” />

                            </PathGeometry.Transform>

                        </PathGeometry>

                    </GeometryDrawing.Geometry>

                    <GeometryDrawing.Pen>

                        <Pen Brush=”#FF000000”

                                                 DashCap=”Flat”

                                                 EndLineCap=”Flat”

                                                 LineJoin=”Miter”

                                                 MiterLimit=”10”

                                                 StartLineCap=”Flat”

                                                 Thickness=”1” />

                    </GeometryDrawing.Pen>

                </GeometryDrawing>

            </DrawingBrush.Drawing>

        </DrawingBrush>

 

        <ControlTemplate x:Key=”SliderTemplate”

                                          TargetType=”Slider”>

            <Grid>

                <Grid.RowDefinitions>

                    <RowDefinition />

                    <RowDefinition />

                </Grid.RowDefinitions>

                <Controls:Guage Ticks=”100”

                                                        TickSize=”1,25”

                                                        Grid.Row=”0”

                                                        Grid.RowSpan=”2”

                                                        TickBrush=”Gray” />

                <Controls:Guage Ticks=”20”

                                                        TickSize=”2,50”

                                                        Grid.Row=”0”

                                                        Grid.RowSpan=”2”

                                                        TickBrush=”IndianRed”

                                                        x:Name=”PART_Track” />

                <TextBlock Text=” آسان است WPF”

                                            FontSize=”14”

                                            Foreground=”Black”

                                            HorizontalAlignment=”Center”

                                            VerticalAlignment=”Center” />

                <Rectangle Grid.Row=”0”

                                            VerticalAlignment=”Stretch”

                                            RenderTransformOrigin=”0.5,1”

                                            Width=”50”

                                            Opacity=”0.75”

                                            Fill=”{StaticResource DrawingBrush1}”>

                    <Rectangle.RenderTransform>

                        <RotateTransform Angle=”{Binding Value, RelativeSource={RelativeSource TemplatedParent}}” />

                    </Rectangle.RenderTransform>

                </Rectangle>

 

            </Grid>

        </ControlTemplate>

    </Window.Resources>

    <DockPanel>

        <TextBlock Text=”{Binding Value, ElementName=_slider}”

                              DockPanel.Dock=”Top” />

        <Slider x:Name=”_moveSlider”

                            Minimum=”-90”

                            Maximum=”90”

                            Value=”-45”

                            Orientation=”Vertical”

                            Width=”50”

                            Margin=”20,0,0,0”

                            VerticalAlignment=”Top”

                            Height=”200”

                            DockPanel.Dock=”Left” />

        <Slider x:Name=”_slider”

                            Minimum=”-100”

                            Maximum=”100”

                            Template=”{StaticResource SliderTemplate}”

                            Value=”{Binding Value, ElementName=_moveSlider}” />

    </DockPanel>

</Window>

 

 

 

ما در کلاس Guage برای تامین هر سه مرحله مورد نیاز (یعنی اولا توصیف شکل گرافیکی، ثانیا ترسیم و قرار دادن آن در یک محتوای مناسب و در نهایت درج در یک میزبان قابل نمایش در Visual Tree)   کلاس FrameworkElement را هدف گرفته و کلاس خود را از آن منشعب (Derived) میکنیم.
همانطور که قبلا هم اشاره شد کلاس FrameworkElement در WPF از اهمیت بسیار بالایی برخوردار است.این کلاس در واقع حلقه اتصال کلاسهای المانهای WPF و امکانات بالقوه هسته (Core-level set) سطوح پاین تر از کلاس UIElement تلقی میشود. این جایگاه خاص و کلیدی این امکان را به این کلاس میدهد که علاوه بر قابلیتهایی که از کلاس UIElement به ارث میبرد، قابلیت های کلیدی استفاده از DataBinding استفاده از Style و .... را نیز برای کلیه کلاسهایی که مستقیم و غیر مستقیم وارث این کلاس باشند فراهم نماید. کلاس Control نمونه بارزی از این کلاسها میباشد.

کلاس Gusge با ایجاد سه عضو Ticks و TickSize و TickBrush که به ترتیب از نوع int و Size و Brush میباشند امکان سفارشی کردن صفحه مدرج زمینه را در ساده ترین شکل ممکن فراهم میکند. Ticks تعداد درجات در زاویه 180 در جه (نیم دایره) است و TickSize اندازه طول و قطر هر نشانگر درجه را تامین میکند. بدیهیست که TickBrush نوع Brush مورد نظر برای ترسیم این درجه بندی را مشخص میکند.
کلاس Gusge با Override کردن متد OnRender عملا با یک تیر چند نشان زده و مهمتر از هر چیزی یک DrawingContext آماده دریافت میکند که چنانچه قبلا توضیح داده شد استفاده از متدهای آن برای رسم اشکال ساده گرافیکی پایه، ضروری میباشد. چنانچه در ادامه کد کاملا مشخص شده است ما از متد DrawEllipse این کلاس برای ترسیم دایره مرکزی، از متد DrawRectangle برای ترسیم خطوط مدرج (که در دو ردیف رسم میشوند) و از DrawText برای اعداد مشخص کننده بر روی نوار مدرج استفاده کرده ایم. همچنین دو متد Push   و Pop به صورت مکمل و  در داخل یک حلقه برای جابجایی (از نوع Transform) خطوط مدرج در بازه یک زاویه 180 درجه نقش کلیدی بازی میکنند .
به نظر میرسد آنچه در XAML مشاهده میکنید به اندازه کافی گویا میباشد و بدیهیست که نمونه سازی کلاس Guage در XAML انجام شده و با استفاده از تکنیک Element Binding یکی دیگر از قابلیتهای بسیار استثنایی WPF که امکان Binding دو کنترل UI به یکدیگر میباشد به نمایش گذاشته شده است.
همچنین در قسمت Resourceها مشخصات ترسیم عقربه با استفاده از دستورات Geometry String Commands ترسیم شده است. این شبه زبان بسیار ساده ولی قدرتمند با استفاده از دستورات ساده ای نظیر M و Z و F و L و A و H  و... می توانند سگمنتهای گرافیکی مورد نیاز در کل Geometry ما را که فقط یک عقربه ساده میباشد تامین نمایند. در ادامه این عقربه با استفاده از یک ماتریس و ویژگی Transform هر آنچه ما به عنوان عقزبه یک کنترل Guage نیاز داریم را انجام خواهد داد.
 

 دانلود سورس کد پروژه در ویژوال استودیو 2012

در پایان ضمن پوزش به دلیل وقفه نسبتا طولانی در بخش دوم این مطلب امیدوارم که مورد توجه همکاران محترم قرار گرفته باشد.

 

۰ نظر موافقین ۰ مخالفین ۰ ۲۷ بهمن ۹۲ ، ۱۲:۱۹
مهران حسین نیا
يكشنبه, ۲۰ بهمن ۱۳۹۲، ۱۰:۳۴ ب.ظ

ایجاد یک کنترل 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

 

۰ نظر موافقین ۰ مخالفین ۰ ۲۰ بهمن ۹۲ ، ۲۲:۳۴
مهران حسین نیا