内容简介:新建一个wpf项目,4.5.2吧
新建一个wpf项目,4.5.2吧
画个圆
用ContentControl包裹一层
给ContentControl加一个默认模板
<Canvas> <Canvas.Resources> <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/> </ControlTemplate> </Canvas.Resources> <ContentControl Canvas.Top="100" Canvas.Left="100" Width="100" Height="100" Template="{StaticResource DesignerItemTemplate}"> <Ellipse Fill="#FF42C742" /> </ContentControl> </Canvas>
让它可以移动,暂不考虑美观,先实现特性
public class MoveThumb : Thumb { public MoveThumb() { DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta); } private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e) { Control item = this.DataContext as Control; if (item != null) { double left = Canvas.GetLeft(item); double top = Canvas.GetTop(item); Canvas.SetLeft(item, left + e.HorizontalChange); Canvas.SetTop(item, top + e.VerticalChange); } } }
继承一个thumb,拖动时候,设置自己在canvas中的位置。
然后把thumb包裹要显示的内容,并且把内容设置不可命中测试
<Canvas> <Canvas.Resources> <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <Grid> <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Cursor="SizeAll"/> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" IsHitTestVisible="False"/> </Grid> </ControlTemplate> </Canvas.Resources> <ContentControl Canvas.Top="100" Canvas.Left="100" Width="100" Height="100" Template="{StaticResource DesignerItemTemplate}"> <Ellipse Fill="#FF42C742" /> </ContentControl> </Canvas>
thumb就像一个可以接受drag的矩形,更改透明外观
<Canvas.Resources> <ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type local:MoveThumb}"> <Rectangle Fill="Transparent"/> </ControlTemplate> <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <Grid> <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" IsHitTestVisible="False"/> </Grid> </ControlTemplate> </Canvas.Resources>
调整大小
8个thumb,4个角落矩形同时调整宽高,四个边矩形调整宽高
public class ResizeThumb : Thumb { public ResizeThumb() { DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta); } private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e) { Control item = this.DataContext as Control; if (item != null) { double deltaVertical, deltaHorizontal; switch (VerticalAlignment) { case System.Windows.VerticalAlignment.Bottom: deltaVertical = Math.Min(-e.VerticalChange, item.ActualHeight - item.MinHeight); item.Height -= deltaVertical; break; case System.Windows.VerticalAlignment.Top: deltaVertical = Math.Min(e.VerticalChange, item.ActualHeight - item.MinHeight); Canvas.SetTop(item, Canvas.GetTop(item) + deltaVertical); item.Height -= deltaVertical; break; default: break; } switch (HorizontalAlignment) { case System.Windows.HorizontalAlignment.Left: deltaHorizontal = Math.Min(e.HorizontalChange, item.ActualWidth - item.MinWidth); Canvas.SetLeft(item, Canvas.GetLeft(item) + deltaHorizontal); item.Width -= deltaHorizontal; break; case System.Windows.HorizontalAlignment.Right: deltaHorizontal = Math.Min(-e.HorizontalChange, item.ActualWidth - item.MinWidth); item.Width -= deltaHorizontal; break; default: break; } } e.Handled = true; } }
设置外观
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="Control"> <Grid> <local:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/> <local:ResizeThumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0" VerticalAlignment="Stretch" HorizontalAlignment="Left"/> <local:ResizeThumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0" VerticalAlignment="Stretch" HorizontalAlignment="Right"/> <local:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/> <local:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0" VerticalAlignment="Top" HorizontalAlignment="Left"/> <local:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0" VerticalAlignment="Top" HorizontalAlignment="Right"/> <local:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6" VerticalAlignment="Bottom" HorizontalAlignment="Left"/> <local:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6" VerticalAlignment="Bottom" HorizontalAlignment="Right"/> </Grid> </ControlTemplate> <ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type local:MoveThumb}"> <Rectangle Fill="Transparent"/> </ControlTemplate> <ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/> <Control Template="{StaticResource ResizeDecoratorTemplate}"/> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" IsHitTestVisible="False"/> </Grid> </ControlTemplate> </Canvas.Resources>
注意
增加旋转,目前放在4个拐角,按下,移动,实现旋转
4个四分之三的 圆,放在拐角,行为如下,下面代码还具有学习价值,用到的向量知识,求解的旋转了多少度
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; namespace DiagramTool { public class RotateThumb : Thumb { private Point centerPoint; private Vector startVector; private double initialAngle; private Canvas designerCanvas; private ContentControl designerItem; private RotateTransform rotateTransform; public RotateThumb() { DragDelta += new DragDeltaEventHandler(this.RotateThumb_DragDelta); DragStarted += new DragStartedEventHandler(this.RotateThumb_DragStarted); } private void RotateThumb_DragStarted(object sender, DragStartedEventArgs e) { this.designerItem = DataContext as ContentControl; if (this.designerItem != null) { this.designerCanvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas; if (this.designerCanvas != null) { this.centerPoint = this.designerItem.TranslatePoint( new Point(this.designerItem.Width * this.designerItem.RenderTransformOrigin.X, this.designerItem.Height * this.designerItem.RenderTransformOrigin.Y), this.designerCanvas); Point startPoint = Mouse.GetPosition(this.designerCanvas); this.startVector = Point.Subtract(startPoint, this.centerPoint); this.rotateTransform = this.designerItem.RenderTransform as RotateTransform; if (this.rotateTransform == null) { this.designerItem.RenderTransform = new RotateTransform(0); this.initialAngle = 0; } else { this.initialAngle = this.rotateTransform.Angle; } } } } private void RotateThumb_DragDelta(object sender, DragDeltaEventArgs e) { if (this.designerItem != null && this.designerCanvas != null) { Point currentPoint = Mouse.GetPosition(this.designerCanvas); Vector deltaVector = Point.Subtract(currentPoint, this.centerPoint); double angle = Vector.AngleBetween(this.startVector, deltaVector); RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform; rotateTransform.Angle = this.initialAngle + Math.Round(angle, 0); this.designerItem.InvalidateMeasure(); } } } }
提供外观,上面给RotateThumb的,四分之三的圆,下面是一个整合4个拐角的一个control的样式
<Style TargetType="{x:Type s:RotateThumb}"> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/> <Setter Property="Cursor" Value="Hand"/> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type s:RotateThumb}"> <Grid Width="30" Height="30"> <Path Fill="red" Stretch="Fill" Data="M 50,100 A 50,50 0 1 1 100,50 H 50 V 100"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <ControlTemplate x:Key="RotateDecoratorTemplate" TargetType="{x:Type Control}"> <Grid> <s:RotateThumb Margin="-18,-18,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/> <s:RotateThumb Margin="0,-18,-18,0" VerticalAlignment="Top" HorizontalAlignment="Right"> <s:RotateThumb.RenderTransform> <RotateTransform Angle="90" /> </s:RotateThumb.RenderTransform> </s:RotateThumb> <s:RotateThumb Margin="0,0,-18,-18" VerticalAlignment="Bottom" HorizontalAlignment="Right"> <s:RotateThumb.RenderTransform> <RotateTransform Angle="180" /> </s:RotateThumb.RenderTransform> </s:RotateThumb> <s:RotateThumb Margin="-18,0,0,-18" VerticalAlignment="Bottom" HorizontalAlignment="Left"> <s:RotateThumb.RenderTransform> <RotateTransform Angle="270" /> </s:RotateThumb.RenderTransform> </s:RotateThumb> </Grid> </ControlTemplate>
没啥好解释的。
然后修改ContentControl的模板,加上旋转层,然后设置RenderTransformOrigin
<ControlTemplate x:Key="DesignerItemTemplate" TargetType="ContentControl"> <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <Control Name="RotateDecorator" Template="{StaticResource RotateDecoratorTemplate}" /> <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/> <Control Template="{StaticResource ResizeDecoratorTemplate}"/> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" IsHitTestVisible="False"/> </Grid> </ControlTemplate> </Canvas.Resources> <ContentControl Canvas.Top="100" Canvas.Left="100" RenderTransformOrigin="0.5,0.5" Width="100" Height="100" Template="{StaticResource DesignerItemTemplate}"> <Ellipse Fill="#FF42C742" />
效果如下:
以上版本叫做草稿版本,因为代码都冗余在一起,此时拓展第二个元素不方便,接下来重构一下。
基于Adorner的方式实现该效果,新建DiagramTool2的wpf项目
我们每个可以拖动调整的元素,叫DesignerItem
我们添加装饰叫 DesignerItemDecorator
Decorator 英文就是叫装饰器的意思
我们可以通过AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(某个控件);拿到 某个控件的 装饰层,从而在顶部加东西,也就是装饰。
我们迁移上一个MoveThumb控件,然后新增Control类型的DesignerItemDecorator控件
这一步,我们可以可以获得ContentControl和外层的Canvas传递给 Adorner进行控制
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Controls; using System.Windows.Documents; using System.Windows; using System.Windows.Media; namespace DiagramTool2 { public class DesignerItemDecorator : Control { private Adorner adorner; public bool ShowDecorator { get { return (bool)GetValue(ShowDecoratorProperty); } set { SetValue(ShowDecoratorProperty, value); } } public static readonly DependencyProperty ShowDecoratorProperty = DependencyProperty.Register("ShowDecorator", typeof(bool), typeof(DesignerItemDecorator), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(ShowDecoratorProperty_Changed))); public DesignerItemDecorator() { Unloaded += new RoutedEventHandler(this.DesignerItemDecorator_Unloaded); } private void HideAdorner() { if (this.adorner != null) { this.adorner.Visibility = Visibility.Hidden; } } private void ShowAdorner() { if (this.adorner == null) { AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this); if (adornerLayer != null) { ContentControl designerItem = this.DataContext as ContentControl; //添加装饰器 adornerLayer.Add(this.adorner); if (this.ShowDecorator) { this.adorner.Visibility = Visibility.Visible; } else { this.adorner.Visibility = Visibility.Hidden; } } } else { this.adorner.Visibility = Visibility.Visible; } } private void DesignerItemDecorator_Unloaded(object sender, RoutedEventArgs e) { if (this.adorner != null) { AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this); if (adornerLayer != null) { adornerLayer.Remove(this.adorner); } this.adorner = null; } } private static void ShowDecoratorProperty_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { DesignerItemDecorator decorator = (DesignerItemDecorator)d; bool showDecorator = (bool)e.NewValue; if (showDecorator) { decorator.ShowAdorner(); } else { decorator.HideAdorner(); } } } }
这里加了一个ShowDecorator属性,用来控制显示和隐藏 装饰的东西,也就是最外层的
在unloaded从 顶部移除装饰层。
接下来我们在App.xaml 全局增加样式
<Application.Resources> <ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type s:MoveThumb}"> <Rectangle Fill="Transparent"/> </ControlTemplate> <Style x:Key="DesignerItemStyle" TargetType="ContentControl"> <Setter Property="MinHeight" Value="50"/> <Setter Property="MinWidth" Value="50"/> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/> <Setter Property="SnapsToDevicePixels" Value="true"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ContentControl"> <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"> <s:MoveThumb Cursor="SizeAll" Template="{StaticResource MoveThumbTemplate}" /> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" Margin="{TemplateBinding Padding}"/> <s:DesignerItemDecorator x:Name="ItemDecorator"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </Application.Resources>
我们的行为作用在 ContentControl的,在MainWindow增加一个控件
<Window x:Class="DiagramTool2.MainWindow" 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:DiagramTool2" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Canvas> <ContentControl Width="100" Height="100" Padding="1" Canvas.Left="110" Canvas.Top="100" Style="{StaticResource DesignerItemStyle}"> <Ellipse IsHitTestVisible="False" > <Ellipse.Fill> <RadialGradientBrush Center="0.2, 0.2" GradientOrigin="0.2, 0.2" RadiusX="0.8" RadiusY="0.8"> <GradientStop Color="LightBlue" Offset="0"/> <GradientStop Color="#FF33CF57" Offset="0.9"/> <GradientStop Color="#FF31A067" Offset="0.437"/> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> </ContentControl> </Canvas> </Window>
运行项目,此时可以拖动的,
我们加的装饰层,其实也在上面,只不过没内容而已
接下来写Adorner,因为顶部有个AdornerLayer专门放这种类型控件的
添加一个上篇文章的测试adorner
public class HasUpdateNotify : Adorner { private string notifyNumber = "5"; public string NotifyNumber { get { return notifyNumber; } set { notifyNumber = value; } } public HasUpdateNotify(UIElement adornedElement, string nb) : base(adornedElement) { this.NotifyNumber = nb; } protected override void OnRender(DrawingContext drawingContext) { Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize); SolidColorBrush renderBrush = new SolidColorBrush(Colors.Red); renderBrush.Opacity = 1.0; Pen renderPen = new Pen(new SolidColorBrush(Colors.Red), 1); double renderRadius = 10.0; Point tr = adornedElementRect.TopRight; tr.Offset(-5, 9); drawingContext.DrawEllipse(renderBrush, renderPen, tr, renderRadius, renderRadius); if (!string.IsNullOrEmpty(notifyNumber)) { tr.Offset(-3, -7); FormattedText numberTxt = new FormattedText(notifyNumber, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, new Typeface(System.Windows.SystemFonts.MessageFontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), 12, System.Windows.Media.Brushes.White); drawingContext.DrawText(numberTxt, tr); } }
然后在这里加上装饰
当然这不会显示,因为上方的ShowDecorator是false
我们修改DesignerItem的模板
然后运行效果如下
此时我们只要实现 resize和rotate的adorner,加在这里就行了。
当然,上面只是渲染效果,
真正是添加元素,并且具有响应事件,很明显当前需求是这样的一个adorner
一个基本的Adorner代码模板
public class ResizeAdorner : Adorner { VisualCollection visuals; public ResizeAdorner(UIElement adorned) : base(adorned) { visuals = new VisualCollection(this); } protected override Size ArrangeOverride(Size finalSize) { return finalSize; } protected override Visual GetVisualChild(int index) { return visuals[index]; } protected override int VisualChildrenCount { get { return visuals.Count; } } }
经过修改:我们先实现Size的,size是4个角落+4条方向的thumb线
索性封装一个控件叫 SizeChrome,然后每个thumb执行的特殊性,原生thumb满足不了,继承写个ResizeThumb
SizeChrome代码如下,只是提供一个容器用的,可以理解容器
using System.Windows; using System.Windows.Controls; namespace DiagramTool2 { public class SizeChrome : Control { static SizeChrome() { FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(SizeChrome), new FrameworkPropertyMetadata(typeof(SizeChrome))); } } }
SizeAdorner也很简单
using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; namespace DiagramTool2 { public class SizeAdorner : Adorner { private SizeChrome chrome; private VisualCollection visuals; private ContentControl designerItem; protected override int VisualChildrenCount { get { return this.visuals.Count; } } public SizeAdorner(ContentControl designerItem) : base(designerItem) { this.SnapsToDevicePixels = true; this.designerItem = designerItem; this.chrome = new SizeChrome(); this.chrome.DataContext = designerItem; this.visuals = new VisualCollection(this); this.visuals.Add(this.chrome); } protected override Visual GetVisualChild(int index) { return this.visuals[index]; } protected override Size ArrangeOverride(Size arrangeBounds) { this.chrome.Arrange(new Rect(new Point(0.0, 0.0), arrangeBounds)); return arrangeBounds; } } }
我们完全可以把SizeChrome内部的东西,在Adorner里面add进去,每个拐角的thumb增加事件,然后只需要给contentcontrol的DesignerItemDecorator中加入这个adorner就可以完成,但是这样不易维护。
所以提取出一个XXChrome来容纳 一些东西,这里也指定了Chrome的DataContext为ContentControl了,调用这个adorner时候会传递进来的。
第三步骤,实现ResizeThumb
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Media; using System.Collections.Generic; namespace DiagramTool2 { public class ResizeThumb : Thumb { private RotateTransform rotateTransform; private double angle; private Adorner adorner; private Point transformOrigin; private ContentControl designerItem; private Canvas canvas; public ResizeThumb() { DragStarted += new DragStartedEventHandler(this.ResizeThumb_DragStarted); DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta); } private void ResizeThumb_DragStarted(object sender, DragStartedEventArgs e) { this.designerItem = this.DataContext as ContentControl; if (this.designerItem != null) { this.canvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas; if (this.canvas != null) { this.transformOrigin = this.designerItem.RenderTransformOrigin; this.rotateTransform = this.designerItem.RenderTransform as RotateTransform; if (this.rotateTransform != null) { this.angle = this.rotateTransform.Angle * Math.PI / 180.0; } else { this.angle = 0.0d; } } } } private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e) { if (this.designerItem != null) { double deltaVertical, deltaHorizontal; switch (VerticalAlignment) { case System.Windows.VerticalAlignment.Bottom: deltaVertical = Math.Min(-e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle)))); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) - deltaVertical * this.transformOrigin.Y * Math.Sin(-this.angle)); this.designerItem.Height -= deltaVertical; break; case System.Windows.VerticalAlignment.Top: deltaVertical = Math.Min(e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaVertical * Math.Cos(-this.angle) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle)))); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaVertical * Math.Sin(-this.angle) - (this.transformOrigin.Y * deltaVertical * Math.Sin(-this.angle))); this.designerItem.Height -= deltaVertical; break; default: break; } switch (HorizontalAlignment) { case System.Windows.HorizontalAlignment.Left: deltaHorizontal = Math.Min(e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaHorizontal * Math.Sin(this.angle) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle)); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaHorizontal * Math.Cos(this.angle) + (this.transformOrigin.X * deltaHorizontal * (1 - Math.Cos(this.angle)))); this.designerItem.Width -= deltaHorizontal; break; case System.Windows.HorizontalAlignment.Right: deltaHorizontal = Math.Min(-e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle)); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + (deltaHorizontal * this.transformOrigin.X * (1 - Math.Cos(this.angle)))); this.designerItem.Width -= deltaHorizontal; break; default: break; } } e.Handled = true; } } }
这里有个弧度
1弧度约为57.3°,而一π弧度等于180°。在数学和物理中,弧度是角的度量单位。它是由国际单位制导出的单位,单位缩写是rad。弧度也就是弧长等于半径的圆弧,其所对的圆心角
弧度 = 角度*PI/180
角度 = 弧度*180/PI
所以,这里把角度 转换弧度,方便下面的计算
定义好后,用上去,那么当前ContentControl顶层就会多个SizeChrome出来
这里我们调整宽高,还显示当前宽高,我们加个转换器,显示整数的宽高
public class DoubleFormatConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { double d = (double)value; return Math.Round(d); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } }
<s:DoubleFormatConverter x:Key="doubleFormatConverter"/> <Style TargetType="{x:Type s:SizeChrome}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type s:SizeChrome}"> <Grid SnapsToDevicePixels="True"> <Path Stroke="Red" StrokeThickness="1" Height="10" VerticalAlignment="Bottom" Margin="-2,0,-2,-15" Stretch="Fill" Data="M0,0 0,10 M 0,5 100,5 M 100,0 100,10"/> <TextBlock Text="{Binding Path=Width, Converter={StaticResource doubleFormatConverter}}" Background="White" Padding="3,0,3,0" Foreground="Red" Margin="0,0,0,-18" HorizontalAlignment="Center" VerticalAlignment="Bottom"/> <Path Stroke="Red" StrokeThickness="1" Width="10" HorizontalAlignment="Right" Margin="0,-2,-15,-2" Stretch="Fill" Data="M5,0 5,100 M 0,0 10,0 M 0,100 10,100"/> <TextBlock Text="{Binding Path=Height, Converter={StaticResource doubleFormatConverter}}" Background="White" Foreground="Red" Padding="3,0,3,0" Margin="0,0,-18,0" HorizontalAlignment="Right" VerticalAlignment="Center"> <TextBlock.LayoutTransform> <RotateTransform Angle="90" CenterX="1" CenterY="0.5"/> </TextBlock.LayoutTransform> </TextBlock> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
此时运行项目
接下来添加 调整大小的,修改sizechrome外观
<Style TargetType="{x:Type s:SizeChrome}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type s:SizeChrome}"> <Grid SnapsToDevicePixels="True"> <Grid> <s:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 -4 0 0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/> <s:ResizeThumb Width="3" Cursor="SizeWE" Margin="-4 0 0 0" VerticalAlignment="Stretch" HorizontalAlignment="Left"/> <s:ResizeThumb Width="3" Cursor="SizeWE" Margin="0 0 -4 0" VerticalAlignment="Stretch" HorizontalAlignment="Right"/> <s:ResizeThumb Height="3" Cursor="SizeNS" Margin="0 0 0 -4" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/> <s:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0" VerticalAlignment="Top" HorizontalAlignment="Left"/> <s:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0" VerticalAlignment="Top" HorizontalAlignment="Right"/> <s:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6" VerticalAlignment="Bottom" HorizontalAlignment="Left"/> <s:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6" VerticalAlignment="Bottom" HorizontalAlignment="Right"/> </Grid> <Path Stroke="Red" StrokeThickness="1" Height="10" VerticalAlignment="Bottom" Margin="-2,0,-2,-15" Stretch="Fill" Data="M0,0 0,10 M 0,5 100,5 M 100,0 100,10"/> <TextBlock Text="{Binding Path=Width, Converter={StaticResource doubleFormatConverter}}" Background="White" Padding="3,0,3,0" Foreground="Red" Margin="0,0,0,-18" HorizontalAlignment="Center" VerticalAlignment="Bottom"/> <Path Stroke="Red" StrokeThickness="1" Width="10" HorizontalAlignment="Right" Margin="0,-2,-15,-2" Stretch="Fill" Data="M5,0 5,100 M 0,0 10,0 M 0,100 10,100"/> <TextBlock Text="{Binding Path=Height, Converter={StaticResource doubleFormatConverter}}" Background="White" Foreground="Red" Padding="3,0,3,0" Margin="0,0,-18,0" HorizontalAlignment="Right" VerticalAlignment="Center"> <TextBlock.LayoutTransform> <RotateTransform Angle="90" CenterX="1" CenterY="0.5"/> </TextBlock.LayoutTransform> </TextBlock> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
然后运行
同样的方式,我们可以把旋转按钮也加在SizeChrome里面,但是想到一个更好的方案,把旋转和缩放放在一个adorner,然后尺寸显示放在第二个adorner,在resizethumb的dragcompleted时候,移除具体的尺寸数值,本来这里也应该有2个adorner。或者可以再sizechrome内部加一个依赖属性,用来控制边缘数值线的显示。
同样的方式,新增一个ResizeRotateChrome
using System.Windows; using System.Windows.Controls; namespace DiagramTool2 { public class ResizeRotateChrome : Control { static ResizeRotateChrome() { FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(ResizeRotateChrome), new FrameworkPropertyMetadata(typeof(ResizeRotateChrome))); } } }
ResizeRotateAdorner
using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; namespace DiagramTool2 { public class ResizeRotateAdorner : Adorner { private VisualCollection visuals; private ResizeRotateChrome chrome; protected override int VisualChildrenCount { get { return this.visuals.Count; } } public ResizeRotateAdorner(ContentControl designerItem) : base(designerItem) { SnapsToDevicePixels = true; this.chrome = new ResizeRotateChrome(); this.chrome.DataContext = designerItem; this.visuals = new VisualCollection(this); this.visuals.Add(this.chrome); } protected override Size ArrangeOverride(Size arrangeBounds) { this.chrome.Arrange(new Rect(arrangeBounds)); return arrangeBounds; } protected override Visual GetVisualChild(int index) { return this.visuals[index]; } } }
接下来实现关键的旋转
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; namespace DiagramTool2 { public class RotateThumb : Thumb { private double initialAngle; private RotateTransform rotateTransform; private Vector startVector; private Point centerPoint; private ContentControl designerItem; private Canvas canvas; public RotateThumb() { DragDelta += new DragDeltaEventHandler(this.RotateThumb_DragDelta); DragStarted += new DragStartedEventHandler(this.RotateThumb_DragStarted); } private void RotateThumb_DragStarted(object sender, DragStartedEventArgs e) { this.designerItem = DataContext as ContentControl; if (this.designerItem != null) { this.canvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas; if (this.canvas != null) { this.centerPoint = this.designerItem.TranslatePoint( new Point(this.designerItem.Width * this.designerItem.RenderTransformOrigin.X, this.designerItem.Height * this.designerItem.RenderTransformOrigin.Y), this.canvas); Point startPoint = Mouse.GetPosition(this.canvas); this.startVector = Point.Subtract(startPoint, this.centerPoint); this.rotateTransform = this.designerItem.RenderTransform as RotateTransform; if (this.rotateTransform == null) { this.designerItem.RenderTransform = new RotateTransform(0); this.initialAngle = 0; } else { this.initialAngle = this.rotateTransform.Angle; } } } } private void RotateThumb_DragDelta(object sender, DragDeltaEventArgs e) { if (this.designerItem != null && this.canvas != null) { Point currentPoint = Mouse.GetPosition(this.canvas); Vector deltaVector = Point.Subtract(currentPoint, this.centerPoint); double angle = Vector.AngleBetween(this.startVector, deltaVector); RotateTransform rotateTransform = this.designerItem.RenderTransform as RotateTransform; rotateTransform.Angle = this.initialAngle + Math.Round(angle, 0); this.designerItem.InvalidateMeasure(); } } } }
修改ResizeThumb
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Media; using System.Collections.Generic; namespace DiagramTool2 { public class ResizeThumb : Thumb { private RotateTransform rotateTransform; private double angle; private Adorner adorner; private Point transformOrigin; private ContentControl designerItem; private Canvas canvas; public ResizeThumb() { DragStarted += new DragStartedEventHandler(this.ResizeThumb_DragStarted); DragDelta += new DragDeltaEventHandler(this.ResizeThumb_DragDelta); DragCompleted += new DragCompletedEventHandler(this.ResizeThumb_DragCompleted); } private void ResizeThumb_DragStarted(object sender, DragStartedEventArgs e) { this.designerItem = this.DataContext as ContentControl; if (this.designerItem != null) { this.canvas = VisualTreeHelper.GetParent(this.designerItem) as Canvas; if (this.canvas != null) { this.transformOrigin = this.designerItem.RenderTransformOrigin; this.rotateTransform = this.designerItem.RenderTransform as RotateTransform; if (this.rotateTransform != null) { this.angle = this.rotateTransform.Angle * Math.PI / 180.0; } else { this.angle = 0.0d; } AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.canvas); if (adornerLayer != null) { this.adorner = new SizeAdorner(this.designerItem); adornerLayer.Add(this.adorner); } } } } private void ResizeThumb_DragDelta(object sender, DragDeltaEventArgs e) { if (this.designerItem != null) { double deltaVertical, deltaHorizontal; switch (VerticalAlignment) { case System.Windows.VerticalAlignment.Bottom: deltaVertical = Math.Min(-e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle)))); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) - deltaVertical * this.transformOrigin.Y * Math.Sin(-this.angle)); this.designerItem.Height -= deltaVertical; break; case System.Windows.VerticalAlignment.Top: deltaVertical = Math.Min(e.VerticalChange, this.designerItem.ActualHeight - this.designerItem.MinHeight); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaVertical * Math.Cos(-this.angle) + (this.transformOrigin.Y * deltaVertical * (1 - Math.Cos(-this.angle)))); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaVertical * Math.Sin(-this.angle) - (this.transformOrigin.Y * deltaVertical * Math.Sin(-this.angle))); this.designerItem.Height -= deltaVertical; break; default: break; } switch (HorizontalAlignment) { case System.Windows.HorizontalAlignment.Left: deltaHorizontal = Math.Min(e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) + deltaHorizontal * Math.Sin(this.angle) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle)); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + deltaHorizontal * Math.Cos(this.angle) + (this.transformOrigin.X * deltaHorizontal * (1 - Math.Cos(this.angle)))); this.designerItem.Width -= deltaHorizontal; break; case System.Windows.HorizontalAlignment.Right: deltaHorizontal = Math.Min(-e.HorizontalChange, this.designerItem.ActualWidth - this.designerItem.MinWidth); Canvas.SetTop(this.designerItem, Canvas.GetTop(this.designerItem) - this.transformOrigin.X * deltaHorizontal * Math.Sin(this.angle)); Canvas.SetLeft(this.designerItem, Canvas.GetLeft(this.designerItem) + (deltaHorizontal * this.transformOrigin.X * (1 - Math.Cos(this.angle)))); this.designerItem.Width -= deltaHorizontal; break; default: break; } } e.Handled = true; } private void ResizeThumb_DragCompleted(object sender, DragCompletedEventArgs e) { if (this.adorner != null) { AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this.canvas); if (adornerLayer != null) { adornerLayer.Remove(this.adorner); } this.adorner = null; } } } }
拖大小完成后,从canvas顶部移除 移除adorner,这个adorner是SizeAdorner,里面放的SizeChrome这个控件,我们修改SizeChrome的外观为那个尺寸线条就好了,把那几个调整宽高的点移动到旋转的ResizeRotateChrome中去。
修改DesignerItemDecorator直接增加一个旋转和调整大小的装饰器。
修改外观样式,
<Style TargetType="{x:Type Shape}" x:Key="ThumbCorner"> <Setter Property="SnapsToDevicePixels" Value="true" /> <Setter Property="Stroke" Value="#FFC8C8C8" /> <Setter Property="StrokeThickness" Value=".5" /> <Setter Property="Width" Value="7" /> <Setter Property="Height" Value="7" /> <Setter Property="Margin" Value="-2" /> <Setter Property="Fill"> <Setter.Value> <RadialGradientBrush Center="0.2, 0.2" GradientOrigin="0.2, 0.2" RadiusX="0.8" RadiusY="0.8"> <GradientStop Color="White" Offset="0.0" /> <GradientStop Color="Gray" Offset="0.8" /> </RadialGradientBrush> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type s:ResizeRotateChrome}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type s:ResizeRotateChrome}"> <Grid> <Grid Margin="-3"> <s:RotateThumb Width="7" Height="7" Margin="0,-20,0,0" Cursor="Hand" VerticalAlignment="Top" HorizontalAlignment="Center"/> <s:ResizeThumb Height="3" Cursor="SizeNS" Background="Red" VerticalAlignment="Top" HorizontalAlignment="Stretch"/> <s:ResizeThumb Width="3" Cursor="SizeWE" Background="Red" VerticalAlignment="Stretch" HorizontalAlignment="Left"/> <s:ResizeThumb Width="3" Cursor="SizeWE" Background="Red" VerticalAlignment="Stretch" HorizontalAlignment="Right"/> <s:ResizeThumb Height="3" Cursor="SizeNS" Background="Red" VerticalAlignment="Bottom" HorizontalAlignment="Stretch"/> <s:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Top" HorizontalAlignment="Left"/> <s:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Top" HorizontalAlignment="Right"/> <s:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNESW" VerticalAlignment="Bottom" HorizontalAlignment="Left"/> <s:ResizeThumb Width="7" Height="7" Margin="-2" Cursor="SizeNWSE" VerticalAlignment="Bottom" HorizontalAlignment="Right"/> </Grid> <Grid IsHitTestVisible="False" Opacity="1" Margin="-3"> <Rectangle SnapsToDevicePixels="True" StrokeThickness="1" Margin="1" Stroke="White"/> <Line StrokeThickness="1" X1="0" Y1="0" X2="0" Y2="20" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-19,0,0" Stroke="White"/> <Ellipse Style="{StaticResource ThumbCorner}" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="-1,-20,0,0"/> <Ellipse Style="{StaticResource ThumbCorner}" HorizontalAlignment="Left" VerticalAlignment="Top"/> <Ellipse Style="{StaticResource ThumbCorner}" HorizontalAlignment="Right" VerticalAlignment="Top"/> <Ellipse Style="{StaticResource ThumbCorner}" HorizontalAlignment="Left" VerticalAlignment="Bottom"/> <Ellipse Style="{StaticResource ThumbCorner}" HorizontalAlignment="Right" VerticalAlignment="Bottom"/> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <s:DoubleFormatConverter x:Key="doubleFormatConverter"/> <Style TargetType="{x:Type s:SizeChrome}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type s:SizeChrome}"> <Grid SnapsToDevicePixels="True"> <Path Stroke="Red" StrokeThickness="1" Height="10" VerticalAlignment="Bottom" Margin="-2,0,-2,-15" Stretch="Fill" Data="M0,0 0,10 M 0,5 100,5 M 100,0 100,10"/> <TextBlock Text="{Binding Path=Width, Converter={StaticResource doubleFormatConverter}}" Background="White" Padding="3,0,3,0" Foreground="Red" Margin="0,0,0,-18" HorizontalAlignment="Center" VerticalAlignment="Bottom"/> <Path Stroke="Red" StrokeThickness="1" Width="10" HorizontalAlignment="Right" Margin="0,-2,-15,-2" Stretch="Fill" Data="M5,0 5,100 M 0,0 10,0 M 0,100 10,100"/> <TextBlock Text="{Binding Path=Height, Converter={StaticResource doubleFormatConverter}}" Background="White" Foreground="Red" Padding="3,0,3,0" Margin="0,0,-18,0" HorizontalAlignment="Right" VerticalAlignment="Center"> <TextBlock.LayoutTransform> <RotateTransform Angle="90" CenterX="1" CenterY="0.5"/> </TextBlock.LayoutTransform> </TextBlock> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
运行项目
一个节点的 调整,旋转 特性就出来了。
客户端,只需要是ContentControl包裹的 的内容都可以被调整。
AY这篇文章,根据 这篇文章 ,自己理解源码编写。
时间有限,对此项目的 讲解还算挺彻底的了。
最后一步修改外观即可,5个顶点,再加一些 选中特性外观,就是一个完美的调整了。
====================www.ayjs.net 杨洋 wpfui.com ayui ay aaronyang=======请不要转载谢谢了。=========
命名规范:
XXAdorner定义一个辅助的顶层外观,如果外观复杂,可以考虑增加一个XXChrome的控件,来管理这个顶层外观
推荐您阅读更多有关于“”的文章
以上所述就是小编给大家介绍的《AY 一个wpf画图项目复习下【1/10】》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- AY 一个wpf画图项目复习下【2/10】
- 画图倒不如手绘
- 值得拥有的手绘风格画图工具
- 使用drawio进行画图真的很方便
- 我终于懂得如何使用matplotlib进行画图
- 算法对建筑业的影响,不仅仅是画图
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
豆瓣,流行的秘密
黄修源 / 机械工业出版社 / 2009-9 / 29.00
380万人为何会齐聚豆瓣? HIN1和SARS是如何传播扩散开的? 贾君鹏何以快速窜红网络? 通过创新扩散的理论的分析和说明,给出了所有这些问题的答案! 这本书从豆瓣的流行现象说开来,应用了创新扩散等传播学道理来解释了豆瓣如何流行起来,同时作者还同时用创新扩散的理论解释了为何会出现世界变平的现象,长尾理论,SARS病毒的高速传播等。 作者以前任豆瓣设计师的身份以自己亲......一起来看看 《豆瓣,流行的秘密》 这本书的介绍吧!