کانواس با قابلیت اسکرول و خطکشی در WPF

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


نخست به سراغ ایجاد یک کانواس با خط‌کشی میرویم مانند شکل زیر:

یک برنامه جدید از نوع WPF ایجادکنید(می‌توانید نام آن را GridCanvas بگذارید). درون آن یک کلاس جدید بسازید با نام GrdCanvas(این کلاس از Canvas ارث می‌برد)، در بخش using اطلاعات زیر را قراردهید(اگر موجود نباشد)

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

جلوی نام کلاس برای ارث‌بری بنویسید :Canvas سپس درون آن کد زیر را قرار دهید:

#region Properties
        public static readonly DependencyProperty GridShowProperty =
            DependencyProperty.Register("GridShow",
            typeof(bool),
            typeof(GrdCanvas),
            new FrameworkPropertyMetadata(false, GridShowPropertyChanged));

        public bool GridShow
        {
            get { return (bool)GetValue(GridShowProperty); }
            set { SetValue(GridShowProperty, value); }
        }
        private static void GridShowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            GrdCanvas _grdCanvas = d as GrdCanvas;
            if (_grdCanvas == null) return;
            if ((bool)e.NewValue)
            {
                _grdCanvas.OnGridShow();
            }
            else
                _grdCanvas.OnGridHide();
        }
        private void OnGridShow()
        {
            float _size = 25;
            DrawingBrush _drawingBrush = new DrawingBrush();
            Rect _rect = new Rect(0, 0, _size, _size);
            Geometry _geometry = new RectangleGeometry(_rect);
            Pen _pen = new Pen(Brushes.Gray, 0.1);
            _pen.DashStyle = DashStyles.Solid;
            GeometryDrawing _geometryDrawing = new GeometryDrawing();
            _geometryDrawing.Brush = Brushes.White;
            _geometryDrawing.Geometry = _geometry;
            _geometryDrawing.Pen = _pen;
            _drawingBrush.TileMode = TileMode.Tile;
            _drawingBrush.Viewport = _rect;
            _drawingBrush.ViewportUnits = BrushMappingMode.Absolute;
            _drawingBrush.Drawing = _geometryDrawing;
            this.Background = _drawingBrush;
        }
        private void OnGridHide()
        {
            this.Background = Brushes.White;
        }
        #endregion

خط 2 یک ویژگی‌وابسته(DependencyProperty) به نام GridShow تعریف‌می‌کند که این امکان را فراهم‌می‌کند تا با تنظیم آن گرید نمایش داده‌شود یا نمایش داده‌نشود، مقدار پیش فرض آن false یعنی نمایش داده‌نشود است و با تغییر مقدار آن تابع GridShowPropertyChanged برای عنصر موردنظر فعال می‌گردد و خود این تابع با توجه به مقدار GridShow(مقدار جدید) اقدام به نمایش‌ یا عدم نمایش می‌کند.

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

حتما عکس زمینه ویندوز را تغییرداده‌اید و می‌دانید اگر تصویر کوچک باشد می‌توان آن را در طول و عرض صفحه گسترش داد و تکرار کرد(Tile). به عکس زیر برای فهم بیشتر دقت‌کنید.

همین تکنیک را برروی پس‌زمینه کانواس انجام می‌دهیم. در تابع OnGridShow نخست یک مربع ایجادکرده و سپس از آن برای رنگ‌کردن استفاده می‌کنیم.  _drawingBrush كار رنگ‌آميزی را انجام‌می‌دهد.

حال برنامه را ذخیره و کامپایل نمایید. در پنجره MainWindow نخست خط زیر را به ویندوز اضافه کنید:

xmlns:local="clr-namespace:GridCanvas" x:Class="GridCanvas.MainWindow"

و سپس به درون دستور Grid کد زیر را قراردهید:

<local:GrdCanvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" GridShow="True">
            <Button Content="Button" Canvas.Left="399" Canvas.Top="139" Width="239" Height="247"/>
        </local:GrdCanvas>

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

بروزرسانی:يكشنبه 23 مرداد ماه 1401

اگر بخواهیم کانواس‌مان قابلیت اسکرول داشته باشد لازم است نخت ابعاد کانواس را بزرگتر از ابعاد ویندوزمان کنیم، کد زیر را با کد قرار داده‌شده میان دستور Grid جایگزین کنید:

 

<ScrollViewer
            VerticalScrollBarVisibility="Auto"
            HorizontalScrollBarVisibility="Auto"
        >
        <local:GrdCanvas GridShow="True" Width="800" Height="500">
            <Button Content="Button" Canvas.Left="399" Canvas.Top="139" Width="239" Height="247"/>
        </local:GrdCanvas>
        </ScrollViewer>

با کمی دقت می‌بینید که به کانواس ابعادی بزرگتر از ویندوزمان دارد و خود کانواس نیز درون یک اسکرول‌ویوور(ScrollViewer) قراردارد، این باعث می‌گردد تا کانواس قابلیت اسکرول داشته‌باشد.

برنامه را ذخیره و اجرا نمایید.

>

شاید بخواهید ابعاد کانواس‌تان براساس چیدمان اشیای داخل آن متغیر باشد! یعنی اگر هیچ شی‌ی درون آن نباشد، آنگاه کانواس قابلیت اسکرول نداشته باشد و اگر شی‌ی درون آن قرار گرفت به‌طوریکه بخشی یا تمام آن مخفی بود بتوان با اسکرول کردن آن را مشاهده‌کرد.

کد زمل زیر را با قبلی جایگزین کنید:

<ScrollViewer
            VerticalScrollBarVisibility="Auto"
            HorizontalScrollBarVisibility="Auto">
                <local:GrdCanvas GridShow="True">
                    <Button Content="Button" Canvas.Left="399" Canvas.Top="139" Width="239" Height="247"/>
                </local:GrdCanvas>
        </ScrollViewer>

و در کلاس کانواس نیز یک تغییر کوچک انجام می‌دهیم و کد زیر را به کلاس اضافه‌می‌کنیم:

protected override Size MeasureOverride(Size constraint)
        {
            Size _size = new Size();
            foreach (UIElement _element in this.InternalChildren)
            {
                double _left = Canvas.GetLeft(_element);
                double _top = Canvas.GetTop(_element);
                _left = double.IsNaN(_left) ? 0 : _left;
                _top = double.IsNaN(_top) ? 0 : _top;
                _element.Measure(constraint);
                Size _desiredSize = _element.DesiredSize;
                if (!double.IsNaN(_desiredSize.Width) && !double.IsNaN(_desiredSize.Height))
                {
                    _size.Width = System.Math.Max(_size.Width, _left + _desiredSize.Width);
                    _size.Height = System.Math.Max(_size.Height, _top + _desiredSize.Height);
                }
            }
            return _size;
        }

این تابع تمامی المان‌های داخل کانواس را بررسی می‌کند و ابعاد کانواس را با بیشترین اندازه مورد نیاز(DesiredSize) المان‌ها جایگزین می‌کند، توجه شود هر المان از جنس UIElement دارای یک تابع به نام Measure می‌باشد که این امکان را فراهم می‌کند تا اندازه المان را براساس محدودیت‌های والد(مثلا کانواس یا گرید و ... بخصوص در گرید بدلیل چینش المان‌ها توسط زمل کارکرد تابع بیشتر نمایان می‌شود) محاسبه کرده و بازگرداند. در آینده یک مطلب درباره مقادیر Width و Height و تفاوت میان ActualSize و DesireSize و RenderSize خواهیم‌داشت.

برنامه را ذخیره و اجرا کنید.


فایلهای مطلب

کپی
لینک اشتراک گذاری

  • 410
  • 0