图片预览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));

posted @ 2021-04-28 14:35  wesson2019  阅读(471)  评论(0编辑  收藏  举报