WPF 的Canvas画图区整体缩放与平移(二)

WPF对象都具有RenderTransform的属性,可以通过设置RenderTransform来对WPF的元素进行变换,无论是控件还是形状都可以变换。典型的变换包括缩小放大与平移。

(一)缩放

(见前一篇文章)

(二)平移

为了实现平移,这里以按下鼠标中间键并移动鼠标作为事件触发方式,来实现平移。即先下辖鼠标中键(滚轮键),移动鼠标,这样WPF元素就会跟随鼠标平移。

WPF元素和形状的变换是针对于自身的坐标系的,因此不能直接获取其自身坐标系的点来进行变换,否则是不对了,有时也会不停闪烁跳动,本人一开始不小心用错了,就用待平移的WPF元素自身坐标点来作为平移参数,后来发现屏幕不停闪烁跳动,且平移距离也不正确。

如果采用Canvas作为画板来绘制一些形状,想要通过鼠标或触摸操作来进行平移,那么不能简单地对canvas进行变换,否则Cancas平移的时候就会覆盖周边的其它控件,也就是Canvas画布自身被移动了,而不仅仅是Canvas内部画出来的形状被移动了。

那如果需要实现移动怎么办呢?与前一篇文章一样,我采取的方式是在Canvas外面包装一个WPF元素,比如Border元素。这样,Canvas就成为Border元素的子元素了,然后在Border元素上实现鼠标控制操作来变换Canvas元素。

  <Border Name="outside" Grid.Column="1" Background="LightBlue" 
                PreviewMouseDown="outsidewrapper_PreviewMouseDown" 
                PreviewMouseMove="outsidewrapper_PreviewMouseMove" 
                PreviewMouseUp="outside_PreviewMouseUp"
                PreviewMouseWheel="outside_PreviewMouseWheel"
                ClipToBounds="True">

            <Canvas Name="inside" Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource AncestorType=Border}}" 
                    Height="{Binding Path=ActualHeight,RelativeSource={RelativeSource AncestorType=Border}}">
                <Canvas.RenderTransform>
                    <TransformGroup/>
                </Canvas.RenderTransform>
                <Line Canvas.Left="50" Canvas.Top="50" X1="100" Y1="200"  X2="100" Y2="200" Stroke="Black" StrokeThickness="5"/>
                <Rectangle Canvas.Left="150" Canvas.Top="150" Width="380" Height="296" Fill="Red" />

            </Canvas>
        </Border>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        Point previousPoint;
        bool isTranslateStart = false;

        private void outsidewrapper_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
//说明:虽然WPF元素存在MouseLeftButtonDown和MouseRightButtonDown事件,
//但却没找到MouseMiddleButtonDown事件。
//然而,幸运的是,MouseDown事件是不论哪个鼠标键按下的时候都会触发的,因此可以利用该事件,
//并通过检测哪个键被按下的状态参数来判断是否是中间键被按下了。
//也就是说,当出现MouseDown事件的时候,判断三个键的状态,如果中间键被按下,
//而其它两个键没有被按下,则认为是中间键按下的事件。在该事件中,记录下当时鼠标的位置
            
if(e.MiddleButton==MouseButtonState.Pressed&&e.LeftButton==MouseButtonState.Released&&e.RightButton==MouseButtonState.Released)
            {
                previousPoint = e.GetPosition(outside);
                isTranslateStart = true;
            }
            e.Handled = true;
        }

        private void outsidewrapper_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.MiddleButton == MouseButtonState.Pressed && e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
            {
                if (isTranslateStart)
                {
                    Point currentPoint = e.GetPosition(outside);  //不能用 inside,必须用outside
                    Vector v = currentPoint - previousPoint;
                    TransformGroup tg = inside.RenderTransform as TransformGroup;                   
                    tg.Children.Add(new TranslateTransform(v.X,v.Y));  //centerX和centerY用外部包装元素的坐标,不能用内部被变换的Canvas元素的坐标
                //    inside.RenderTransform = tg;
                    previousPoint = currentPoint;
                }

            }
            e.Handled = true;
        }

        private void outside_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.MiddleButton == MouseButtonState.Pressed && e.LeftButton == MouseButtonState.Released && e.RightButton == MouseButtonState.Released)
            {
                if (isTranslateStart)
                {
                    isTranslateStart = false;
                }
            }
            e.Handled = true;
        }

        private void outside_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
        {

            Point currentPoint = e.GetPosition(outside);  //不能用 inside,必须用outside
               
            TransformGroup tg = inside.RenderTransform as TransformGroup;

            double s = ((double)e.Delta) / 1000.0+1.0;

            //centerX和centerY用外部包装元素的坐标,不能用内部被变换的Canvas元素的坐标
            tg.Children.Add(new ScaleTransform(s,s,currentPoint.X,currentPoint.Y));  
            e.Handled = true;
        }
    }

需要注意事项包括:

(1)包装的元素需要添加ClipToBounds="True"属性,这样内部Canvas超出包装元素的时候,超出部分就会被裁剪掉;

(2)把Canvas元素的初始大小设置为与包装元素一样大小,可以通过RelativeSource来设置:Width="{Binding Path=ActualWidth,RelativeSource={RelativeSource AncestorType=Border}}" ,Height="{Binding Path=ActualHeight,RelativeSource={RelativeSource AncestorType=Border}}"。

(3)将外包包装元素的背景和Canvas的背景设置为一样的背景,这样当Canvas平移的时候,不会给然感觉Canvas画布在移动,而是感觉内部元素(比如线、多边形、圆形、矩形、文字等等)。或者Canvas不设置背景也是可行的(但可能Canvas会收不到鼠标事件......)。

(4)用鼠标事件控制平移的话,可以使用隧道事件,且事件应该关联在外部包装元素Border上。平移的大小用外包装元素坐标系统中的坐标点进行计算。当然具体根据需要采用合适的事件,比如滚轮事件之类的,触控事件之类的。

(5)虽然WPF元素存在MouseLeftButtonDown和MouseRightButtonDown事件,但却没找到MouseMiddleButtonDown事件。然而,幸运的是,MouseDown事件是不论哪个鼠标键按下的时候都会触发的,因此可以利用该事件,并通过检测哪个键被按下的状态参数来判断是否是中间键被按下了。也就是说,当出现MouseDown事件的时候,判断三个键的状态,如果中间键被按下,而其它两个键没有被按下,则认为是中间键按下的事件。在该事件中,记录下当时鼠标的位置。鼠标移动的时候,获取新的鼠标位置,并与记录为对象成员变量的前一个鼠标位置相减,即刻得知平移量,通过向Canvas的TransformGroup中添加平移变换,即可实现平移变换。

(6)理解:按理说所有元素的变换都是针对自身的坐标体系的,而不是针对外部父元素的坐标体系的,但是WPF有个特点,虽然时针对自身坐标系的变化,但是变换前后自身的ActualWidth和ActualHeight都没有发生任何变化,下次继续变换的时候,还是用的ActualWidth和ActualHeigth作为自身坐标系的参考用途,而不是按照变换后的实际尺寸在定位自身坐标系尺寸的。所以,使用外部包装元素的坐标系来给定每次变换的(CenterX和CenterY)是可行的。这一点需要慢慢理解。

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
LiveCharts控件提供了两种方法来实现缩放平移: 1. 使用ZoomingOptions: 在XAML中,可以通过设置ZoomingOptions属性来启用缩放平移功能。例如,以下代码启用了鼠标滚轮缩放和拖动平移功能: ``` <lvc:CartesianChart ZoomingOptions="{x:Static lvc:ZoomingOptions.Xy}"> ... </lvc:CartesianChart> ``` ZoomingOptions属性有四个可选值: - None:禁用缩放平移功能。 - X:启用水平缩放平移功能。 - Y:启用垂直缩放平移功能。 - Xy:启用水平和垂直缩放平移功能。 2. 使用Zoom和Pan方法: 在代码中,可以使用Zoom方法和Pan方法来实现缩放平移。例如,以下代码实现了通过代码缩放平移: ``` private void ZoomIn_Click(object sender, RoutedEventArgs e) { chart.Zoom(new Point(0, 0), ZoomDirection.ZoomIn); } private void ZoomOut_Click(object sender, RoutedEventArgs e) { chart.Zoom(new Point(0, 0), ZoomDirection.ZoomOut); } private void PanLeft_Click(object sender, RoutedEventArgs e) { chart.Pan(new Point(-10, 0)); } private void PanRight_Click(object sender, RoutedEventArgs e) { chart.Pan(new Point(10, 0)); } ``` 其中,Zoom方法有两个参数:缩放中心点和缩放方向。缩放中心点是一个Point类型的值,表示缩放的中心点。缩放方向是ZoomDirection枚举类型的值,表示缩放的方向,有ZoomIn和ZoomOut两个值。 Pan方法有一个参数:平移平移是一个Point类型的值,表示平移的向

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值