如題,項目中需要實現使用鼠標拖動、縮放一個矩形框,WPF中沒有現成的,那就自己造一個輪子:)
造輪子前先看看Windows自帶的畫圖工具中是怎樣做的,如下圖:
在被拖動的矩形框四周有9個小框,可以從不同方向拖動來放大縮小矩形框,另外需要注意的是,還有一個框,就是圖中虛線的矩形框,這個框,是用來拖動目標控件的;我們要做的,就是模仿畫圖中的做法,在自定義控件中顯示10個框,然后根據鼠標所在的框來處理鼠標輸入,實現拖動與放大。
參考這篇博文繼續聊WPF——Thumb控件得知,WPF中有現成的拖動控件,可以提供對應的事件(DragDelta & DragCompleted), 就用它了。
還有一個需要考慮的是,我們的這個自定義控件中有10個不同作用的Thumb控件,如何區分事件從哪個Thumb發出來的呢?這樣我們才能知道用戶希望的操作是拖動,還是縮放,而且縮放也要知道朝哪個方向縮放。可以使用Tag屬性,但是它是Object類型的,會涉及到拆箱,所以還是自定義一個CustomThumb。
首先,定義說明拖動方向的枚舉:
- public enum DragDirection
- {
- TopLeft = 1,
- TopCenter = 2,
- TopRight = 4,
- MiddleLeft = 16,
- MiddleCenter = 32,
- MiddleRight = 64,
- BottomLeft = 256,
- BottomCenter = 512,
- BottomRight = 1024,
- }
public enum DragDirection { TopLeft = 1, TopCenter = 2, TopRight = 4, MiddleLeft = 16, MiddleCenter = 32, MiddleRight = 64, BottomLeft = 256, BottomCenter = 512, BottomRight = 1024, }
好了,有了這個枚舉,就可以知道用戶操作的意圖了,現在自定義一個CustomThumb。
- public class CustomThumb : Thumb
- {
- public DragDirection DragDirection { get; set; }
- }
public class CustomThumb : Thumb { public DragDirection DragDirection { get; set; } }
這些都弄好了,現在來寫自定義控件的模板:
- <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>
<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,否則控件加載時將會找不到控件模板
- static DragHelperBase()
- {
- DefaultStyleKeyProperty.OverrideMetadata(typeof(DragHelperBase),
- new FrameworkPropertyMetadata(typeof(DragHelperBase)));
- }
- public DragHelperBase()
- {
- SetResourceReference(StyleProperty, typeof(DragHelperBase));
- }
static DragHelperBase() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DragHelperBase), new FrameworkPropertyMetadata(typeof(DragHelperBase))); } public DragHelperBase() { SetResourceReference(StyleProperty, typeof(DragHelperBase)); }
從控件模板可以看出,10個CustomThumb都在自定義控件的視覺樹中,所以我們可以使用Thumb的路由事件,接收鼠標操作的事件並進行處理:
在重寫的方法OnApplyTemplate中添加路由事件訂閱:
- 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;
- }
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將不會被調用。
由於這是個抽象類,所以我們需要派生類提供幾個必須的方法:
- 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);
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:
- #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
#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
- 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; }
- }
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; } }
public delegate void DragChangedEventHandler(object Sender, DragChangedEventArgs e);
當用戶點擊目標控件時,我們的拖動控件應該顯示出來,而且,拖動控件的大小、位置應該跟目標控件一致:
- #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
#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個小框,只顯本體區域(虛線框),指示目標控件已經選中但不可以編輯。
當用戶拖動鼠標時,處理拖動事件:
- 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;
- }
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坐標即可
- 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
- };
- }
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)的邊界:
- 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
- };
- }
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 屬性:
- #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
#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
下面是測試效果:
http://blog.csdn.net/jtl309/article/details/50651911