在WPF中的Canvas上实现控件的拖动、缩放

  如题,项目中需要实现使用鼠标拖动、缩放一个矩形框,WPF中没有现成的,那就自己造一个轮子:)

   造轮子前先看看Windows自带的画图工具中是怎样做的,如下图:

 

   在被拖动的矩形框四周有9个小框,可以从不同方向拖动来放大缩小矩形框,另外需要注意的是,还有一个框,就是图中虚线的矩形框,这个框,是用来拖动目标控件的;我们要做的,就是模仿画图中的做法,在自定义控件中显示10个框,然后根据鼠标所在的框来处理鼠标输入,实现拖动与放大。

    参考这篇博文继续聊WPF——Thumb控件得知,WPF中有现成的拖动控件,可以提供对应的事件(DragDelta & DragCompleted), 就用它了。

还有一个需要考虑的是,我们的这个自定义控件中有10个不同作用的Thumb控件,如何区分事件从哪个Thumb发出来的呢?这样我们才能知道用户希望的操作是拖动,还是缩放,而且缩放也要知道朝哪个方向缩放。可以使用Tag属性,但是它是Object类型的,会涉及到拆箱,所以还是自定义一个CustomThumb。

    首先,定义说明拖动方向的枚举:

 

  1. public enum DragDirection  
  2. {  
  3.     TopLeft = 1,  
  4.     TopCenter = 2,  
  5.     TopRight = 4,  
  6.     MiddleLeft = 16,  
  7.     MiddleCenter = 32,  
  8.     MiddleRight = 64,  
  9.     BottomLeft = 256,  
  10.     BottomCenter = 512,  
  11.     BottomRight = 1024,  
  12. }  
    public enum DragDirection
    {
        TopLeft = 1,
        TopCenter = 2,
        TopRight = 4,
        MiddleLeft = 16,
        MiddleCenter = 32,
        MiddleRight = 64,
        BottomLeft = 256,
        BottomCenter = 512,
        BottomRight = 1024,
    }

 

好了,有了这个枚举,就可以知道用户操作的意图了,现在自定义一个CustomThumb。

 

  1. public class CustomThumb : Thumb  
  2. {  
  3.     public DragDirection DragDirection { get; set; }  
  4. }  
    public class CustomThumb : Thumb
    {
        public DragDirection DragDirection { get; set; }
    }

这些都弄好了,现在来写自定义控件的模板:

 

 

  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  2.                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  3.                     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   
  4.                     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
  5.                     xmlns:local="clr-namespace:UICommon.Controls"  
  6.                     xmlns:Core="clr-namespace:System;assembly=mscorlib"  
  7.                     mc:Ignorable="d">  
  8.   
  9.   
  10.       
  11.     <ControlTemplate TargetType="{x:Type local:DragHelperBase}" x:Key="DrapControlHelperTemplate">  
  12.         <ControlTemplate.Resources>  
  13.             <Style TargetType="{x:Type Thumb}" x:Key="CornerThumbStyle">  
  14.                 <Setter Property="Width" Value="{Binding CornerWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>  
  15.                 <Setter Property="Height" Value="{Binding CornerWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>  
  16.                 <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>  
  17.                 <Setter Property="BorderThickness" Value="3"/>  
  18.                 <Setter Property="Background" Value="Transparent"/>  
  19.                 <Setter Property="Template">  
  20.                     <Setter.Value>  
  21.                         <ControlTemplate TargetType="{x:Type Thumb}">  
  22.                             <Border SnapsToDevicePixels="True"  
  23.                                     Width="{TemplateBinding Width}"   
  24.                                     Height="{TemplateBinding Height}"  
  25.                                     Background="{TemplateBinding Background}"   
  26.                                     BorderBrush="{TemplateBinding BorderBrush}"  
  27.                                     BorderThickness="{TemplateBinding BorderThickness}"/>  
  28.                         </ControlTemplate>  
  29.                     </Setter.Value>  
  30.                 </Setter>  
  31.             </Style>  
  32.               
  33.             <Style TargetType="{x:Type Thumb}" x:Key="AreaThumbStyle">  
  34.                 <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}"/>  
  35.                 <Setter Property="Background" Value="Transparent"/>  
  36.                 <Setter Property="Padding" Value="0"/>  
  37.                 <Setter Property="Margin" Value="0"/>  
  38.                 <Setter Property="Template">  
  39.                     <Setter.Value>  
  40.                         <ControlTemplate TargetType="{x:Type Thumb}">  
  41.                             <Rectangle Margin="0" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True"  
  42.                                        Stroke="{TemplateBinding BorderBrush}" StrokeDashArray="2.0 2.0" Stretch="Fill"  
  43.                                        StrokeThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top, Mode=OneWay}"/>  
  44.                         </ControlTemplate>  
  45.                     </Setter.Value>  
  46.                 </Setter>  
  47.             </Style>  
  48.         </ControlTemplate.Resources>  
  49.           
  50.         <Grid x:Name="PART_MainGrid">  
  51.             <Grid.ColumnDefinitions>  
  52.                 <ColumnDefinition Width="*"/>  
  53.                 <ColumnDefinition Width="*"/>  
  54.                 <ColumnDefinition Width="*"/>  
  55.             </Grid.ColumnDefinitions>  
  56.             <Grid.RowDefinitions>  
  57.                 <RowDefinition Height="*"/>  
  58.                 <RowDefinition Height="*"/>  
  59.                 <RowDefinition Height="*"/>  
  60.             </Grid.RowDefinitions>  
  61.   
  62.   
  63.             <local:CustomThumb DragDirection="MiddleCenter" Grid.RowSpan="3" Grid.ColumnSpan="3" Cursor="SizeAll" Style="{StaticResource AreaThumbStyle}"/>  
  64.               
  65.             <local:CustomThumb DragDirection="TopLeft"      Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Top"    Cursor="SizeNWSE"/>  
  66.             <local:CustomThumb DragDirection="TopCenter"    Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Top"    Cursor="SizeNS"/>  
  67.             <local:CustomThumb DragDirection="TopRight"     Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Top"    Cursor="SizeNESW"/>  
  68.               
  69.             <local:CustomThumb DragDirection="MiddleLeft"   Style="{StaticResource CornerThumbStyle}" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Center" Cursor="SizeWE"/>  
  70.             <local:CustomThumb DragDirection="MiddleRight"  Style="{StaticResource CornerThumbStyle}" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Center" Cursor="SizeWE"/>  
  71.               
  72.             <local:CustomThumb DragDirection="BottomLeft"   Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Bottom" Cursor="SizeNESW"/>  
  73.             <local:CustomThumb DragDirection="BottomCenter" Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Bottom" Cursor="SizeNS"/>  
  74.             <local:CustomThumb DragDirection="BottomRight"  Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Bottom" Cursor="SizeNWSE"/>  
  75.               
  76.         </Grid>  
  77.     </ControlTemplate>  
  78.   
  79.     <Style TargetType="{x:Type local:DragHelperBase}"  BasedOn="{StaticResource {x:Type ContentControl}}">  
  80.         <Setter Property="BorderBrush" Value="Green"/>  
  81.         <Setter Property="BorderThickness" Value="1"/>  
  82.         <Setter Property="Padding" Value="0"/>  
  83.         <Setter Property="Margin" Value="0"/>  
  84.         <Setter Property="MinHeight" Value="5"/>  
  85.         <Setter Property="MinWidth" Value="5"/>  
  86.         <Setter Property="Template" Value="{StaticResource DrapControlHelperTemplate}"/>  
  87.     </Style>  
  88.   
  89. </ResourceDictionary>  
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    xmlns:local="clr-namespace:UICommon.Controls"
                    xmlns:Core="clr-namespace:System;assembly=mscorlib"
                    mc:Ignorable="d">


    
    <ControlTemplate TargetType="{x:Type local:DragHelperBase}" x:Key="DrapControlHelperTemplate">
        <ControlTemplate.Resources>
            <Style TargetType="{x:Type Thumb}" x:Key="CornerThumbStyle">
                <Setter Property="Width" Value="{Binding CornerWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>
                <Setter Property="Height" Value="{Binding CornerWidth, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>
                <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"/>
                <Setter Property="BorderThickness" Value="3"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Thumb}">
                            <Border SnapsToDevicePixels="True"
                                    Width="{TemplateBinding Width}" 
						            Height="{TemplateBinding Height}"
						            Background="{TemplateBinding Background}" 
						            BorderBrush="{TemplateBinding BorderBrush}"
						            BorderThickness="{TemplateBinding BorderThickness}"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            
            <Style TargetType="{x:Type Thumb}" x:Key="AreaThumbStyle">
                <Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource TemplatedParent}}"/>
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Padding" Value="0"/>
                <Setter Property="Margin" Value="0"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Thumb}">
                            <Rectangle Margin="0" Fill="{TemplateBinding Background}" SnapsToDevicePixels="True"
                                       Stroke="{TemplateBinding BorderBrush}" StrokeDashArray="2.0 2.0" Stretch="Fill"
                                       StrokeThickness="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top, Mode=OneWay}"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ControlTemplate.Resources>
        
        <Grid x:Name="PART_MainGrid">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>


            <local:CustomThumb DragDirection="MiddleCenter" Grid.RowSpan="3" Grid.ColumnSpan="3" Cursor="SizeAll" Style="{StaticResource AreaThumbStyle}"/>
            
            <local:CustomThumb DragDirection="TopLeft"      Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Top"    Cursor="SizeNWSE"/>
            <local:CustomThumb DragDirection="TopCenter"    Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Top"    Cursor="SizeNS"/>
            <local:CustomThumb DragDirection="TopRight"     Style="{StaticResource CornerThumbStyle}" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Top"    Cursor="SizeNESW"/>
            
            <local:CustomThumb DragDirection="MiddleLeft"   Style="{StaticResource CornerThumbStyle}" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Center" Cursor="SizeWE"/>
            <local:CustomThumb DragDirection="MiddleRight"  Style="{StaticResource CornerThumbStyle}" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Center" Cursor="SizeWE"/>
            
            <local:CustomThumb DragDirection="BottomLeft"   Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Left"   VerticalAlignment="Bottom" Cursor="SizeNESW"/>
            <local:CustomThumb DragDirection="BottomCenter" Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Bottom" Cursor="SizeNS"/>
            <local:CustomThumb DragDirection="BottomRight"  Style="{StaticResource CornerThumbStyle}" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Right"  VerticalAlignment="Bottom" Cursor="SizeNWSE"/>
            
        </Grid>
    </ControlTemplate>

    <Style TargetType="{x:Type local:DragHelperBase}"  BasedOn="{StaticResource {x:Type ContentControl}}">
        <Setter Property="BorderBrush" Value="Green"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="0"/>
        <Setter Property="Margin" Value="0"/>
        <Setter Property="MinHeight" Value="5"/>
        <Setter Property="MinWidth" Value="5"/>
        <Setter Property="Template" Value="{StaticResource DrapControlHelperTemplate}"/>
    </Style>

</ResourceDictionary>

 

下面编写控件的构造函数,设置DefaultStyleKeyProperty,否则控件加载时将会找不到控件模板

  1. static DragHelperBase()  
  2. {  
  3.     DefaultStyleKeyProperty.OverrideMetadata(typeof(DragHelperBase),  
  4.         new FrameworkPropertyMetadata(typeof(DragHelperBase)));  
  5. }  
  6. public DragHelperBase()  
  7. {  
  8.     SetResourceReference(StyleProperty, typeof(DragHelperBase));  
  9. }  
        static DragHelperBase()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DragHelperBase),
                new FrameworkPropertyMetadata(typeof(DragHelperBase)));
        }
        public DragHelperBase()
        {
            SetResourceReference(StyleProperty, typeof(DragHelperBase));
        }

        从控件模板可以看出,10个CustomThumb都在自定义控件的视觉树中,所以我们可以使用Thumb的路由事件,接收鼠标操作的事件并进行处理:

        在重写的方法OnApplyTemplate中添加路由事件订阅:

 

  1. public sealed override void OnApplyTemplate()  
  2. {  
  3.     base.OnApplyTemplate();  
  4.   
  5.     MainGrid = GetPartFormTemplate<Grid>("PART_MainGrid");  
  6.       
  7.     AddLogicalChild(MainGrid);  
  8.   
  9.     AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta));  
  10.     AddHandler(Thumb.DragCompletedEvent, new RoutedEventHandler(OnDragCompleted));  
  11.   
  12.     Visibility = Visibility.Collapsed;  
  13. }  
        public sealed override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            MainGrid = GetPartFormTemplate<Grid>("PART_MainGrid");
            
            AddLogicalChild(MainGrid);

            AddHandler(Thumb.DragDeltaEvent, new DragDeltaEventHandler(OnDragDelta));
            AddHandler(Thumb.DragCompletedEvent, new RoutedEventHandler(OnDragCompleted));

            Visibility = Visibility.Collapsed;
        }

  可以看到在最后一句的代码中将自定义控件的Visibility属性设为Collapsed,有2个原因,1.用户没选中目标控件前,我们的拖动控件是不应该显示出来的,第二,如果在构造函数中设置这个属性,OnApplyTemplate将不会被调用。

 

  由于这是个抽象类,所以我们需要派生类提供几个必须的方法:

 

  1. protected abstract bool GetTargetIsEditable();  
  2. protected abstract Rect GetTargetActualBound();  
  3. protected abstract void SetTargetActualBound(Rect NewBound);  
  4. protected abstract void RaisenDragChangingEvent(Rect NewBound);  
  5. protected abstract void RaisenDragCompletedEvent(Rect NewBound);  
        protected abstract bool GetTargetIsEditable();
        protected abstract Rect GetTargetActualBound();
        protected abstract void SetTargetActualBound(Rect NewBound);
        protected abstract void RaisenDragChangingEvent(Rect NewBound);
        protected abstract void RaisenDragCompletedEvent(Rect NewBound);

另外,还要注册2个路由事件,方便其他对象获取目标控件的ActualBound:

 

 

  1. #region Drag Event  
  2.   
  3. public static readonly RoutedEvent DragChangingEvent  
  4.     = EventManager.RegisterRoutedEvent("DragChangingEvent", RoutingStrategy.Bubble, typeof(DragChangedEventHandler), typeof(DragHelperBase));  
  5.   
  6. public event DragChangedEventHandler DragChanging  
  7. {  
  8.     add  
  9.     {  
  10.         AddHandler(DragChangingEvent, value);  
  11.     }  
  12.     remove  
  13.     {  
  14.         RemoveHandler(DragChangingEvent, value);  
  15.     }  
  16. }  
  17.   
  18. public static readonly RoutedEvent DragCompletedEvent  
  19.             = EventManager.RegisterRoutedEvent("DragCompletedEvent", RoutingStrategy.Bubble, typeof(DragChangedEventHandler), typeof(DragHelperBase));  
  20.   
  21. public event DragChangedEventHandler DragCompleted  
  22. {  
  23.     add  
  24.     {  
  25.         AddHandler(DragCompletedEvent, value);  
  26.     }  
  27.     remove  
  28.     {  
  29.         RemoveHandler(DragCompletedEvent, value);  
  30.     }  
  31. }  
  32. #endregion  
        #region Drag Event

        public static readonly RoutedEvent DragChangingEvent
            = EventManager.RegisterRoutedEvent("DragChangingEvent", RoutingStrategy.Bubble, typeof(DragChangedEventHandler), typeof(DragHelperBase));

        public event DragChangedEventHandler DragChanging
        {
            add
            {
                AddHandler(DragChangingEvent, value);
            }
            remove
            {
                RemoveHandler(DragChangingEvent, value);
            }
        }

        public static readonly RoutedEvent DragCompletedEvent
                    = EventManager.RegisterRoutedEvent("DragCompletedEvent", RoutingStrategy.Bubble, typeof(DragChangedEventHandler), typeof(DragHelperBase));

        public event DragChangedEventHandler DragCompleted
        {
            add
            {
                AddHandler(DragCompletedEvent, value);
            }
            remove
            {
                RemoveHandler(DragCompletedEvent, value);
            }
        }
        #endregion
  1. public class DragChangedEventArgs : RoutedEventArgs  
  2. {  
  3.     public DragChangedEventArgs(RoutedEvent Event, Rect NewBound, object Target = null) : base(Event)  
  4.     {  
  5.         this.NewBound = NewBound;  
  6.         DragTargetElement = Target;  
  7.     }  
  8.     public Rect NewBound { get; private set; }  
  9.   
  10.     public object DragTargetElement { get; private set; }  
  11. }  
    public class DragChangedEventArgs : RoutedEventArgs
    {
        public DragChangedEventArgs(RoutedEvent Event, Rect NewBound, object Target = null) : base(Event)
        {
            this.NewBound = NewBound;
            DragTargetElement = Target;
        }
        public Rect NewBound { get; private set; }

        public object DragTargetElement { get; private set; }
    }
  1. public delegate void DragChangedEventHandler(object Sender, DragChangedEventArgs e);  
    public delegate void DragChangedEventHandler(object Sender, DragChangedEventArgs e);

 

 

当用户点击目标控件时,我们的拖动控件应该显示出来,而且,拖动控件的大小、位置应该跟目标控件一致:

 

  1. #region SetupVisualPropertes  
  2. protected void SetupVisualPropertes(double TargetThickness, bool IsEditable)  
  3. {  
  4.     Visibility IsCornerVisibe = IsEditable ? Visibility.Visible : Visibility.Collapsed;  
  5.   
  6.     double ActualMargin = (CornerWidth - TargetThickness) / 2.0;  
  7.     //让9个小框排布在目标边框的中线上  
  8.     MainGrid.Margin = new Thickness(0 - ActualMargin);  
  9.   
  10.     foreach (CustomThumb item in MainGrid.Children)  
  11.     {  
  12.         if (item != null)  
  13.         {  
  14.             item.BorderThickness = new Thickness(TargetThickness);  
  15.   
  16.             if (item.DragDirection == DragDirection.MiddleCenter)  
  17.             {  
  18.                 item.Margin = new Thickness(ActualMargin);  
  19.             }  
  20.             else  
  21.             {  
  22.                 item.Visibility = IsCornerVisibe;  
  23.             }  
  24.         }  
  25.     }  
  26. }  
  27. #endregion  
        #region SetupVisualPropertes
        protected void SetupVisualPropertes(double TargetThickness, bool IsEditable)
        {
            Visibility IsCornerVisibe = IsEditable ? Visibility.Visible : Visibility.Collapsed;

            double ActualMargin = (CornerWidth - TargetThickness) / 2.0;
            //让9个小框排布在目标边框的中线上
            MainGrid.Margin = new Thickness(0 - ActualMargin);

            foreach (CustomThumb item in MainGrid.Children)
            {
                if (item != null)
                {
                    item.BorderThickness = new Thickness(TargetThickness);

                    if (item.DragDirection == DragDirection.MiddleCenter)
                    {
                        item.Margin = new Thickness(ActualMargin);
                    }
                    else
                    {
                        item.Visibility = IsCornerVisibe;
                    }
                }
            }
        }
        #endregion

 如果目标控件当前不允许编辑,则不要显示四周的9个小框,只显本体区域(虚线框),指示目标控件已经选中但不可以编辑。
当用户拖动鼠标时,处理拖动事件:

 

 

  1. private void OnDragDelta(object sender, DragDeltaEventArgs e)  
  2. {  
  3.     if(!GetTargetIsEditable())  
  4.     {  
  5.         e.Handled = true;  
  6.         return;  
  7.     }  
  8.   
  9.     CustomThumb thumb = e.OriginalSource as CustomThumb;  
  10.       
  11.     if (thumb == null)  
  12.     {  
  13.         return;  
  14.     }  
  15.   
  16.     double VerticalChange = e.VerticalChange;  
  17.     double HorizontalChange = e.HorizontalChange;  
  18.   
  19.     Rect NewBound = Rect.Empty;  
  20.   
  21.     if (thumb.DragDirection == DragDirection.MiddleCenter)  
  22.     {  
  23.         NewBound = DragElement(HorizontalChange, VerticalChange);  
  24.     }  
  25.     else  
  26.     {  
  27.         NewBound = ResizeElement(thumb, HorizontalChange, VerticalChange);  
  28.     }  
  29.   
  30.     RaisenDragChangingEvent(NewBound);  
  31.     SetTargetActualBound(NewBound);  
  32.   
  33.     e.Handled = true;  
  34. }  
  35.   
  36. private void OnDragCompleted(object sender, RoutedEventArgs e)  
  37. {  
  38.     Rect NewBound = new Rect  
  39.     {  
  40.         Y = Canvas.GetTop(this),  
  41.         X = Canvas.GetLeft(this),  
  42.         Width = this.ActualWidth,  
  43.         Height = this.ActualHeight  
  44.     };  
  45.   
  46.     RaisenDragCompletedEvent(NewBound);  
  47.   
  48.     e.Handled = true;  
  49. }  
        private void OnDragDelta(object sender, DragDeltaEventArgs e)
        {
            if(!GetTargetIsEditable())
            {
                e.Handled = true;
                return;
            }

            CustomThumb thumb = e.OriginalSource as CustomThumb;
            
            if (thumb == null)
            {
                return;
            }

            double VerticalChange = e.VerticalChange;
            double HorizontalChange = e.HorizontalChange;

            Rect NewBound = Rect.Empty;

            if (thumb.DragDirection == DragDirection.MiddleCenter)
            {
                NewBound = DragElement(HorizontalChange, VerticalChange);
            }
            else
            {
                NewBound = ResizeElement(thumb, HorizontalChange, VerticalChange);
            }

            RaisenDragChangingEvent(NewBound);
            SetTargetActualBound(NewBound);

            e.Handled = true;
        }

        private void OnDragCompleted(object sender, RoutedEventArgs e)
        {
            Rect NewBound = new Rect
            {
                Y = Canvas.GetTop(this),
                X = Canvas.GetLeft(this),
                Width = this.ActualWidth,
                Height = this.ActualHeight
            };

            RaisenDragCompletedEvent(NewBound);

            e.Handled = true;
        }

下面是处理目标控件的拖动:修改目标控件的XY坐标即可

 

 

  1. private Rect DragElement(double HorizontalChange, double VerticalChange)  
  2. {  
  3.     Rect TargetActualBound = GetTargetActualBound();  
  4.   
  5.     double TopOld  = CorrectDoubleValue(TargetActualBound.Y);  
  6.     double LeftOld = CorrectDoubleValue(TargetActualBound.X);  
  7.     double TopNew  = CorrectDoubleValue(TopOld + VerticalChange);  
  8.     double LeftNew = CorrectDoubleValue(LeftOld + HorizontalChange);  
  9.   
  10.     TopNew  = CorrectNewTop(DragHelperParent, TopNew, TargetActualBound.Height);  
  11.     LeftNew = CorrectNewLeft(DragHelperParent, LeftNew, TargetActualBound.Width);  
  12.   
  13.     Canvas.SetTop(this, TopNew);  
  14.     Canvas.SetLeft(this, LeftNew);  
  15.   
  16.     return new Rect  
  17.     {  
  18.         Y = TopNew,  
  19.         X = LeftNew,  
  20.         Width = TargetActualBound.Width,  
  21.         Height = TargetActualBound.Height  
  22.     };  
  23. }  
        private Rect DragElement(double HorizontalChange, double VerticalChange)
        {
            Rect TargetActualBound = GetTargetActualBound();

            double TopOld  = CorrectDoubleValue(TargetActualBound.Y);
            double LeftOld = CorrectDoubleValue(TargetActualBound.X);
            double TopNew  = CorrectDoubleValue(TopOld + VerticalChange);
            double LeftNew = CorrectDoubleValue(LeftOld + HorizontalChange);

            TopNew  = CorrectNewTop(DragHelperParent, TopNew, TargetActualBound.Height);
            LeftNew = CorrectNewLeft(DragHelperParent, LeftNew, TargetActualBound.Width);

            Canvas.SetTop(this, TopNew);
            Canvas.SetLeft(this, LeftNew);

            return new Rect
            {
                Y = TopNew,
                X = LeftNew,
                Width = TargetActualBound.Width,
                Height = TargetActualBound.Height
            };
        }

下面是处理缩放目标控件,思考一下,其实原理就是从不同的方向放大或缩小目标控件的 Width & Height 属性,或者XY坐标,并且加上限制,不让目标控件超过父控件(在这里是Canvas)的边界:

 

 

  1. private Rect ResizeElement(CustomThumb HitedThumb, double HorizontalChange, double VerticalChange)  
  2. {  
  3.     #region Get Old Value  
  4.   
  5.     if (HitedThumb == null) return Rect.Empty;  
  6.       
  7.   
  8.     Rect TargetActualBound = GetTargetActualBound();  
  9.   
  10.     double TopOld    = CorrectDoubleValue(TargetActualBound.Y);  
  11.     double LeftOld   = CorrectDoubleValue(TargetActualBound.X);  
  12.     double WidthOld  = CorrectDoubleValue(TargetActualBound.Width);  
  13.     double HeightOld = CorrectDoubleValue(TargetActualBound.Height);  
  14.   
  15.     double TopNew    = TopOld;  
  16.     double LeftNew   = LeftOld;  
  17.     double WidthNew  = WidthOld;  
  18.     double HeightNew = HeightOld;  
  19.  
  20.     #endregion  
  21.   
  22.     if (HitedThumb.DragDirection == DragDirection.TopLeft  
  23.         || HitedThumb.DragDirection == DragDirection.MiddleLeft  
  24.         || HitedThumb.DragDirection == DragDirection.BottomLeft)  
  25.     {  
  26.         ResizeFromLeft(DragHelperParent, LeftOld, WidthOld, HorizontalChange, out LeftNew, out WidthNew);  
  27.     }  
  28.   
  29.     if (HitedThumb.DragDirection == DragDirection.TopLeft  
  30.         || HitedThumb.DragDirection == DragDirection.TopCenter  
  31.         || HitedThumb.DragDirection == DragDirection.TopRight)  
  32.     {  
  33.         ResizeFromTop(DragHelperParent, TopOld, HeightOld, VerticalChange, out TopNew, out HeightNew);  
  34.     }  
  35.   
  36.     if (HitedThumb.DragDirection == DragDirection.TopRight  
  37.         || HitedThumb.DragDirection == DragDirection.MiddleRight  
  38.         || HitedThumb.DragDirection == DragDirection.BottomRight)  
  39.     {  
  40.         ResizeFromRight(DragHelperParent, LeftOld, WidthOld, HorizontalChange, out WidthNew);  
  41.     }  
  42.   
  43.     if (HitedThumb.DragDirection == DragDirection.BottomLeft  
  44.         || HitedThumb.DragDirection == DragDirection.BottomCenter  
  45.         || HitedThumb.DragDirection == DragDirection.BottomRight)  
  46.     {  
  47.         ResizeFromBottom(DragHelperParent, TopOld, HeightOld, VerticalChange, out HeightNew);  
  48.     }  
  49.   
  50.     this.Width = WidthNew;  
  51.     this.Height = HeightNew;  
  52.     Canvas.SetTop(this, TopNew);  
  53.     Canvas.SetLeft(this, LeftNew);  
  54.   
  55.     return new Rect  
  56.     {  
  57.         X = LeftNew,  
  58.         Y = TopNew,  
  59.         Width = WidthNew,  
  60.         Height = HeightNew  
  61.     };  
  62. }  
        private Rect ResizeElement(CustomThumb HitedThumb, double HorizontalChange, double VerticalChange)
        {
            #region Get Old Value

            if (HitedThumb == null) return Rect.Empty;
            

            Rect TargetActualBound = GetTargetActualBound();

            double TopOld    = CorrectDoubleValue(TargetActualBound.Y);
            double LeftOld   = CorrectDoubleValue(TargetActualBound.X);
            double WidthOld  = CorrectDoubleValue(TargetActualBound.Width);
            double HeightOld = CorrectDoubleValue(TargetActualBound.Height);

            double TopNew    = TopOld;
            double LeftNew   = LeftOld;
            double WidthNew  = WidthOld;
            double HeightNew = HeightOld;

            #endregion

            if (HitedThumb.DragDirection == DragDirection.TopLeft
                || HitedThumb.DragDirection == DragDirection.MiddleLeft
                || HitedThumb.DragDirection == DragDirection.BottomLeft)
            {
                ResizeFromLeft(DragHelperParent, LeftOld, WidthOld, HorizontalChange, out LeftNew, out WidthNew);
            }

            if (HitedThumb.DragDirection == DragDirection.TopLeft
                || HitedThumb.DragDirection == DragDirection.TopCenter
                || HitedThumb.DragDirection == DragDirection.TopRight)
            {
                ResizeFromTop(DragHelperParent, TopOld, HeightOld, VerticalChange, out TopNew, out HeightNew);
            }

            if (HitedThumb.DragDirection == DragDirection.TopRight
                || HitedThumb.DragDirection == DragDirection.MiddleRight
                || HitedThumb.DragDirection == DragDirection.BottomRight)
            {
                ResizeFromRight(DragHelperParent, LeftOld, WidthOld, HorizontalChange, out WidthNew);
            }

            if (HitedThumb.DragDirection == DragDirection.BottomLeft
                || HitedThumb.DragDirection == DragDirection.BottomCenter
                || HitedThumb.DragDirection == DragDirection.BottomRight)
            {
                ResizeFromBottom(DragHelperParent, TopOld, HeightOld, VerticalChange, out HeightNew);
            }

            this.Width = WidthNew;
            this.Height = HeightNew;
            Canvas.SetTop(this, TopNew);
            Canvas.SetLeft(this, LeftNew);

            return new Rect
            {
                X = LeftNew,
                Y = TopNew,
                Width = WidthNew,
                Height = HeightNew
            };
        }

下面是从不同的方向修改目标控件的XY坐标或者Width & Height 属性:

 

 

  1. #region Resize Base Methods  
  2.  
  3. #region ResizeFromTop  
  4. private static void ResizeFromTop(FrameworkElement Parent, double TopOld, double HeightOld, double VerticalChange, out double TopNew, out double HeightNew)  
  5. {  
  6.     double MiniHeight = 10;  
  7.   
  8.     double top = TopOld + VerticalChange;  
  9.     TopNew = ((top + MiniHeight) > (HeightOld + TopOld)) ? HeightOld + TopOld - MiniHeight : top;  
  10.     TopNew = TopNew < 0 ? 0 : TopNew;  
  11.   
  12.     HeightNew = HeightOld + TopOld - TopNew;  
  13.   
  14.     HeightNew = CorrectNewHeight(Parent, TopNew, HeightNew);  
  15. }  
  16. #endregion  
  17.  
  18. #region ResizeFromLeft  
  19. private static void ResizeFromLeft(FrameworkElement Parent, double LeftOld, double WidthOld, double HorizontalChange, out double LeftNew, out double WidthNew)  
  20. {  
  21.     double MiniWidth = 10;  
  22.     double left = LeftOld + HorizontalChange;  
  23.   
  24.     LeftNew = ((left + MiniWidth) > (WidthOld + LeftOld)) ? WidthOld + LeftOld - MiniWidth : left;  
  25.   
  26.     LeftNew = LeftNew < 0 ? 0 : LeftNew;  
  27.   
  28.     WidthNew = WidthOld + LeftOld - LeftNew;  
  29.   
  30.     WidthNew = CorrectNewWidth(Parent, LeftNew, WidthNew);  
  31. }  
  32. #endregion  
  33.  
  34. #region ResizeFromRight  
  35. private static void ResizeFromRight(FrameworkElement Parent, double LeftOld, double WidthOld, double HorizontalChange, out double WidthNew)  
  36. {  
  37.     if (LeftOld + WidthOld + HorizontalChange < Parent.ActualWidth)  
  38.     {  
  39.         WidthNew = WidthOld + HorizontalChange;  
  40.     }  
  41.     else  
  42.     {  
  43.         WidthNew = Parent.ActualWidth - LeftOld;  
  44.     }  
  45.   
  46.     WidthNew = WidthNew < 0 ? 0 : WidthNew;  
  47. }  
  48. #endregion  
  49.  
  50. #region ResizeFromBottom  
  51. private static void ResizeFromBottom(FrameworkElement Parent, double TopOld, double HeightOld, double VerticalChange, out double HeightNew)  
  52. {  
  53.     if (TopOld + HeightOld + VerticalChange < Parent.ActualWidth)  
  54.     {  
  55.         HeightNew = HeightOld + VerticalChange;  
  56.     }  
  57.     else  
  58.     {  
  59.         HeightNew = Parent.ActualWidth - TopOld;  
  60.     }  
  61.   
  62.     HeightNew = HeightNew < 0 ? 0 : HeightNew;  
  63. }  
  64. #endregion  
  65.  
  66. #region CorrectNewTop  
  67. private static double CorrectNewTop(FrameworkElement Parent, double Top, double Height)  
  68. {  
  69.     double NewHeight = ((Top + Height) > Parent.ActualHeight) ? (Parent.ActualHeight - Height) : Top;  
  70.     return NewHeight < 0 ? 0 : NewHeight;  
  71. }  
  72. #endregion  
  73.  
  74. #region CorrectNewLeft  
  75. private static double CorrectNewLeft(FrameworkElement Parent, double Left, double Width)  
  76. {  
  77.     double NewLeft = ((Left + Width) > Parent.ActualWidth) ? (Parent.ActualWidth - Width) : Left;  
  78.   
  79.     return NewLeft < 0 ? 0 : NewLeft;  
  80. }  
  81. #endregion  
  82.  
  83. #region CorrectNewWidth  
  84. private static double CorrectNewWidth(FrameworkElement Parent, double Left, double WidthNewToCheck)  
  85. {  
  86.     double Width = ((Left + WidthNewToCheck) > Parent.ActualWidth) ? (Parent.ActualWidth - Left) : WidthNewToCheck;  
  87.   
  88.     return Width < 0 ? 0 : Width;  
  89. }  
  90. #endregion  
  91.  
  92. #region CorrectNewHeight  
  93. private static double CorrectNewHeight(FrameworkElement Parent, double Top, double HeightNewToCheck)  
  94. {  
  95.     double Height = ((Top + HeightNewToCheck) > Parent.ActualHeight) ? (Parent.ActualHeight - Top) : HeightNewToCheck;  
  96.     return Height < 0 ? 0 : Height;  
  97. }  
  98. #endregion  
  99.  
  100. #region CorrectDoubleValue  
  101. protected static double CorrectDoubleValue(double Value)  
  102. {  
  103.     return (double.IsNaN(Value) || (Value < 0.0)) ? 0 : Value;  
  104. }  
  105. #endregion  
  106.  
  107. #endregion  
        #region Resize Base Methods

        #region ResizeFromTop
        private static void ResizeFromTop(FrameworkElement Parent, double TopOld, double HeightOld, double VerticalChange, out double TopNew, out double HeightNew)
        {
            double MiniHeight = 10;

            double top = TopOld + VerticalChange;
            TopNew = ((top + MiniHeight) > (HeightOld + TopOld)) ? HeightOld + TopOld - MiniHeight : top;
            TopNew = TopNew < 0 ? 0 : TopNew;

            HeightNew = HeightOld + TopOld - TopNew;

            HeightNew = CorrectNewHeight(Parent, TopNew, HeightNew);
        }
        #endregion

        #region ResizeFromLeft
        private static void ResizeFromLeft(FrameworkElement Parent, double LeftOld, double WidthOld, double HorizontalChange, out double LeftNew, out double WidthNew)
        {
            double MiniWidth = 10;
            double left = LeftOld + HorizontalChange;

            LeftNew = ((left + MiniWidth) > (WidthOld + LeftOld)) ? WidthOld + LeftOld - MiniWidth : left;

            LeftNew = LeftNew < 0 ? 0 : LeftNew;

            WidthNew = WidthOld + LeftOld - LeftNew;

            WidthNew = CorrectNewWidth(Parent, LeftNew, WidthNew);
        }
        #endregion

        #region ResizeFromRight
        private static void ResizeFromRight(FrameworkElement Parent, double LeftOld, double WidthOld, double HorizontalChange, out double WidthNew)
        {
            if (LeftOld + WidthOld + HorizontalChange < Parent.ActualWidth)
            {
                WidthNew = WidthOld + HorizontalChange;
            }
            else
            {
                WidthNew = Parent.ActualWidth - LeftOld;
            }

            WidthNew = WidthNew < 0 ? 0 : WidthNew;
        }
        #endregion

        #region ResizeFromBottom
        private static void ResizeFromBottom(FrameworkElement Parent, double TopOld, double HeightOld, double VerticalChange, out double HeightNew)
        {
            if (TopOld + HeightOld + VerticalChange < Parent.ActualWidth)
            {
                HeightNew = HeightOld + VerticalChange;
            }
            else
            {
                HeightNew = Parent.ActualWidth - TopOld;
            }

            HeightNew = HeightNew < 0 ? 0 : HeightNew;
        }
        #endregion

        #region CorrectNewTop
        private static double CorrectNewTop(FrameworkElement Parent, double Top, double Height)
        {
            double NewHeight = ((Top + Height) > Parent.ActualHeight) ? (Parent.ActualHeight - Height) : Top;
            return NewHeight < 0 ? 0 : NewHeight;
        }
        #endregion

        #region CorrectNewLeft
        private static double CorrectNewLeft(FrameworkElement Parent, double Left, double Width)
        {
            double NewLeft = ((Left + Width) > Parent.ActualWidth) ? (Parent.ActualWidth - Width) : Left;

            return NewLeft < 0 ? 0 : NewLeft;
        }
        #endregion

        #region CorrectNewWidth
        private static double CorrectNewWidth(FrameworkElement Parent, double Left, double WidthNewToCheck)
        {
            double Width = ((Left + WidthNewToCheck) > Parent.ActualWidth) ? (Parent.ActualWidth - Left) : WidthNewToCheck;

            return Width < 0 ? 0 : Width;
        }
        #endregion

        #region CorrectNewHeight
        private static double CorrectNewHeight(FrameworkElement Parent, double Top, double HeightNewToCheck)
        {
            double Height = ((Top + HeightNewToCheck) > Parent.ActualHeight) ? (Parent.ActualHeight - Top) : HeightNewToCheck;
            return Height < 0 ? 0 : Height;
        }
        #endregion

        #region CorrectDoubleValue
        protected static double CorrectDoubleValue(double Value)
        {
            return (double.IsNaN(Value) || (Value < 0.0)) ? 0 : Value;
        }
        #endregion

        #endregion

下面是测试效果:

 



完整的测试代码可以到我的github下载

http://blog.csdn.net/jtl309/article/details/50651911

posted on 2017-04-24 16:36  chen110xi  阅读(7138)  评论(1编辑  收藏  举报