图片预览Image、加载、放缩、拖动
Image属性Stretch
Stretch | 效果 | 备注 |
---|---|---|
None | 图片原始大小 | 图片从原点(左上角)开始展示 |
Fill | 图片填满控件 | 图片不成比例 |
Uniform | 图片成比例尽可能填满控件 | 控件上下或者左右两侧可能有空白 |
UniformToFill | 图片成比例填满控件 | 图片的右下两侧有可能被裁剪 |
XAML
<UserControl.Resources>
<Storyboard x:Key="MsgShowStory" Storyboard.TargetName="msgBorder">
<DoubleAnimation From="1" To="0" Duration="0:0:1.5" Storyboard.TargetProperty="Opacity"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Duration="0:0:4">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0" >
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:1.5" >
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid ClipToBounds="True" MouseWheel="Grid_MouseWheel" SizeChanged="Grid_SizeChanged" MouseLeave="GridMain_MouseLeave">
<Grid x:Name="gridCanvas"
Width="{Binding ElementName=image,Path=ActualWidth}"
Height="{Binding ElementName=image,Path=ActualHeight}"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Image x:Name="image" Stretch="Uniform"
MouseLeftButtonDown="Image_MouseLeftButtonDown" MouseLeftButtonUp="Image_MouseLeftButtonUp" MouseMove="Image_MouseMove"/>
</Grid>
<Border x:Name="msgBorder" Width="100" Height="62" Background="WhiteSmoke" CornerRadius="5" Opacity="1" IsHitTestVisible="False" Visibility="Collapsed">
<TextBlock x:Name="msgTextBlock" Text="100%" Foreground="Gray" FontWeight="Bold" FontSize="22" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
cs
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
public partial class UC_ImageDisplay : UserControl
{
/// <summary>
/// 原图大小
/// </summary>
private Size _imageSize;
/// <summary>
/// 界面大小
/// </summary>
private Size _ucSize;
/// <summary>
/// 最小缩放比例,不小于Mini(原始尺寸,当前容器)*50%
/// </summary>
private double _minScale;
/// <summary>
/// 最大缩放比例
/// </summary>
private double _maxScale;
/// <summary>
/// 初始默认缩放比
/// </summary>
private double _defaultScale;
/// <summary>
/// 画布相对于原图缩放比例
/// </summary>
private double _zoomScale;
/// <summary>
/// 鼠标滚动一次(120)代表的缩放大小
/// </summary>
private const double _scrollValue = 0.2;
/// <summary>
/// 是否开始拖动鼠标
/// </summary>
private bool _enableMove;
/// <summary>
/// 鼠标拖动的起点
/// </summary>
private Point _ptStart;
/// <summary>
/// 鼠标起点时的边距
/// </summary>
private Point _ptMargin;
public UC_ImageDisplay()
{
InitializeComponent();
}
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
// 释放资源
BindingOperations.ClearBinding(image, System.Windows.Controls.Image.SourceProperty);
}
// 图片拖动
// 图片居中
// 图片加载
}
图片拖动
private void Grid_MouseWheel(object sender, MouseWheelEventArgs e)
{
//计算新的缩放比
double zoomScale = Math.Min(Math.Max(_zoomScale + (_scrollValue * e.Delta / 120), _minScale), _maxScale);
//新的缩放比跟旧的一样的话就不再浪费资源进行处理了
if (_zoomScale == zoomScale)
{
return;
}
Point ptUI = e.GetPosition(this);//获取鼠标点相对于UI界面的坐标
Point ptMap = e.GetPosition(gridCanvas);//获取鼠标点相对于缩放前画布的坐标
Point ptImage = new Point(ptMap.X / _zoomScale, ptMap.Y / _zoomScale);//获取鼠标点相对于原图的坐标
Point ptMapNew = new Point(ptImage.X * zoomScale, ptImage.Y * zoomScale);//计算鼠标点相对于缩放后画布的坐标
Console.WriteLine($"=============================");
Console.WriteLine($"图像大小是:长:{gridCanvas.Width / _zoomScale},宽:{gridCanvas.Height / _zoomScale}");
Console.WriteLine($"Map缩放前大小是:长:{gridCanvas.Width },宽:{gridCanvas.Height}");
Console.WriteLine($"Map缩放后大小是:长:{gridCanvas.Width * zoomScale / _zoomScale},宽:{gridCanvas.Height * zoomScale / _zoomScale}");
Console.WriteLine($"缩放点的UI坐标是:X:{ptUI.X },Y:{ptUI.Y }");
Console.WriteLine($"缩放点的真实坐标是:X:{ptImage.X},Y:{ptImage.Y }");
Console.WriteLine($"缩放点的在Map上的原坐标是:X:{ptMap.X },Y:{ptMap.Y }");
Console.WriteLine($"缩放点的在Map上缩放后的坐标是,X:{ptMapNew.X },Y:{ptMapNew.Y }");
Console.WriteLine($"MapMargin,X:{gridCanvas.Margin.Left },Y:{gridCanvas.Margin.Top }");
//计算缩放后画布的偏移量
if (zoomScale == _defaultScale)
{
double left = (_ucSize.Width - (_imageSize.Width * zoomScale)) / 2;
double top = (_ucSize.Height - (_imageSize.Height * zoomScale)) / 2;
Console.WriteLine($"Margin after,X:{left},Y:{top}");
gridCanvas.Margin = new Thickness(left, top, 0, 0);
}
else
{
double left = (ptMapNew.X - ptMap.X) * (-1);
double top = (ptMapNew.Y - ptMap.Y) * (-1);
Console.WriteLine($"Margin after,X:{left},Y:{top}");
// 画布大小
double width = gridCanvas.Width * zoomScale / _zoomScale;
double height = gridCanvas.Height * zoomScale / _zoomScale;
if (zoomScale > _zoomScale) // 放大
{
// 鼠标位置相对不动
left += gridCanvas.Margin.Left;
top += gridCanvas.Margin.Top;
if (width <= this.ActualWidth)
{
left = (this.ActualWidth - width) / 2;
}
if (height <= this.ActualHeight)
{
top = (this.ActualHeight - height) / 2;
}
}
else
{
// 缩小时新边距过大
if (left + width >= this.ActualWidth)
{
left = (this.ActualWidth - width) / 2;
}
if (top + height >= this.ActualHeight)
{
top = (this.ActualHeight - height) / 2;
}
}
gridCanvas.Margin = new Thickness(left, top, 0, 0);
}
SetImageZoomScale(zoomScale);
}
private void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_enableMove = true;
//获取鼠标点相对于UI界面的坐标
_ptStart = e.GetPosition(this);
_ptMargin.X = gridCanvas.Margin.Left;
_ptMargin.Y = gridCanvas.Margin.Top;
}
private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_enableMove = false;
}
private void GridMain_MouseLeave(object sender, MouseEventArgs e)
{
_enableMove = false;
}
private void Image_MouseMove(object sender, MouseEventArgs e)
{
if (_enableMove)
{
//计算新的缩放比
double zoomScale = Math.Min(Math.Max(_zoomScale, _minScale), _maxScale);
if (IsEqual(zoomScale, _defaultScale))
{
return;
}
Point ptUI = e.GetPosition(this);//获取鼠标点相对于UI界面的坐标
Point ptMap = e.GetPosition(gridCanvas);//获取鼠标点相对于缩放前画布的坐标
Point ptImage = new Point(ptMap.X / _zoomScale, ptMap.Y / _zoomScale);//获取鼠标点相对于原图的坐标
Point ptMapNew = new Point(ptImage.X * zoomScale, ptImage.Y * zoomScale);//计算鼠标点相对于缩放后画布的坐标
Console.WriteLine($"=============================");
Console.WriteLine($"图像大小是:长:{gridCanvas.Width / _zoomScale},宽:{gridCanvas.Height / _zoomScale} 画板:{this.ActualWidth} X {this.ActualHeight}");
Console.WriteLine($"Map缩放前大小是:长:{gridCanvas.Width },宽:{gridCanvas.Height}");
Console.WriteLine($"Map缩放后大小是:长:{gridCanvas.Width * zoomScale / _zoomScale},宽:{gridCanvas.Height * zoomScale / _zoomScale}");
Console.WriteLine($"缩放点的UI坐标是:X:{ptUI.X },Y:{ptUI.Y }");
Console.WriteLine($"缩放点的真实坐标是:X:{ptImage.X},Y:{ptImage.Y }");
Console.WriteLine($"缩放点的在Map上的原坐标是:X:{ptMap.X },Y:{ptMap.Y }");
Console.WriteLine($"缩放点的在Map上缩放后的坐标是,X:{ptMapNew.X },Y:{ptMapNew.Y }");
Console.WriteLine($"MapMargin,X:{gridCanvas.Margin.Left },Y:{gridCanvas.Margin.Top }");
double xOffset = _ptMargin.X + (ptUI.X - _ptStart.X);
double yOffset = _ptMargin.Y + (ptUI.Y - _ptStart.Y);
SetImageMargin(xOffset, yOffset);
}
}
/// <summary>
/// 鼠标拖动时画布相对界面的偏移量
/// </summary>
private void SetImageMargin(double left, double top)
{
double blankWidth = this.ActualWidth - gridCanvas.Width;
double blankHeight = this.ActualHeight - gridCanvas.Height;
// 如果留有空白
if (blankWidth >= 0)
{
left = blankWidth / 2;
}
else
{
// 边界判断
if (left >= 0)
{
left = 0;
}
else if (left <= blankWidth)
{
left = blankWidth;
}
}
if (blankHeight >= 0)
{
top = blankHeight / 2;
}
else
{
if (top >= 0)
{
top = 0;
}
else if (top <= blankHeight)
{
top = blankHeight;
}
}
_ = Dispatcher.BeginInvoke(new Action(() =>
{
gridCanvas.Margin = new Thickness(left, top, 0, 0);
}));
}
图片居中
private void Grid_SizeChanged(object sender, SizeChangedEventArgs e)
{
InitZoomScaleImageDispaly();
}
private void InitZoomScaleImageDispaly()
{
_ucSize = RenderSize;
CalculateZoomScale(_imageSize, _ucSize);
_defaultScale = _zoomScale;
// 居中显示
double left = (_ucSize.Width - (_imageSize.Width * _zoomScale)) / 2;
double top = (_ucSize.Height - (_imageSize.Height * _zoomScale)) / 2;
gridCanvas.Margin = new Thickness(left, top, 0, 0);
}
/// <summary>
/// 设置默认的缩放比、最小缩放比、最大缩放比
/// </summary>
/// <param name="imageSize">图像大小</param>
/// <param name="uiSize">待显示的界面大小</param>
/// <returns></returns>
private void CalculateZoomScale(Size imageSize, Size uiSize)
{
if (imageSize.Width == 0 || imageSize.Height == 0)
{
return;
}
if (uiSize.Width == 0 || uiSize.Height == 0)
{
return;
}
//取最大的压缩比例
double zoomScale = Math.Min(uiSize.Width / imageSize.Width, uiSize.Height / imageSize.Height);
//图片尺寸大于容器
if (zoomScale < 1)
{
if (zoomScale < 0.5)
{
//极大
_minScale = zoomScale;
_maxScale = Math.Floor(1 / zoomScale);
}
else
{
_minScale = zoomScale;
_maxScale = Math.Ceiling(2 / zoomScale);
}
}
//图片尺寸小于容器
else
{
if (zoomScale > 8)
{
//极小
_minScale = 1;
_maxScale = Math.Floor(0.5 * zoomScale);
zoomScale = _maxScale;
}
else if (zoomScale > 4)
{
//较小
_minScale = 1;
_maxScale = Math.Floor(0.75 * zoomScale);
zoomScale = _maxScale;
}
else
{
_minScale = 1;
_maxScale = Math.Ceiling(zoomScale);
zoomScale = 1;
}
}
SetImageZoomScale(zoomScale);
}
/// <summary>
/// 画布相对于原图缩放比例
/// </summary>
private void SetImageZoomScale(double zoomScale)
{
if (zoomScale <= 0)
{
return;
}
_zoomScale = zoomScale;
_ = Dispatcher.BeginInvoke(new Action(() =>
{
//计算缩放后图片的大小
image.Width = _imageSize.Width * zoomScale;
image.Height = _imageSize.Height * zoomScale;
msgTextBlock.Text = Math.Round(zoomScale * 100) + "%";
Storyboard storyboard = FindResource("MsgShowStory") as Storyboard;
storyboard.Begin();
}));
}
Image绑定图片
/// <summary>
/// 从字节数组加载图片
/// </summary>
/// <param name="bytes"></param>
public void LoadLayer(byte[] bytes)
{
BitmapImage bitmap = GenerateBitmapImage(bytes);
LoadLayer(bitmap);
}
/// <summary>
/// 加载底图(Uniform缩小或最佳比例放大)
/// </summary>
/// <param name="bmp"></param>
/// <param name="mouseOperateType"></param>
public void LoadLayer(BitmapImage bmp)
{
BindingOperations.ClearBinding(image, Image.SourceProperty);
image.Source = bmp;
_imageSize = new Size(bmp.PixelWidth, bmp.PixelHeight);
InitZoomScaleImageDispaly();
}
IsEqual 浮点数double相等性比较
Uri路径引用
XAML
<Image x:Name="Image1" Source="pack://application:,,,/Image/nopicture.jpg"/>
<Image x:Name="Image2" Source="pack://application:,,,/CurrentProjectName;component/Image/nopicture.jpg"/>
cs
// 默认绝对路径
Image1.Source = new BitmapImage(new Uri("pack://application:,,,/Image/nopicture.jpg"));
Image2.Source = new BirmapImage(new Uri(“pack://application:,,,/CurrentProjectName;component/Image/nopicture.jpg”));
// 相对路径
Image1.Source = new BitmapImage(new Uri("\\Image\\nopicture.jpg", UriKind.Relative));
// 引用dll中的图片
Image2.Source = new BitmapImage(new Uri(“ImageLibiary;component/Image/nopicture.jpg”, UriKind.Relavite));