在WPF中如何使用scrollviewer放大图片并通过鼠标画出选区,保存这个选区到一张图片?

howze 2020-08-31 02:03:22
需求是这样的,我在做一个图片查看器
其中,使用了scrollviewer来放大或者缩小图片
这一步我已经实现了
然后在图片上,绘制矩形选取,这一步我也实现了
但是接下来遇到两个问题:
1、因为我的矩形选区是放在scrollviewer里的,导致随着图片放大缩小,矩形的边框会变粗或者变细。(不是我想把矩形选区是放在scrollviewer里,而是不放在里面就无法在图片上画选区)
2、我无法截取矩形选取内的图片,截取后是空白的,
以下是代码,请问该如何解决,谢谢!

xaml:

<Window x:Class="截图.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:截图"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Orientation="Vertical" HorizontalAlignment="Left" Minimum="1" x:Name="slider"/>


<ScrollViewer Name="scrollViewer" Grid.Column="1"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Visible" Margin="0,0,0,40">

<Grid Name="grid" Width="400" Height="400" >
<Grid.LayoutTransform>
<TransformGroup x:Name="TfGroup">
<ScaleTransform x:Name="scaleTransform"/>
</TransformGroup>
</Grid.LayoutTransform>

<Image x:Name="img" Source="C:\Users\howze\Desktop\20200728104010.png" />

<Canvas x:Name="canvas" MouseDown="Canvas_MouseDown" MouseMove="Canvas_MouseMove" MouseUp="Canvas_MouseUp" Background="Transparent"/>
</Grid>
</ScrollViewer>

<Button Grid.Column="1" Content="Capture" Margin="338,381,337.333,9.667" Click="Button_Click"/>
</Grid>
</Window>




后台代码:



public partial class MainWindow : Window
{
Point? lastCenterPositionOnTarget;
Point? lastMousePositionOnTarget;
Point? lastDragPoint;

private System.Windows.Point startPoint;
private System.Windows.Shapes.Rectangle rect;

private int w1;
private int h1;

public MainWindow()
{
InitializeComponent();

scrollViewer.ScrollChanged += OnScrollViewerScrollChanged;

scrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;

slider.ValueChanged += OnSliderValueChanged;

}

void OnScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
{
Point? targetBefore = null;
Point? targetNow = null;

if (!lastMousePositionOnTarget.HasValue)
{
if (lastCenterPositionOnTarget.HasValue)
{
var centerOfViewport = new Point(scrollViewer.ViewportWidth / 2,
scrollViewer.ViewportHeight / 2);
Point centerOfTargetNow =
scrollViewer.TranslatePoint(centerOfViewport, grid);

targetBefore = lastCenterPositionOnTarget;
targetNow = centerOfTargetNow;
}
}
else
{
targetBefore = lastMousePositionOnTarget;
targetNow = Mouse.GetPosition(grid);

lastMousePositionOnTarget = null;
}

if (targetBefore.HasValue)
{
double dXInTargetPixels = targetNow.Value.X - targetBefore.Value.X;
double dYInTargetPixels = targetNow.Value.Y - targetBefore.Value.Y;

double multiplicatorX = e.ExtentWidth / grid.Width;
double multiplicatorY = e.ExtentHeight / grid.Height;

double newOffsetX = scrollViewer.HorizontalOffset -
dXInTargetPixels * multiplicatorX;
double newOffsetY = scrollViewer.VerticalOffset -
dYInTargetPixels * multiplicatorY;

if (double.IsNaN(newOffsetX) || double.IsNaN(newOffsetY))
{
return;
}

scrollViewer.ScrollToHorizontalOffset(newOffsetX);
scrollViewer.ScrollToVerticalOffset(newOffsetY);
}
}
}


private void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
lastMousePositionOnTarget = Mouse.GetPosition(grid);

System.Windows.Point centerPoint = e.GetPosition(img);

var val = e.Delta * 0.01; //描述鼠标滑轮滚动

if (scaleTransform.ScaleX + val < 0.1) return;
if (scaleTransform.ScaleX + val > 100) return;

scaleTransform.CenterX = centerPoint.X;
scaleTransform.CenterY = centerPoint.Y;

scaleTransform.ScaleX += val;
scaleTransform.ScaleY += val;


if (e.Delta > 0)
{
slider.Value += 1;
}
if (e.Delta < 0)
{
slider.Value -= 1;
}

e.Handled = true;
}

private void OnSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
scaleTransform.ScaleX = e.NewValue;
scaleTransform.ScaleY = e.NewValue;

var centerOfViewport = new System.Windows.Point(scrollViewer.ViewportWidth / 2, scrollViewer.ViewportHeight / 2);

lastCenterPositionOnTarget = scrollViewer.TranslatePoint(centerOfViewport, grid);
}

private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
canvas.Children.Clear();

startPoint = e.GetPosition(canvas);

//MessageBox.Show(startPoint.ToString());

rect = new System.Windows.Shapes.Rectangle
{
Stroke = System.Windows.Media.Brushes.IndianRed,
StrokeThickness = 0.2,
StrokeDashArray = new DoubleCollection { 2 }
};
Canvas.SetLeft(rect, startPoint.X);
Canvas.SetTop(rect, startPoint.Y);
canvas.Children.Add(rect);
}

private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released || rect == null)
return;

var pos = e.GetPosition(canvas);

var x = Math.Min(pos.X, startPoint.X);
var y = Math.Min(pos.Y, startPoint.Y);

var w = Math.Max(pos.X, startPoint.X) - x;
var h = Math.Max(pos.Y, startPoint.Y) - y;

w1 = (int)w;
h1 = (int)h;

rect.Width = w;
rect.Height = h;

Canvas.SetLeft(rect, x);
Canvas.SetTop(rect, y);
}

private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
{

}

private void Button_Click(object sender, RoutedEventArgs e)
{

RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
96,
96,
PixelFormats.Default);

targetBitmap.Render(canvas);

using (FileStream tFileStream = new FileStream(@"C:\Users\howze\Desktop\test.jpg",
FileMode.Create, FileAccess.Write))
{

PngBitmapEncoder tPngBitmapEncoder = new PngBitmapEncoder();
tPngBitmapEncoder.Interlace = PngInterlaceOption.On;
tPngBitmapEncoder.Frames.Add(BitmapFrame.Create(targetBitmap));
tPngBitmapEncoder.Save(tFileStream);
tFileStream.Close();
}

}
}
...全文
467 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
howze 2020-08-31
  • 打赏
  • 举报
回复
引用 1 楼 耗子哭死猫 的回复:
可以放在Canvas里面,Canvas嵌套Canvas
嵌套也试过了,还是一样的不行
耗子哭死猫 2020-08-31
  • 打赏
  • 举报
回复
可以放在Canvas里面,Canvas嵌套Canvas
在Windows系统,notepad.exe(记事本)是一个“经典的”、“简洁的”文本编辑器。这个软件,没有华丽的外观,也没有繁杂的功能,仅仅是一个文本编辑小软件。虽然经过Windows系统数十年的变换,但它却保持着永恒姿态,数十年来几乎不曾改变过。曾经,VS的经典DEMO,就有它的身影,一个新建的项目,就藏有一个新建的“记事本”。然而,在WPF的项目,“记事本”却消失的无影无踪,也许是很容易实现,也许是为了革新,而不愿再传承“经典”。确实,使用WPF技术再次让“记事本”复活,确实也是一件非常容易的事情。但是,如果,使用WPF技术,再搭配当下非常流行的MVVM模式呢?复活“记事本”的难度却陡然上升至很多WPF程序员为之默默叹气。而,MVVM模式是掌握WPF的最顶级技术,MVVM模式拥有的无尽的优势,让WPF相对于过往的编程模式来说,是一种革命性的创新,从而也成为大WPF项目必须的模式。但,学习难度。。。。。。在这个《WPF记事本开发详解》的课程,赵老师带领你在WPF,从零开始一步步构建MVVM模式,直到让你亲自以WPF+MVVM的方式,让这个经典的“记事本”软件从你的手“复活”。在课程,赵老师会详细讲解WPF和MVVM的各种技巧,让你从此爱上WPF+MVVM编程。

110,566

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧