WPF图像上画线

图上显示跟随鼠标的十字交叉线

我们在PageLight页面上实现该功能

第一步,PageLight.xaml添加以下代码

<Page ...>
  <Grid>
    ...
    <Grid Name="workspace" Grid.Row="2" Grid.Column="1" Background="Black"
          MouseLeftButtonDown="ImgMouseLeftButtonDown" 
          MouseLeftButtonUp="ImgMouseLeftButtonUp"
          MouseMove="ImgMouseMove"
          MouseWheel="ImgMouseWheel"
          MouseLeave="ImgMouseLeave">
      <Grid.Resources>
        <TransformGroup x:Key="Imageview">
          <ScaleTransform/>
          <TranslateTransform/>
        </TransformGroup>
      </Grid.Resources>
      <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled"
                    Cursor="Cross" Focusable="False" x:Name="BackFrame">
        <ContentControl Name="imgcontrol">
          <Image Name="image" Source="F:\chenggeng\source\Image\Desert.jpg"
                 Width="Auto" Height="Auto" RenderTransform="{StaticResource Imageview}" 
                 Stretch="Uniform"  RenderOptions.BitmapScalingMode="NearestNeighbor"/>
        </ContentControl>
      </ScrollViewer>
      <Path Fill="Yellow" Stroke="Blue" Name="crossline"></Path>
    </Grid>
  </Grid>
</Page>

工作布局Grid中有两个控件,一个是可变换的图像,一个是承载十字交叉线的Path

第二步,PageLight.xaml.cs中添加以下代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace wpfbase
{
  public partial class PageLight : Page
  {
    // 定义十字交叉线
    private LineGeometry lineX, lineY;
    private GeometryGroup linegroup;
    public PageLight()
    {
      InitializeComponent();
      // 初始化十字交叉线
      lineX = new LineGeometry();
      lineY = new LineGeometry();
      linegroup = new GeometryGroup();
      crossline.Data = linegroup;
      linegroup.Children.Add(lineX);
      linegroup.Children.Add(lineY);
      this.SizeChanged += new System.Windows.SizeChangedEventHandler(PageLightResized);
      // 工作区添加滚轮事件冒泡
      workspace.PreviewMouseWheel += (sender, e) => {
        var eventArg = new  MouseWheelEventArgs(e.MouseDevice,e.Timestamp,e.Delta);
        eventArg.RoutedEvent = UIElement.MouseWheelEvent;
        eventArg.Source = sender;
        workspace.RaiseEvent(eventArg);
      };
    }
    /* ------------页面缩放响应-------------- */
    private void PageLightResized(object sender, System.EventArgs e) {
      // 窗口及图像的ActualWidth均发生改变
      // 窗口实际尺寸,图像比例下实际尺寸
      var group = workspace.FindResource("Imageview") as TransformGroup;
      var scale = group.Children[0] as ScaleTransform;
      var move = group.Children[1] as TranslateTransform;
      move.X = 0;
      move.Y = 0;
      scale.ScaleX = 1;
      scale.ScaleY = 1;
    }
    /* -------------图像缩放处理--------------- */
    // img为可视区域,image为图像
    private bool mouseDown;
    private Point position;
    private void ImgMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
      if(!image.IsMouseOver && !crossline.IsMouseOver) {return;}
      image.CaptureMouse();
      mouseDown = true;
      position = e.GetPosition(imgcontrol);
      Point mouseXY = e.GetPosition(workspace);
  }
    private void ImgMouseLeftButtonUp(object sender, MouseButtonEventArgs e) {
      if(!image.IsMouseOver && !crossline.IsMouseOver) {return;}
      image.ReleaseMouseCapture();
      mouseDown = false;
    }
    private void ImgMouseMove(object sender, MouseEventArgs e) {
      if(!image.IsMouseOver && !crossline.IsMouseOver) {
        ClearCrossLine();
        return;
      }
      if(mouseDown) {
        DoMouseMove(imgcontrol, e);
      }
      Point mouse = e.GetPosition(imgcontrol);
      FlashCrossLine(mouse);
    }
    private void ImgMouseLeave(object sender, MouseEventArgs e) {
      ClearCrossLine();
    }
    private void ClearCrossLine() {
      lineX.StartPoint = new Point();
      lineX.EndPoint = new Point();
      lineY.StartPoint = new Point();
      lineY.EndPoint = new Point();
    }
    private void FlashCrossLine(Point mouse) {
      var group = workspace.FindResource("Imageview") as TransformGroup;
      double scale = (group.Children[0] as ScaleTransform).ScaleX;
      var move = group.Children[1] as TranslateTransform;
      double W = imgcontrol.ActualWidth;
      double H = imgcontrol.ActualHeight;
      double w = image.ActualWidth;
      double h = image.ActualHeight;
      lineX.StartPoint = new Point(mouse.X, Math.Max(0, move.Y+(H-h)/2));
      lineX.EndPoint = new Point(mouse.X, Math.Min(H, move.Y+h*scale+(H-h)/2));
      lineY.StartPoint = new Point(Math.Max(0, move.X+(W-w)/2), mouse.Y);
      lineY.EndPoint = new Point(Math.Min(W, move.X+w*scale+(W-w)/2), mouse.Y);
    }
    private void DoMouseMove(ContentControl img, MouseEventArgs e) {
      if(e.LeftButton != MouseButtonState.Pressed) {return;}
      var group = workspace.FindResource("Imageview") as TransformGroup;
      double scale = (group.Children[0] as ScaleTransform).ScaleX;
      var move = group.Children[1] as TranslateTransform;
      var mouseXY = e.GetPosition(img);
      move.X += mouseXY.X - position.X;
      move.Y += mouseXY.Y - position.Y;
      position = mouseXY;
      // W+w > 2*move_x > -((2*scale-1)*w + W)  水平平移限制条件
      // H+h > 2*move_y > -((2*scale-1)*h + H)  垂直平移限制条件
      double W = img.ActualWidth;
      double H = img.ActualHeight;
      double w = image.ActualWidth;
      double h = image.ActualHeight;
      if(move.X*2 > W+w-20)
        move.X = (W+w-20)/2;
      if(-move.X*2 > (2*scale-1)*w+W-20)
        move.X = 10-(scale-0.5)*w-W/2;
      if(move.Y*2 > H+h-20)
        move.Y = (H+h-20)/2;
      if(-move.Y*2 > (2*scale-1)*h+H-20)
        move.Y = 10-(scale-0.5)*h-H/2;
    }
    private void ImgMouseWheel(object sender, MouseWheelEventArgs e) {
      if(!image.IsMouseOver && !crossline.IsMouseOver) {return;}
      var point = e.GetPosition(image);
      var group = workspace.FindResource("Imageview") as TransformGroup;
      var delta = e.Delta * 0.002;
      DoWheelZoom(group, point, delta);
      Point mouse = e.GetPosition(imgcontrol);
      FlashCrossLine(mouse);
    }
    private void DoWheelZoom(TransformGroup group, Point point, double delta) {
      var transform = group.Children[0] as ScaleTransform;
      if (transform.ScaleX + delta < 0.1) return;
      transform.ScaleX += delta;
      transform.ScaleY += delta;
      var transform1 = group.Children[1] as TranslateTransform;
      transform1.X -= point.X*delta;
      transform1.Y -= point.Y*delta;
    }
  }
}

代码说明:

  1. 为了后台实现十字交叉线对鼠标的跟随,我们不在前台xaml文件中添加十字线(后台查找LineGeometry元素的方法比较复杂),直接在后台页面类中定义十字交叉线成员lineX,lineY,linegroup;并在构造函数中初始化十字交叉线。

  2. 在上一篇中,我们的鼠标响应函数都是针对图像控件的,但是在这一篇中,由于十字交叉线是跟随鼠标的,图像控件响应鼠标的函数都不能被触发,所以我们将鼠标响应函数移接到Grid控件上,鼠标在Gird工作区都能够响应。

  3. 由于Grid中包含了ScrollViewer,而此处的ScrollViewer功能又是被禁用的,从而导致滚轮事件无法触发。对于这种嵌套式滚动(一个滚动控件嵌套另一个滚动控件)我们在页面的构造函数中添加滚轮事件的冒泡。

  4. 在鼠标位置判定上,使用控件的IsMouseOver属性代替空控件判断鼠标是否在图像区域

  5. 在鼠标移动的响应函数中,增加十字交叉线的刷新和清除功能。

  6. 在Grid控件中增加鼠标离开响应函数,实现离开时清除十字交叉线

效果如下:

WPF跟随图像的十字线

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值