在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();
}

}
}
...全文
600 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
howze 2020-08-31
  • 打赏
  • 举报
回复
引用 1 楼 耗子哭死猫 的回复:
可以放在Canvas里面,Canvas嵌套Canvas
嵌套也试过了,还是一样的不行
耗子哭死猫 2020-08-31
  • 打赏
  • 举报
回复
可以放在Canvas里面,Canvas嵌套Canvas
下载代码方式:https://pan.quark.cn/s/dd3561eca308 在软件开发领域,面向对象编程(OOP)是一种普遍采纳的结构化方法,它使得开发者能够借助模拟现实环境的实体和关系来构建软件系统。在本案例,我们观察到的是一个关于借助抽象类来执行不同几何图形面积求解的实践应用。现在,让我们详细分析这一议题。 标题 "应用抽象类计算面积" 清晰地表明我们将要讨论一个抽象类,此类设定了一个用于测量图形面积的标准函数,但并未提供实际的执行过程。抽象类在诸如C#或Java等编程语言通常借助`abstract`修饰符进行声明,它们无法直接创建对象实例,仅能作为其他类的基础模板。 描述部分提及的"图形界面应用"暗示这是一个基于视觉用户界面(GUI)的系统,可能运用了.NET Framework的Windows Forms或WPF技术,或者是Java平台的Swing或JavaFX框架。在这样环境下,用户能够通过视觉元素与这些几何体进行互动,例如输入相关尺寸并观看到计算得出的面积值。 抽象类“几何体”内嵌了“计算面积”这一抽象函数。在代码层面,这可以被表述为: ```csharp public abstract class GeometricShape { public abstract double CalculateArea(); } ``` 随后,有三个派生类:圆(Circle)、矩形(Rectangle)和三角形(Triangle),它们各自提供了这个抽象函数的具体实现。比如,圆的面积是通过π乘以半径的平方得到的,矩形的面积是长和宽的乘积,而三角形的面积可能是底乘以高再除以2的结果。这些类将提供具体实现来计算它们各自的面积: ```csharp p...

111,129

社区成员

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

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

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