首页 > 编程语言 >【C#】WPF自定义Image实现图像缩放、平移

【C#】WPF自定义Image实现图像缩放、平移

时间:2024-08-02 09:40:58浏览次数:14  
标签:scaler 自定义 缩放 C# image ImageContainer transer Part start

1.xaml实现

<UserControl
    x:Class="HalconDemo.ImageUserControl"
    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:local="clr-namespace:HalconDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch"
    d:DesignHeight="1024"
    d:DesignWidth="1920"
    mc:Ignorable="d">
    <Grid>
        <ContentControl
            Name="Part_ImageContainer"
            MaxWidth="1920"
            MaxHeight="1024"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch"
            ClipToBounds="True"
            Cursor="Hand">
            <Image
                Name="image"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                RenderOptions.BitmapScalingMode="NearestNeighbor">
                <Image.RenderTransform>
                    <TransformGroup x:Name="group">
                        <ScaleTransform x:Name="scaler" />
                        <TranslateTransform x:Name="transer" />
                    </TransformGroup>
                </Image.RenderTransform>
            </Image>
        </ContentControl>
    </Grid>
</UserControl>

2.cs实现

/// <summary>
/// ImageUserControl.xaml 的交互逻辑
/// </summary>
public partial class ImageUserControl : UserControl
{
    /// <summary>
    /// 鼠标是否按下
    /// </summary>
    private bool mouseDown;


    /// <summary>
    /// 移动图片前,按下鼠标左键时,鼠标相对于Part_ImageContainer的点
    /// </summary>
    Point moveStart;

    public ImageUserControl()
    {
        InitializeComponent();
    }

    ~ImageUserControl()
    {
        Part_ImageContainer.MouseLeftButtonDown -= ImgMouseLeftButtonDown;
        Part_ImageContainer.MouseMove -= ImgMouseMove;
        Part_ImageContainer.MouseUp -= ImgMouseUp;
    }

    /// <summary>
    /// 初始化加载图像
    /// </summary>
    /// <param name="filePath">图像路径</param>
    /// <param name="IsMove">是否移动,true是,false否</param>
    public void InitImage(string filePath,bool IsMove = true)
    {
        if (IsMove)
        {
            Part_ImageContainer.MouseLeftButtonDown += ImgMouseLeftButtonDown;
            Part_ImageContainer.MouseMove += ImgMouseMove;
            Part_ImageContainer.MouseUp += ImgMouseUp;
        }
        image.Source = new BitmapImage(new Uri(filePath, UriKind.Absolute));
    }

    private void ImgMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        //尝试将鼠标强制捕获到此元素
        Part_ImageContainer.CaptureMouse();
        mouseDown = true;
        moveStart = e.GetPosition(Part_ImageContainer);
    }

    private void ImgMouseMove(object sender, MouseEventArgs e)
    {
        var mouseEnd = e.GetPosition(Part_ImageContainer);           // 鼠标移动时,获取鼠标相对图片容器的点
        if (mouseDown)
        {
            if (e.LeftButton == MouseButtonState.Pressed)           // 按下鼠标左键,移动图片
            {
                DoMove(mouseEnd);
            }
        }
    }

    private void ImgMouseUp(object sender, MouseButtonEventArgs e)
    {
        Part_ImageContainer.ReleaseMouseCapture();
        mouseDown = false;
    }

    private void ImgMouseWheel(object sender, MouseWheelEventArgs e)
    {
        var point = e.GetPosition(image);
        double delta = e.Delta * 0.002;
        DoScale(point, delta);
    }
    /// <summary>
    /// 缩放图片。最小为0.1倍,最大为30倍
    /// </summary>
    /// <param name="point">相对于图片的点,以此点为中心缩放</param>
    /// <param name="delta">缩放的倍数增量</param>
    private void DoScale(Point point, double delta)
    {
        // 限制最大、最小缩放倍数
        if (scaler.ScaleX + delta < 0.1 || scaler.ScaleX + delta > 30) return;

        scaler.ScaleX += delta;
        scaler.ScaleY += delta;

        transer.X -= point.X * delta;
        transer.Y -= point.Y * delta;
    }

    /// <summary>
    /// 移动图片
    /// </summary>
    /// <param name="moveEndPoint">移动图片的终点(相对于Part_ImageContainer)</param>
    private void DoMove(Point moveEndPoint)
    {
        // 考虑到旋转的影响,因此将两个点转换到Part_Image坐标系,计算x、y的增量
        Point start = Part_ImageContainer.TranslatePoint(moveStart, image);
        Point end = Part_ImageContainer.TranslatePoint(moveEndPoint, image);


        // 判断一下,如果scale很大的时候,移动会很迟缓。此时应该将移动放大
        if (scaler.ScaleX > 7)
        {
            transer.X += (end.X - start.X) * 4;
            transer.Y += (end.Y - start.Y) * 4;
        }
        else if (scaler.ScaleX > 5)
        {
            transer.X += (end.X - start.X) * 3;
            transer.Y += (end.Y - start.Y) * 3;
        }
        else if (scaler.ScaleX > 3)
        {
            transer.X += (end.X - start.X) * 2;
            transer.Y += (end.Y - start.Y) * 2;
        }
        else if (scaler.ScaleX < 0.5)
        {
            transer.X += (end.X - start.X) * 0.5;
            transer.Y += (end.Y - start.Y) * 0.5;
        }
        else
        {
            transer.X += (end.X - start.X);
            transer.Y += (end.Y - start.Y);
        }
        moveStart = moveEndPoint;

        // 以下代码,抄的https://blog.csdn.net/weixin_42975610/article/details/113741534

        // W+w > 2*move_x > -((2*scale-1)*w + W)  水平平移限制条件
        // H+h > 2*move_y > -((2*scale-1)*h + H)  垂直平移限制条件

        if (transer.X * 2 > image.ActualWidth + Part_ImageContainer.ActualWidth - 20)
            transer.X = (image.ActualWidth + Part_ImageContainer.ActualWidth - 20) / 2;

        if (-transer.X * 2 > (2 * scaler.ScaleX - 1) * image.ActualWidth + Part_ImageContainer.ActualWidth - 20)
            transer.X = -((scaler.ScaleX - 0.5) * image.ActualWidth + Part_ImageContainer.ActualWidth / 2 - 10);

        if (transer.Y * 2 > image.ActualHeight + Part_ImageContainer.ActualHeight - 20)
            transer.Y = (image.ActualHeight + Part_ImageContainer.ActualHeight - 20) / 2;

        if (-transer.Y * 2 > (2 * scaler.ScaleY - 1) * image.ActualHeight + Part_ImageContainer.ActualHeight - 20)
            transer.Y = -((scaler.ScaleY - 0.5) * image.ActualHeight + Part_ImageContainer.ActualHeight / 2 - 10);
    }

    /// <summary>
    /// 图像缩放
    /// </summary>
    /// <param name="point">当前坐标</param>
    /// <param name="scale">缩放倍数</param>
    public void ImageScale(Point point, double scale)
    {
        DoScale(point, scale);
    }
}

3.调用

//控件引用
 <Canvas
     x:Name="canvas"
     Grid.Row="1"
     Background="Black">

     <HalconDemo:ImageUserControl
         x:Name="ImageUserControl"
         Grid.Row="1"
         Width="{Binding ElementName=canvas, Path=ActualWidth}"
         Height="{Binding ElementName=canvas, Path=ActualHeight}"
         MouseWheel="ImageUserControl_MouseWheel" />

 </Canvas>
//鼠标滚动缩放
 private void ImageUserControl_MouseWheel(object sender, MouseWheelEventArgs e)
 {
     var point = e.GetPosition(image);
     double delta = e.Delta * 0.002;
     ImageUserControl.ImageScale(point, delta);
 }

标签:scaler,自定义,缩放,C#,image,ImageContainer,transer,Part,start
From: https://www.cnblogs.com/qiutian-hao/p/18338034

相关文章

  • 利用 Oracle 19c 新特性 from service 修复备库 GAP
    转发自:https://mp.weixin.qq.com/s/Jz8lEQ6QAnjoTeErbX0q_g前 言相信我们DBA在OracleDataGuard环境中遇到过因主库归档空间有限,归档日志又没有备份,空间满的时候直接删除了归档,导致丢失归档日志,而备库还没有及时接收到这个归档,导致备库出现了GAP现象。因为日志的中断,备......
  • .NET 7 GC垃圾回收
    垃圾回收(GarbageCollection,简称GC)是现代编程语言中一个至关重要的机制,它帮助开发者管理内存,防止内存泄漏,提升应用程序的稳定性和性能。在.NET7中,垃圾回收机制得到了进一步的优化和改进。本文将深入探讨.NET7中的GC垃圾回收,了解其工作原理、改进之处以及在实际应用中的影......
  • 企业微信 启动错误 0xc0000142
    废话不多说,上教程。......
  • AcWing3302. 表达式求值
    代码解释while(j<str.size()&&isdigit(str[j])){x=x*10+str[j++]-'0';}把字符串中里面连续的数字转化为int类型变量,比如输入996/332+8,正常的挨个字符扫描只能扫到’9’,‘9’,‘6’,但是按照上面代码的算法是重新开了一个循......
  • 使用 $fetch 进行 HTTP 请求
    title:使用$fetch进行HTTP请求date:2024/8/2updated:2024/8/2author:cmdragonexcerpt:摘要:文章介绍了Nuxt3中使用\(fetch进行HTTP请求的方法,它是基于ofetch库,支持SSR和自动缓存。\)fetch简化了HTTP请求,支持GET、POST等,可结合useAsyncData或useFetch优化数据获取,避......
  • 比较不同的excel,或者同一个excel的两个不同sheet页,并将不同之处标红,python代码实现
    importopenpyxlfromopenpyxl.stylesimportPatternFill#对比两个sheet,数据一致性校验#获取sheet对象的某一行defgetRow(sheet,rowNo):try:rows=[]forrowinsheet.iter_rows():rows.append(row)returnrows[rowNo-......
  • Linux 命令,cat说明与使用
    1:grep命令功用:读一个或多个文件并显示到标准输出上,如果没有指定files或是指定一个文件,则从标准输入读取,输入以EOF结束。可以使用操作符>把几个文件组合为一个新文件,或者是使用>>在已经存在的文件里添加文件内容。2:命令构件:cat[options][files]3:参数选项:-A,--show-all......
  • 【网络安全】LockBit病毒入侵揭秘:如何防范与应对
    文章目录前言主要特征攻击手段演进历程主要威胁防范与对策==如何入门学习网络安全【黑客】==【----帮助网安学习,以下所有学习资料文末免费领取!----】大纲学习教程面试刷题资料领取前言在数字时代,随着科技的飞速发展,网络安全问题愈发凸显。恶意软件和勒索软件等网络......
  • PCDN技术如何提高数据传输的可靠性?
    PCDN技术通过以下方式提高数据传输的可靠性:1.负载均衡与故障转移:PCDN系统具备负载均衡的能力,可以根据节点的负载情况动态分配请求,避免单点故障和过载情况。此外,当某个节点发生故障时,PCDN可以迅速将流量转移到其他可用节点,确保数据传输的连续性。2.数据备份与冗余:为了提......
  • Android 11.0 Launcher修改density禁止布局改变功能实现
    1.前言在11.0的系统rom定制化开发中,在关于Launcher3的定制化功能中,在有些功能需要要求改变系统原有的density屏幕密度,这样就会造成Launcher3的布局变化,所以就不符合要求,接下来就来看下如何禁止改变density造成Launcher3布局功能改变的实现2.Launcher修改density禁止布局改......