WPF中画布Canvas控件中内容缩放
1. Transform Class 简介
Transform类位于命名空间System.Windows.Media下。在WPF中Transform类被广泛应用于绘图元素的变换。作为大部分可视化控件的基类,UIElement类具有这样的静态依赖属性:
// System.Windows.UIElement
public Transform RenderTransform
{
get
{
return (Transform)base.GetValue(UIElement.RenderTransformProperty);
}
set
{
base.SetValue(UIElement.RenderTransformProperty, value);
}
}
[CommonDependencyProperty]
public static readonly DependencyProperty RenderTransformProperty =
DependencyProperty.Register("RenderTransform", typeof(Transform), typeof(UIElement), new PropertyMetadata(Transform.Identity, new PropertyChangedCallback(UIElement.RenderTransform_Changed)));
private static void RenderTransform_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uielement = (UIElement)d;
if (!uielement.NeverMeasured && !uielement.NeverArranged && !e.IsASubPropertyChange)
{
uielement.InvalidateArrange();
uielement.AreTransformsClean = false;
}
}
官方文档的注解:
A render transform does not regenerate layout size or render size information. Render transforms are typically intended for animating or applying a temporary effect to an element. For example, the element might zoom when focused or moused over, or might jitter on load to draw the eye to that part of the user interface (UI).
故可以通过更改UIElement的RenderTransform属性达到控件变换的目的,且开销较小。
注意到Transform类为抽象类,派生类型有:
- System.Windows.Media.MatrixTransform
- System.Windows.Media.TranslateTransform
- System.Windows.Media.RotateTransform
- System.Windows.Media.ScaleTransform
- System.Windows.Media.SkewTransform
- System.Windows.Media.TransformGroup
由于继承自System.Windows.Media.Transform ,上述各种变换中都具有Value的只读属性,其返回 Matrix 结构。
2. 画布Canvas控件中内容缩放方法简述
网上存在一些解决方案,是通过给Canvas外层添加Border,缩放变换Canvas本省来达到缩放的目的。该方法无法控制Canvas中某一控件不缩放,或各控件非同时缩放,故这里不使用该方法进行缩放,而是监听Canvas的MouseWheel事件并遍历Canvas.Children,对子控件单独控制。控制的基本思想在于,为子控件附加CanZoom附加属性,并附加子控件各自的缩放信息,以此为根据进行缩放变换。
为方便起见,下节直接构造一个新的布局控件 CanvasEx ,其继承于Canvas。
3. CanvasEx具体实现及解读
废话不多说,先上代码:
public class CanvasEx : Canvas
{
private bool _onKeyboardCommand = false;
private bool _onMouseButtonCommand = false;
private Point _previousDraggingPoint;
public static Cursor DragCursor => new Cursor(AppDomain.CurrentDomain.BaseDirectory + "DragHand.cur");
private Cursor _previousCursor;
[Browsable(false)]
private bool IsDragging
{
get {
return (bool)GetValue(IsDraggingProperty); }
set {
SetValue(IsDraggingProperty, value); }
}
// Using a DependencyProperty as the backing store for IsDragging. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsDraggingProperty =
DependencyProperty.Register("IsDragging", typeof(bool), typeof(CanvasEx), new PropertyMetadata(false));
[Browsable(false)]
private bool IsZooming
{
get {
return (bool)GetValue(IsZoomingProperty); }
set {
SetValue(IsZoomingProperty, value); }
}
// Using a DependencyProperty as the backing store for IsZooming. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsZoomingProperty =
DependencyProperty.Register("IsZooming", typeof(bool), typeof(CanvasEx), new PropertyMetadata(false));
public static bool GetCanDrag(DependencyObject obj)
{
return (bool)obj.GetValue(CanDragProperty);
}
public static void SetCanDrag(DependencyObject obj, bool value)
{
obj.SetValue(CanDragProperty, value);
}
// Using a DependencyProperty as the backing store for CanDrag. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CanDragProperty =
DependencyProperty.RegisterAttached("CanDrag", typeof(bool), typeof(CanvasEx), new PropertyMetadata(false));
public static