کنترل با امکان جابجایی توسط موس در WPF

برنامه‌نویسان بخصوص کسانی که می‌خواهند برنامه‌هایی مشابه طراحی فرم( Form Builder) و یا ریپورت(Report Builder) در ویژوال‌استودیو یا نرم‌افزارهای مشابه بنویسند لازم دارند که وقتی یک شی یا یک کنترل توسط کاربر به برنامه اضافه می‌شود، کاربر بتواند آن را با استفاده از موس حرکت دهد و مکان آن را با موس مشخص کند. این کار در محیط فرم‌بیس(FormBase) با بکارگیری ایونت‌(Event)های MouseDown، MouseUp و MouseMove برای هر کنترل امکان‌پذیر است. اما در حالت فرم‌بیس شی‌ها و کنترل‌ها همگی درون یک فرم یا یک پانل(Panel) قرار دارند و حرکت درون هردو (فرم یا پانل) با استفاده از تنظیم دو خاصیت(Property) لفت(Left) و تاپ(Top) برای آن کنترل یا شی امکان‌پذیر است. اما در WPF ما پانل‌های مختلفی چون Canvas، Grid و ... داریم که تغییر مکان کنترل‌ها در آن‌ها روش‌های متفاوتی دارند. مثلا در پانل Canvas برای تنظیم محل یک شی باید با استفاده از دستورهای SetLeft و SetTop و بکارگیری خاصیت وابسته(Attached Property) Left و Top محل شی را درون Canvas تغییر داد اما برای Grid لازم است خاصیت Margin را برای آن شی یا کنترل تغییردهیم. در اینجا هدف آموزش نحوه تغییرمکان کنترل در Canvas می‌باشد.


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

در این روش لازم نیست اصلا برای ایونت‌های تک‌تک کنترل‌ها برنامه نوشت فقط کافی است یک کنترل پرایماتیو(Primitive) به نام Thumb که قابلیت درگ توسط موس را دارد درون تمپلیت کنترل مورد نظر قرار داد. از خواص آن اینکه به شما امکان می‌دهد تا از آن(Thumb) درون کنترل‌های دیگرهم استفاده‌کنید. بدلیل داشتن ایونت‌های DragStarted، DragCompleted و DragDelta به برنامه‌نویس امکان تغییردادن مشخصات مکانی و اندازه و ... کنترلی مورد نظر را فراهم‌می‌کند.

نخست برنامه ویژوال‌استودیو را بازکرده و یک برنامه از نوع WPF ایجادکنید(WPF Application). اگر شما به زبان C# آشنا نمی‌باشید از نرم‌افزارهای مبدل استفاده‌نمایید(چرا که در اینجا به این زبان شرح داده می‌شود اما در فایلهای‌ ضمیمه زبان بیسیک آن هم موجوداست).

یک کلاس جدید ایجاد و نام آن را MoveControl بگذارید. بخش using را با مقادیر زیر تغییر دهید:

using System.Windows.Controls ;
using System.Windows.Controls.Primitives ;

داخل کلاس را با کد زیر جایگزین کنید:

    class  MoveControl : Thumb
	    {
	        public  MoveControl()
	        {
	            DragDelta += new DragDeltaEventHandler(this.MoveControlDragDelta );
	        }
	        private void MoveControlDragDelta(object sender, DragDeltaEventArgs e)
	        {
	            Control _control = this.DataContext  as Control;
	            if  (_control != null )
	            {
	                double  left = Canvas.GetLeft (_control);
	                double  top = Canvas.GetTop (_control);
	                Canvas.SetLeft (_control, left + e.HorizontalChange );
	                Canvas.SetTop (_control, top + e.VerticalChange );
	            }
	        }
	    }
  • 1 ) یک کنترل جدید از نوع Thumb تعریف می‌کند.
  • 5 ) ایونت DragDelta(مربوط به کنترل Thumb می‌باشد) را معرفی می‌کند.
  • 9 ) وقتی کد زمل را برای کنترل(در اینجا منظور از کنترل آن شیی است که می‌خواهیم جابجا کنیم نه MoveControl) توضیح دهیم کاربرد این خط مشخص خواهدشد.
  • .. ) خطوط 12 تا 15 مکان کنترل را تغییر می‌دهد.

حال در MainWindow.xaml دستور Grid را با Canvas جایگزین کرده و نیم‌اسپیس(namespace) برنامه خودتان را به آن اضافه کنید مثلا برای برنامه ساخته شده ضمیمه به این صورت است

xmlns:p="clr-namespace:WpfApplication1"

درست پیش از دستور Canvas کد زیر را اضافه کنید.

    <Window.Resources>
        <ControlTemplate x:Key="MoveControlTemplate" TargetType="{x:Type p:MoveControl}">
            <Grid>
                <Rectangle Stroke="Gray" Fill="Transparent"/>
            <⁄Grid>
        <⁄ControlTemplate>

        <ControlTemplate x:Key="MovableControlTemplate" TargetType="{x:Type ContentControl}">
            <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                <p:MoveControl Template="{StaticResource MoveControlTemplate}" Cursor="SizeAll" ⁄>
                <ContentPresenter Content="{TemplateBinding ContentControl.Content}" ⁄>
            <⁄Grid>
        <⁄ControlTemplate>
    <⁄Window.Resources>
  • .. ) خط 2 تا 6 یک تمپلیت باکس چهارگوش برای کنترل MoveControl تعریف می‌کند. این چهارگوش(مستطیل) دارای رنگ زمینه شفاف می‌باشد.
  • 8 ) آغاز تعریف یک تپلیت برای یک کنترل از نوع کانتنت‌کنترل(ContentControl).
  • 9 ) وقتی یک کنترل جدید از نوع کانتنت‌کنترل در پنجره ایجاد شود چون می‌خواهیم توسط کنترل MoveControl آن را حرکت دهیم باید به گونه‌ای کانتنت‌کنترل را به MoveControl معرفی کنیم این کار توسط DataContext امکان‌پذیراست. به خط شماره 9 در کد بالا رجوع شود.
  • 10) کنترل MoveControl را به کانتنت‌کنترل‌مان اضافه می‌کنیم.
  • 11) چون داریم تمپلیت نمایش کانتنت‌کنترل را تغییر می‌دهیم با این دستور به تمپلیت جدید می‌گوییم همان محتوای اصلی را نمایش بده(اگر این خط برداشته شود درواقع یک کانتنت‌کنترل بدون شکل و محتوا خواهیم‌داشت.

حال درون پانل Canvas کد زیر را اضافه‌کنید:

<ContentControl Width="50" 
                        Height="50" 
                        Canvas.Top="0"
                        Canvas.Left="0"
                        Template="{StaticResource MovableControlTemplate}">
            <Ellipse Stretch="Fill" Fill="Red" IsHitTestVisible="False"/>
        </ContentControl>

کد فوق یک کانتنت‌کنترل با محتوای درونی یک بیضی(البته اینجا چون عرض و ارتفاع یکسان تعریف شده یک دایره نمایش داده می‌شود) با تمپلیت MovableControlTemplate که در بالا تعریف شده، را نمایش می‌دهد.

برنامه را اجرا و کنترل قرمز رنگ را با موس حرکت دهید.

اگر بخواهیم با خارج شدن موس از کانتنت‌کنترل‌مان کادر خاکستری رنگ حذف شود کافی است محتویات Window.Resource را به مقدار زیر تغیر دهیم:

<Window.Resources>
        <ControlTemplate x:Key="MoveControlTemplate" TargetType="{x:Type p:MoveControl}">
            <Grid>
                <Rectangle Stroke="{TemplateBinding BorderBrush}" Fill="Transparent"/>
            </Grid>
        </ControlTemplate>

        <ControlTemplate x:Key="MovableControlTemplate2" TargetType="{x:Type ContentControl}">
            <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                <p:MoveControl x:Name="MyMoveControl" Template="{StaticResource MoveControlTemplate}" Cursor="SizeAll" BorderBrush="Gray"/>
                <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="false">
                    <Setter TargetName="MyMoveControl" Property="BorderBrush" Value="Transparent"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Window.Resources>
  • 4 ) رنگ اضلاع چهارگوش را به پراپرتی BorderBrush که برای Thumb می‌باشد مرتبط کردیم
  • 10) یک نام به MoveControl اختصاص دادیم(MyMoveControl) و رنگ خاکستری برای BorderBrush تنظیم کردیم.
  • .. ) خط 13 تا 17 یک تریگر(Trigger) برای ایونت MouseOver نوشته‌ایم تا در صورتی که موس از روی کنترل خارج شد مقدار BorderBrush آن حالت شفاف پیدا کند(در صورت وارد شدن موس به کنترل دوباره مقدار BorderBrush مجدد خاکستری خواهد شد).

برنامه را اجرا و با موس به روی کنترل بروید.

هر دو کنترل درون فایل ضمیمه موجود است.

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


فایلهای مطلب

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

  • 1587
  • 0