• 在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

  • 相关阅读:
    python基础 列表推导式
    信息时代的个人知识管理探微
    quaternion 四元数
    Display Lists在内存中的形式
    有关四元数 我所理解的四元数
    ogre scene_blend 透明
    ogre RenderTexture alpha rtt透明续
    四元数
    Ogre overlay实现帧动画
    贴图消失
  • 原文地址:https://www.cnblogs.com/chen110xi/p/6757593.html
Copyright © 2020-2023  润新知