在WPF中的Canvas上实现控件的拖动、缩放
如题,项目中需要实现使用鼠标拖动、缩放一个矩形框,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);
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
在WPF中的Canvas上实现控件的拖动、缩放的更多相关文章
- asp.net中遍历界面上所有控件进行属性设置
* 使用方法: * 前台页面调用方法,重置: protected void Reset_Click(object sender, EventArgs e) { ...
- WPF中不规则窗体与WindowsFormsHost控件的兼容问题完美解决方案
首先先得瑟一下,有关WPF中不规则窗体与WindowsFormsHost控件不兼容的问题,网上给出的解决方案不能满足所有的情况,是有特定条件的,比如 WPF中不规则窗体与WebBrowser控件的兼 ...
- WPF 中动态创建和删除控件
原文:WPF 中动态创建和删除控件 动态创建控件 1.容器控件.RegisterName("Name",要注册的控件) //注册控件 2.容器控件.FindName(" ...
- 在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件(转)
引言 这两天沉迷了Google SketchUp,刚刚玩够,一时兴起,研究了一下WebBrowser. 我在<WebBrowser控件使用技巧分享>一文中曾谈到过“我现在可以通过WebBr ...
- WPF中不规则窗体与WebBrowser控件的兼容问题解决办法
原文:WPF中不规则窗体与WebBrowser控件的兼容问题解决办法 引言 这几天受委托开发一个网络电视项目,要求初步先使用内嵌网页形式实现视频播放和选单,以后再考虑将网页中的所有功能整合进桌面程序. ...
- 封装:WPF中可以绑定的BindPassWord控件
原文:封装:WPF中可以绑定的BindPassWord控件 一.目的:本身自带的PassWord不支持绑定 二.Xaml部分 <UserControl x:Class="HeBianG ...
- WPF中增加Month Calendar月历控件
XAML代码:(这里使用了codeproject.com网站上的一个Dll,你可以在这里下载它:http://www.codeproject.com/cs/miscctrl/MonthCalendar ...
- WPF中实现多选ComboBox控件
在WPF中实现带CheckBox的ComboBox控件,让ComboBox控件可以支持多选. 将ComboBox的ItemsSource属性Binding到一个Book的集合, public clas ...
- WPF中Expander的用法和控件模板详解
一.Expander的用法 在WPF中,Expander是一个很实用的复合控件,可以很方便的实现下拉菜单和导航栏等功能.先介绍简单的用法,而后分析他的控件模板. <Window.Resource ...
随机推荐
- linux 服务器安装 nginx
每次安装 nginx 都在网上找教程,这次特意记录一下安装过程. 第一步:安装依赖 一键安装依赖 yum -y install gcc zlib zlib-devel pcre-devel opens ...
- 大数据技术 - MapReduce的Combiner介绍
本章来简单介绍下 Hadoop MapReduce 中的 Combiner.Combiner 是为了聚合数据而出现的,那为什么要聚合数据呢?因为我们知道 Shuffle 过程是消耗网络IO 和 磁盘I ...
- OSFPv3的配置
实验目的 1. 掌握 OSPFv3 的配置方法 2. 掌握在帧中继环境下 OSPFv3 的配置方法 3. 掌握 OSPFv3 NSSA 的配置方法 4. 掌握外部路由汇总的配置 5. 掌握区 ...
- Farewell Party-构造
Farewell Party 思路 : 转换思路 ,有 a [ i ] 个不相等的 ,那么至少得有 n - a [ i ]个与它相等的. 但是有可能与它拥有相同数目的有很多. 但是为了能够最终 分配成 ...
- google像apple 30亿美元购买流量
google花费30亿美元像apple购买流量作为iphone默认搜索引擎.
- LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)
题目链接 参考yww的题解.本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了. 不知不觉怎么写了这么多... 另外还是有莫队做法的...(虽然可能卡不过) \(60\)分的\(O(n^ ...
- BZOJ.4650.[NOI2016]优秀的拆分(后缀数组 思路)
BZOJ 洛谷 令\(st[i]\)表示以\(i\)为开头有多少个\(AA\)这样的子串,\(ed[i]\)表示以\(i\)结尾有多少个\(AA\)这样的子串.那么\(Ans=\sum_{i=1}^{ ...
- BZOJ3565 : [SHOI2014]超能粒子炮
若$a\leq 1000$,则整个$f$数列会形成$O(a)$段公差为$a$的等差数列. 否则$a^{-1}\leq 1000$,设$ai+b=f(i)$,那么有$i=a^{-1}f(i)-ba^{- ...
- C++程序设计方法2:基本语法
初始化列表 int a[] = {1,2,3}; int a[]{1,2,3} 以上两个式子等价 int a = 3+5: int a = {3+5}; int a(3+5); int a{3+5}; ...
- 编程菜鸟的日记-初学尝试编程-寻找等长数组A与B(所含元素相同,顺序不同)相匹配的元素即a[i]=b[j]
#include <iostream> using namespace std; void matching(int a[],int b[],int N) { int i=0; while ...