WPF头像裁剪

【WPF】图片剪裁-ImageCropping 机缘巧合吧,当时在全网寻找图像剪裁工具,但大都不能满足需求,于是决定动手写。当然如果只是为了完成这么一个功能就没有必要记录了,主要是不依赖与第三方图像库,且实现方式简单,自我感觉很有纪念意义。 阅读详情

需求很常见,就是用户上传头像前进行固定大小的裁剪。
百度一番,找到几个差不多的,
其一 http://download.youkuaiyun.com/detail/tianhaosen/7159901,这个的实现方式是截图框大小固定不变,背景图可以通过鼠标拖动和鼠标滚轮缩放,经过测试,这个对图片的裁剪不是很准确,尤其是大图或者靠近图片边缘裁剪的时候会出现较大误差,然后我尝试调整了下截图位置的算法,但多少还是有偏差,无奈只好放弃,有兴趣的同学可以下下来研究研究。
其二 http://blog.youkuaiyun.com/jtl309/article/details/50651911,这个的实现方式是背景图片保持大小不变,裁剪框可以通过鼠标拖动进行缩放。于是尝试在这个的基础上进行调整:)
首先,裁剪框的拖动以及缩放作者已经做的很完善了,主要是利用了Thumb控件进行自定义。我的需求是裁剪框要是一个固定大小的正方形,所以我对作者的源码进行了修改。源码中作者是在DragHelperBase.cs中对矩形框的大小进行实时计算,其中有4个ResizeFrom**方法,我在这4个方法的入参中均加入了宽高(out double HeightNew,out double WidthNew),并且在方法的最后一行赋值高和宽相等,这样就保证了裁剪框始终是正方形,另外,作者的这个框框拖动的时候能把框拖到大边框以外,是个小bug,我这边在每次拖动之后都跟Parent.ActualWidth比较一下。ok,算法代码修改完毕,下面就是对样式代码进行了一些调整,这个各位同学根据自己需求改动即可。
主要修改了DragHelperBase.cs代码:

   #region ResizeElement
        private Rect ResizeElement(CustomThumb HitedThumb, double HorizontalChange, double VerticalChange)
        {
            #region Get Old Value

            if (HitedThumb == null) return Rect.Empty;


            Rect TargetActualBound = GetTargetActualBound();

            double TopOld    = CorrectDoubleValue(TargetActualBound.Y);
            double LeftOld   = CorrectDoubleValue(TargetActualBound.X);
            double WidthOld  = CorrectDoubleValue(TargetActualBound.Width);
            double HeightOld = CorrectDoubleValue(TargetActualBound.Height);

            double TopNew    = TopOld;
            double LeftNew   = LeftOld;
            double WidthNew  = WidthOld;
            double HeightNew = HeightOld;

            #endregion

            if (HitedThumb.DragDirection == DragDirection.TopLeft
                || HitedThumb.DragDirection == DragDirection.MiddleLeft
                || HitedThumb.DragDirection == DragDirection.BottomLeft)
            {
                ResizeFromLeft(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, HorizontalChange, out LeftNew, out WidthNew, out HeightNew);
            }

            if (HitedThumb.DragDirection == DragDirection.TopLeft
                || HitedThumb.DragDirection == DragDirection.TopCenter
                || HitedThumb.DragDirection == DragDirection.TopRight)
            {
                ResizeFromTop(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, VerticalChange, out TopNew, out HeightNew, out WidthNew);
            }

            if (HitedThumb.DragDirection == DragDirection.TopRight
                || HitedThumb.DragDirection == DragDirection.MiddleRight
                || HitedThumb.DragDirection == DragDirection.BottomRight)
            {
                ResizeFromRight(DragHelperParent, LeftOld, WidthOld,TopOld,HeightOld, HorizontalChange, out WidthNew, out HeightNew);
            }

            if (HitedThumb.DragDirection == DragDirection.BottomLeft
                || HitedThumb.DragDirection == DragDirection.BottomCenter
                || HitedThumb.DragDirection == DragDirection.BottomRight)
            {
                ResizeFromBottom(DragHelperParent, LeftOld, WidthOld, TopOld, HeightOld, VerticalChange, out HeightNew, out WidthNew);
            }

            WidthNew = WidthNew < 0 ? 0 : WidthNew;
            HeightNew = HeightNew < 0 ? 0 : HeightNew;

            this.Width = WidthNew;
            this.Height = HeightNew;
            Canvas.SetTop(this, TopNew);
            Canvas.SetLeft(this, LeftNew);

            return new Rect
            {
                X = LeftNew,
                Y = TopNew,
                Width = WidthNew,
                Height = HeightNew
            };
        }
        #endregion

        #region Resize Base Methods

        private static void CalcSize(double h,double w)
        {

        }

        #region ResizeFromTop

        private static void ResizeFromTop(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double VerticalChange, out double TopNew, out double HeightNew, out double WidthNew)
        {
            double MiniHeight = 10;

            double top = TopOld + VerticalChange;
            TopNew = ((top + MiniHeight) > (HeightOld + TopOld)) ? HeightOld + TopOld - MiniHeight : top;
            TopNew = TopNew < 0 ? 0 : TopNew;

            HeightNew = HeightOld + TopOld - TopNew;

            HeightNew = CorrectNewHeight(Parent, TopNew, HeightNew);
            double tmpWidth = WidthOld;
            if (LeftOld+HeightNew>Parent.ActualWidth)
            {
                HeightNew = tmpWidth;
                WidthNew = tmpWidth;
            }
            else
            {
                WidthNew = HeightNew;
                tmpWidth = HeightNew;
            }

        }
        #endregion

        #region ResizeFromLeft
        private static void ResizeFromLeft(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double HorizontalChange, out double LeftNew, out double WidthNew, out double HeightNew)
        {
            double MiniWidth = 10;
            double left = LeftOld + HorizontalChange;

            LeftNew = ((left + MiniWidth) > (WidthOld + LeftOld)) ? WidthOld + LeftOld - MiniWidth : left;

            LeftNew = LeftNew < 0 ? 0 : LeftNew;

            WidthNew = WidthOld + LeftOld - LeftNew;
            WidthNew = CorrectNewWidth(Parent, LeftNew, WidthNew);
            double tmpHeight = HeightOld;

            if (TopOld + WidthNew > Parent.ActualHeight)
            {
                WidthNew = tmpHeight;
                HeightNew = tmpHeight;
            }
            else
            {
                HeightNew = WidthNew;
                tmpHeight = WidthNew;
            }
        }
        #endregion

        #region ResizeFromRight
        private static void ResizeFromRight(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double HorizontalChange, out double WidthNew, out double HeightNew)
        {
            if (LeftOld + WidthOld + HorizontalChange < Parent.ActualWidth)
            {
                WidthNew = WidthOld + HorizontalChange;
            }
            else
            {
                WidthNew = Parent.ActualWidth - LeftOld;
            }
            if (TopOld + HeightOld + HorizontalChange < Parent.ActualHeight)
            {
                HeightNew = HeightOld + HorizontalChange;
            }
            else
            {
                HeightNew = Parent.ActualWidth - TopOld;
            }

            WidthNew = WidthNew < 0 ? 0 : WidthNew;
            if (WidthNew<HeightNew)
            {
                HeightNew = WidthNew;
            }
            else
            {
                WidthNew = HeightNew;
            }

        }
        #endregion

        #region ResizeFromBottom
        private static void ResizeFromBottom(FrameworkElement Parent, double LeftOld, double WidthOld, double TopOld, double HeightOld, double VerticalChange, out double HeightNew, out double WidthNew)
        {
            if (TopOld + HeightOld + VerticalChange < Parent.ActualWidth)
            {
                HeightNew = HeightOld + VerticalChange;
            }
            else
            {
                HeightNew = Parent.ActualWidth - TopOld;
            }
            if (LeftOld + WidthOld + VerticalChange < Parent.ActualWidth)
            {
                WidthNew = WidthOld + VerticalChange;
            }
            else
            {
                WidthNew = Parent.ActualWidth - LeftOld;
            }
            HeightNew = HeightNew < 0 ? 0 : HeightNew;
            if (WidthNew < HeightNew)
            {
                HeightNew = WidthNew;
            }
            else
            {
                WidthNew = HeightNew;
            }
        }
        #endregion

现在回到我们自己的项目,把上面修改过的工程生成的dll引用进来,

<Canvas x:Name="canvas" Grid.Row="1" Width="500" Height="500">
            <Rectangle Stroke="{StaticResource ButtonBackgroundBrush}" 
                       Fill="Transparent" StrokeThickness="2" 
                       Width="300" Height="300" Canvas.Left="100" Canvas.Top="100"
                       UICommon:DragControlHelper.IsEditable="True" 
                       UICommon:DragControlHelper.IsSelectable="True"/>
            <UICommon:DragControlHelper CornerWidth="6" Background="{StaticResource CancleButtonBackgroudBrush}" BorderBrush="{StaticResource ButtonBackgroundBrush}" DragChanging="DragControlHelper_DragChanging" DragCompleted="DragControlHelper_DragCompleted"/>
 </Canvas>

<StackPanel Grid.Row="2" HorizontalAlignment="Center" Orientation="Horizontal">
            <Button Content="取消" Name="btnCancel" Width="62" Height="32"  Foreground="#FFFFFF" Style="{StaticResource CustomButtonStyle}"
                        Margin="10" Click="btnCancel_Click" Background="{StaticResource CancleButtonBackgroudBrush}"></Button>
            <Button Content="确定" Name="btnConfirm" Width="62" Height="32"  Foreground="#FFFFFF" Style="{StaticResource CustomButtonStyle}"
                        Margin="10" Click="btnConfirm_Click" Background="{StaticResource ButtonBackgroundBrush}"></Button>
</StackPanel>
private void DragControlHelper_DragCompleted(object Sender, UICommon.Controls.DragChangedEventArgs e)
{
            newBound = e.NewBound;//拖动完成后的新位置
}

private void btnConfirm_Click(object sender, RoutedEventArgs e)
{
            Bitmap map = new Bitmap(choosedImagePath);
            int mapHeight = map.Height;
            int mapWidth = map.Width;//图片宽高
            int actualHeight = 0;
            int actualWidth = 0;//Canvas宽高
            int offset = 1;
            int left = 0;
            int top = 0;
            int x = (int)newBound.X;
            int y = (int)newBound.Y;
            if (mapHeight>mapWidth)//此时高度撑满500
            {
                actualHeight = 500;
                actualWidth = 500 * mapWidth / mapHeight;
                left = (500 - actualWidth) / 2;
                x -=  left;
                offset = mapHeight / 500d;//需要小数
            }
            else//此时宽度撑满500
            {
                actualWidth = 500;
                actualHeight = 500 * mapHeight / mapWidth;
                top = (500 - actualHeight) / 2;
                y -= top;
                offset = mapWidth / 500d;
            }

            //计算截图位置和大小
            int w= (int)Math.Round(newBound.Width * offset, MidpointRounding.AwayFromZero);
            int h = (int)Math.Round(newBound.Height * offset, MidpointRounding.AwayFromZero);
            int startX=(int)Math.Round( x * offset,MidpointRounding.AwayFromZero);
            int startY=(int)Math.Round( y * offset,MidpointRounding.AwayFromZero);

            var resultMap = KiCut(map,startX,startY, w, h);
            if (resultMap==null)
            {
                Alert alert = new Alert("图片裁剪失败,请重新选择图片。");
                alert.Owner = this;
                alert.ShowDialog();
                return;
            }
            string savePath = NIM.ToolsAPI.GetLocalAppDataDir() + ConfigHelper.GetSettingStr("appDataDir") + "\\CutImageTemp\\";
            if (!Directory.Exists(savePath))
            {
                Directory.CreateDirectory(savePath);
            }
            string name = "UserHead_" + DateTimeHelper.DatetimeConvertUnix() + ".jpg";
            string path=savePath + name;
            if (resultMap.Width!=300)//用户调整了裁剪框大小
            {
                Bitmap newImage = new Bitmap(300, 300, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                Graphics g = Graphics.FromImage(newImage);
                g.DrawImage(resultMap, 0, 0, 300, 300);
                g.Dispose();
                newImage.Save(path, ImageFormat.Jpeg);
            }
            else//直接保存
            {
                resultMap.Save(path, ImageFormat.Jpeg);
            }
}

private static Bitmap KiCut(Bitmap b, int StartX, int StartY, int iWidth, int iHeight)
{
            if (b == null)
            {
                return null;
            }
            try
            {
                Bitmap bmpOut = new Bitmap(iWidth, iHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                Graphics g = Graphics.FromImage(bmpOut);
                g.DrawImage(b, new System.Drawing.Rectangle(0, 0, iWidth, iHeight), new System.Drawing.Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);
                g.Dispose();
                return bmpOut;
            }
            catch(Exception ex)
            {
                return null;
            }
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
            BinaryReader binReader = new BinaryReader(File.Open(choosedImagePath, FileMode.Open));

            FileInfo fileInfo = new FileInfo(choosedImagePath);
            byte[] bytes = binReader.ReadBytes((int)fileInfo.Length);
            binReader.Close();

            bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.StreamSource = new MemoryStream(bytes);
            bitmap.EndInit();
            ImageBrush brush = new ImageBrush(bitmap);
            brush.Stretch = Stretch.Uniform;
            this.canvas.Background = brush;
}

稍微解释下代码,以上代码只是核心部分,另外newBound在构造函数中需要初始化new Rect(100, 100, 300, 300),因为我这边需要裁剪成300X300的图片,并且我在窗口的Load事件里给Canvas设置了背景图片,也就是用户选择的图片(路径存放在choosedImagePath变量中),并且设置其Stretch属性为Uniform,这样能保证图片在框中自适应正常显示。
虽然我这边默认框大小是300X300,但是一旦用户缩放了裁剪框,我们就需要对裁剪下来的图片进行放大或压缩,然后再做进一步操作(如转码base64、上传至服务器)。

WPF用户头像裁剪器 WPF实现头像裁剪WPF开发者QQ群: 340500857效果预览:代码如下:逻辑代码:更多教程欢迎关注微信公众号: WPF开发者QQ群: 340500857 前言 需要做一个用户选择头像进行裁剪后保存。 效果预览: 代码如下: <Grid> <Border x:Name="containerPanel"> <Canvas x:Name="DrawCanvas" VerticalAlignment 阅读详情

相关推荐

WPF圆形头像显示

不知何时开始,大多数软件的头像采用了圆形头像,使用WPF进行圆形头像显示比较简单,并不是将图像裁剪成圆形头像后显示. 思路是裁剪出一个正方形的头像,然后在显示的时候,只显示正方形中的内切圆部分即可.WPF中的Image类中有一个Clip属性,只需要设置好该属性即可.生成后的效果如下所示:

cpplog 1万+

WPF 容器裁剪

这里可以理解成,将Border以里面的路径进行裁切,上面这个形状大概是个胶囊。这样的话,无论在里面放什么元素,他都只能呈现胶囊形状。如果再WPF中开发UI,难免会碰到一些需要裁剪,掩盖等问题,我们不希望容器里面的东西比容器还大,喧宾夺主。,这样的裁切是通过裁剪掩码,直接不显示不在路径的区域,十分好用。但是有时候他并没有这么好用,比如裁切圆角,或者完全掩盖的时候。使用ImageBrush指定使用图片做为裁剪掩码。使用可视元素作为裁剪掩码,如Shape元素。

Poetindusk 854

WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)

不知从什么时候开始,头像流行使用圆形了,于是各个平台开始追逐显示圆形裁剪图像的技术。WPF 作为一个优秀的 UI 框架,当然有其内建的机制支持这种圆形裁剪。 不过,内建的机制仅支持画刷,而如果被裁剪的元素支持交互,或者拥有普通画刷无法达到的显示效果,那么就需要本文介绍的更加通用的解决方法了。 UWP 的圆形裁剪请左转参考:UWP 将图片裁剪成圆形(椭圆)。 WPF 的 UIElement...

walterlv - 吕毅 2582

WPF 实现裁剪图像

WPF 实现裁剪图像控件名:CropImage作 者:WPFDevelopersOrg - 驚鏵原文链接[1]:https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用.NET4 至 .NET6Visual Studio 2022使用 Canvas 展示选择的裁剪图片使用 4 个 Rectangle 设置未选中区域分别是左上右下中间展示当前...

dotNET跨平台 836

别再只用BorderRadius了!用WPF的Clip属性给你的Image控件做个异形头像框(附GeometryGroup实战)

本文深入探讨了WPF中Clip属性的强大功能,教你如何突破传统BorderRadius的限制,打造独特的异形头像框。通过GeometryGroup和CombinedGeometry的实战应用,展示了从基础圆形到复杂组合图形的实现方法,帮助开发者提升UI设计水平,创造更具吸引力的用户界面。

weixin_30430169的博客 294

WPF图片处理避坑指南:Image控件Stretch属性的4种模式详解(含效果对比图)

本文详细解析了WPF中Image控件Stretch属性的4种模式(None、Fill、Uniform、UniformToFill),通过效果对比图和实际代码示例,帮助开发者避免图片变形、比例失调等常见问题。针对用户头像、全屏背景、产品相册等典型场景提供最佳实践方案,并分享性能优化与高级应用技巧,是WPF图片处理的必备指南。

weixin_28365523的博客 137

WPF 图片头像自由剪切器实时截图细节放大器

在网上找了好久都找不到合适的截图框架,结果都不能满足我的需求,所以在大佬的基础上进行改进,开源实现图片头像自由剪切,实时截图,细节放大等功能 代码不是完整的,因为大多数代码属于公司的,我只能贡献我从开源项目那学到的,这也算是学习人家的开源精神

baoolong的博客 1517

C#与WPF实现图片裁剪功能

本文还有配套的精品资源,点击获取 简介:在C#编程中,图片裁剪功能是一项常见需求,尤其在照片编辑和图像处理软件等领域。本资源着重介绍如何利用WPF框架在C#环境下实现高效的图片裁剪,包括图形渲染、用户交互和性能优化的技术要点。开发者将通过Bitmap和Graphics类实现裁剪操作,并使用WPF的WriteableBitmap和几何API来提供用户友好的裁剪体验,以及通过交...

weixin_28922227的博客 1142

WPF实现用户头像裁剪

WPF开发者QQ群:340500857前言 需要做一个用户选择头像并进行裁剪。欢迎转发、分享、点赞,谢谢大家~。效果预览(更多效果请下载源码体验):一、MainWind...

dotNET跨平台 578

wpf图片剪切(根据设定的参数自动的对当前的图片进行边框裁剪)

wpf图片剪切(根据设定的参数自动的对当前的图片进行边框裁剪) 方法1.提供一个坐标位置,裁剪的宽高。 方法2.提供俩个坐标位置裁剪。 3.BitmapSource对象保存为图片。 4.根据url得到BitmapSource对象。 南无阿弥陀佛

WPF实现控件的移动操作

WPF实现控件的移动操作,实现大小拖动,位置移动,简单易学

WPF圆形头像设置

发现有些小程序设置头像的处理似乎有点问题,特别是不断的放大、缩小后,会出现图片只显示出一半。主要问题是这些程序对于图片的边界判断存在问题,从而导致一些问题。 本文主要实现WPF下用户头像的设置处理,实现功能是用户选择一张图片该例子可以对选择的图片进行缩放和移动操作,最后方块内的部分,会被裁剪出来,最后以圆形头像显示出来。 启动后初始画面如下所示: 点击设置头像后,会显示对话框,点击导

cpplog 4370

wpf裁剪图片框

wpf自定义图片裁剪框架,很好的开源代码,代码精炼,实现精妙,值得推广和学习。

WPFDevelopers图片裁剪组件:专业级图片处理功能实现

WPFDevelopers是一个专注于WPF基础控件和自定义控件库的开源项目,其中图片裁剪组件为开发者提供了专业级的图片处理功能。本文将详细介绍如何使用WPFDevelopers实现高效、精准的图片裁剪功能,帮助开发者快速集成到自己的WPF应用中。 ## 图片裁剪组件核心功能解析 WPFDevelopers的图片裁剪组件包含多个功能模块,满足不同场景下的图片处理需求: ### CropCon

gitblog_00898的博客 623

WPF 自定义图片剪切器 - 头像剪切

转载于 http://blog.youkuaiyun.com/lvguoshan/article/details/48627031 这位大佬写的博客,我把源码扣下来后,加了个DEMO。可以运行使用,.net4.5

C# WPF实现的图片处理小工具,可进行图片裁剪、缩放、涂鸦、旋转、查看图片信息、保存

C# WPF实现的图片处理小工具,可进行图片裁剪、缩放、涂鸦、旋转、查看图片信息、保存。

WPF 自定义图片剪切器 - 头像剪切(扩展与完善、实时截图)

原文:WPF 自定义图片剪切器 - 头像剪切(扩展与完善、实时截图) 一、说明:上一次写的”WPF 自定义图片剪切器 - 头像剪切。你懂得“存在明显的缺陷,由于篇幅较长。重新写了一篇新的。 问题的原因:由于是对图片文件进行剪切处理,当图片加载后变形的话,处理起来明显的有错误...

weixin_34405925的博客 374

从Oracle到腾讯TDSQL数据库升级技术分享

腾讯TDSQL(Tencent Distributed SQL)是一款兼具关系数据库和NoSQL数据库优点的新型数据库,文章主要分享oracle升级为TDSQl数据库技术经验。

u012372850的专栏 2613

wpf圆头像

【代码】wpf圆头像。

Kaizen的博客 275

Android13 CarPowerManagementService启动流程分析

在CarPowerManagementService中init调用PowerHalService的setListener设置事件侦听器以接收车辆的电源事件。

liuning1985622的博客 956

QT实现HTTP文件(夹)下载的完整流程与实战技巧

本文详细介绍了使用QT框架实现HTTP文件和文件夹下载的完整流程与实战技巧。通过QNetworkAccessManager等核心类,讲解了从单文件下载、大文件处理、进度显示、暂停/取消功能,到复杂的文件夹下载策略。文章重点剖析了断点续传、错误处理与网络健壮性等关键问题,为C++桌面应用集成稳定高效的下载功能提供了专业指导。

weixin_29239279的博客 277
上一篇: WPF仿QQ聊天框表情文字混排实现
下一篇: WPF无边框窗口鼠标拖动缩放大小
leebin_20
博客等级 码龄12年 17粉丝 28原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值