WPF 实现用户头像选择器

打印 上一主题 下一主题

主题 932|帖子 932|积分 2796

制作一个用户头像选择器仿 WeGame


  • 制作一个用户头像选择Canvas为父控件所实现,展示图片使用Image,Path当作上方的蒙版;
  • Canvas:主要用途方便移动Image,设置ClipToBounds="True"裁剪为一个正方形200x200做为主要展示区域;
  • Image:展示需要裁剪的图片;


  • Path:CombinedGeometry[1]绘制蒙版大小200x200效果如下;


  • 当选择一个本地图片的时候判断宽与高谁更大,谁小就将它更改为200 ,另一边做等比缩放后给到DrawingVisual绘制一个新的BitmapFrame[2]给Image控件做展示;
  • 当移动图片的时候右侧展示当前区域使用CroppedBitmap[3]进行裁剪并显示;
  • 源码Github[4] Gitee[5]
1)CropAvatar.xaml 代码如下;
  1. <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"<br>                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>                    xmlns:controls="clr-namespace:WPFDevelopers.Controls"><br>    <ResourceDictionary.MergedDictionaries><br>        <ResourceDictionary Source="Basic/ControlBasic.xaml"/><br>    </ResourceDictionary.MergedDictionaries><br><br>    <br><br></ResourceDictionary><br>
复制代码
2)CropAvatar.cs 代码如下;
  1. using System;<br>using System.Windows;<br>using System.Windows.Controls;<br>using System.Windows.Input;<br>using System.Windows.Media;<br>using System.Windows.Media.Imaging;<br>using System.Windows.Shapes;<br>using WPFDevelopers.Helpers;<br><br>namespace WPFDevelopers.Controls<br>{<br>    [TemplatePart(Name = CanvasTemplateName, Type = typeof(Canvas))]<br>    [TemplatePart(Name = ImageTemplateName, Type = typeof(Image))]<br>    [TemplatePart(Name = PathTemplateName, Type = typeof(Path))]<br>    [TemplatePart(Name = GridTemplateName, Type = typeof(Grid))]<br>    [TemplatePart(Name = ReplaceButtonTemplateName, Type = typeof(Button))]<br>    [TemplatePart(Name = AddButtonTemplateName, Type = typeof(Button))]<br>    public partial class CropAvatar : Control<br>    {<br>        private const string CanvasTemplateName = "PART_Canvas";<br>        private const string ImageTemplateName = "PART_Image";<br>        private const string PathTemplateName = "PART_Layout";<br>        private const string GridTemplateName = "PART_Grid";<br>        private const string ReplaceButtonTemplateName = "PART_ReplaceButton";<br>        private const string AddButtonTemplateName = "PART_AddButton";<br>        private Point point;<br>        private const int _size = 200;<br>        private bool isDown;<br>        private bool isLeft;<br>        private CroppedBitmap crop;<br>        private Canvas canvas;<br>        private Image image;<br>        private Path path;<br>        private Grid grid;<br>        private Button replaceButton, addButton;<br>        private int initialX, initialY, voffsetX, voffsetY;<br>        private double vNewStartX, vNewStartY, _StartX, _StartY, centerX, centerY;<br>        private BitmapFrame bitmapFrame;<br><br>        public ImageSource OutImageSource<br>        {<br>            get { return (ImageSource)GetValue(OutImageSourceProperty); }<br>            set { SetValue(OutImageSourceProperty, value); }<br>        }<br><br>        public static readonly DependencyProperty OutImageSourceProperty =<br>            DependencyProperty.Register("OutImageSource", typeof(ImageSource), typeof(CropAvatar), new PropertyMetadata(null));<br><br><br>        static CropAvatar()<br>        {<br>            DefaultStyleKeyProperty.OverrideMetadata(typeof(CropAvatar), new FrameworkPropertyMetadata(typeof(CropAvatar)));<br>        }<br>        public override void OnApplyTemplate()<br>        {<br>            base.OnApplyTemplate();<br>            canvas = GetTemplateChild(CanvasTemplateName) as Canvas;<br>            canvas.Loaded += Canvas_Loaded;<br>            grid = GetTemplateChild(GridTemplateName) as Grid;<br>            image = GetTemplateChild(ImageTemplateName) as Image;<br>            image.MouseDown += Image_MouseDown;<br>            image.MouseMove += Image_MouseMove;<br>            image.MouseUp += Image_MouseUp;<br>            image.MouseLeave += Image_MouseLeave;<br>            path = GetTemplateChild(PathTemplateName) as Path;<br>            replaceButton = GetTemplateChild(ReplaceButtonTemplateName) as Button;<br>            replaceButton.Click += ReplaceButton_Click;<br>            addButton = GetTemplateChild(AddButtonTemplateName) as Button;<br>            addButton.Click += AddButton_Click;<br>        }<br><br>        private void Canvas_Loaded(object sender, RoutedEventArgs e)<br>        {<br>            if (sender is Canvas canvas)<br>            {<br>                var width = canvas.ActualWidth;<br>                var height = canvas.ActualHeight;<br>                centerX = (width - path.Width) / 2.0d;<br>                centerY = (height - path.Height) / 2.0d;<br>                canvas.Clip = new RectangleGeometry(new Rect(centerX, centerY, 200, 200)); <br>                Canvas.SetLeft(path, centerX);<br>                Canvas.SetTop(path, centerY);<br>                Canvas.SetLeft(grid, centerX);<br>                Canvas.SetTop(grid, centerY);<br>            }<br>        }<br><br>        private void Image_MouseLeave(object sender, MouseEventArgs e)<br>        {<br>            isDown = false;<br>            if (isLeft)<br>                _StartX = Canvas.GetLeft(image);<br>            else<br>                _StartY = Canvas.GetTop(image);<br>        }<br><br>        private void Image_MouseUp(object sender, MouseButtonEventArgs e)<br>        {<br>            if (isDown)<br>            {<br>                var vPoint = e.GetPosition(this);<br>                if (isLeft)<br>                {<br>                    _StartX = Canvas.GetLeft(image);<br>                    initialX = voffsetX;<br>                }<br><br>                else<br>                {<br>                    _StartY = Canvas.GetTop(image);<br>                    initialY = voffsetY;<br>                }<br>            }<br>        }<br><br>        private void Image_MouseMove(object sender, MouseEventArgs e)<br>        {<br>            if (e.LeftButton == MouseButtonState.Pressed && isDown)<br>            {<br>                var vPoint = e.GetPosition(this);<br>                if (isLeft)<br>                {<br>                    var voffset = vPoint.X - point.X;<br>                    vNewStartX = _StartX + voffset;<br>                    var xPath = Canvas.GetLeft(path);<br>                    if (vNewStartX <= xPath && vNewStartX >= -(bitmapFrame.Width - 200 - xPath))<br>                    {<br>                        Canvas.SetLeft(image, vNewStartX);<br>                        voffsetX = initialX - (int)voffset;<br>                        voffsetX = voffsetX < 0 ? 0 : voffsetX;<br>                        crop = new CroppedBitmap(bitmapFrame, new Int32Rect(voffsetX, 0, _size, _size));<br><br>                    }<br>                }<br>                else<br>                {<br>                    var voffset = vPoint.Y - point.Y;<br>                    vNewStartY = _StartY + voffset;<br>                    var yPath = Canvas.GetTop(path);<br>                    if (vNewStartY <= yPath && vNewStartY >= -(bitmapFrame.Height - 200 - yPath))<br>                    {<br>                        Canvas.SetTop(image, vNewStartY);<br>                        voffsetY = initialY - (int)voffset;<br>                        voffsetY = voffsetY < 0 ? 0 : voffsetY;<br>                        crop = new CroppedBitmap(bitmapFrame, new Int32Rect(0, voffsetY, _size, _size));<br>                    }<br>                }<br>                OutImageSource = crop;<br>            }<br>        }<br><br>        private void Image_MouseDown(object sender, MouseButtonEventArgs e)<br>        {<br>            isDown = true;<br>            point = e.GetPosition(this);<br>        }<br><br>        private void ReplaceButton_Click(object sender, RoutedEventArgs e)<br>        {<br>            InitialImage();<br>        }<br><br>        private void AddButton_Click(object sender, RoutedEventArgs e)<br>        {<br>            InitialImage();<br>        }<br><br>        void InitialImage()<br>        {<br>            vNewStartX = 0;<br>            vNewStartY = 0;<br>            var uri = ControlsHelper.ImageUri();<br>            if (uri == null) return;<br>            var bitmap = new BitmapImage(uri);<br>            if (bitmap.Height > bitmap.Width)<br>            {<br>                double scale = (double)bitmap.Width / (double)path.Width;<br>                image.Width = _size;<br>                image.Height = (double)bitmap.Height / scale;<br>                isLeft = false;<br>            }<br>            else if (bitmap.Width > bitmap.Height)<br>            {<br>                double scale = (double)bitmap.Height / (double)path.Height;<br>                image.Width = (double)bitmap.Width / scale;<br>                image.Height = _size;<br>                isLeft = true;<br>            }<br>            bitmapFrame = ControlsHelper.CreateResizedImage(bitmap, (int)image.Width, (int)image.Height, 0);<br>            image.Source = bitmapFrame;<br>            if (image.Source != null)<br>            {<br>                replaceButton.Visibility = Visibility.Visible;<br>                addButton.Visibility = Visibility.Collapsed;<br>            }<br>            Canvas.SetLeft(grid, centerX);<br>            Canvas.SetTop(grid, centerY);<br>            _StartX = (canvas.ActualWidth - image.Width) / 2.0d;<br>            _StartY = (canvas.ActualHeight - image.Height) / 2.0d;<br>            Canvas.SetLeft(image, _StartX);<br>            Canvas.SetTop(image, _StartY);        <br>            if (isLeft)<br>            {<br>                initialX = (int)(image.Width - 200) / 2;<br>                initialY = 0;<br>                crop = new CroppedBitmap(bitmapFrame, new Int32Rect(initialX, 0, _size, _size));<br><br>            }<br>            else<br>            {<br>                initialY = (int)(image.Height - 200) / 2;<br>                initialX = 0;<br>                crop = new CroppedBitmap(bitmapFrame, new Int32Rect(0, initialY, _size, _size));<br>            }<br>            OutImageSource = crop;<br>        }<br>       <br>    }<br>}<br><br>
复制代码
3)CropAvatarWindow.xaml使用如下;
  1. <ws:Window x:Class="WPFDevelopers.Samples.ExampleViews.CropAvatarWindow"<br>        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"<br>        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"<br>        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"<br>        xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"<br>        xmlns:ws="https://github.com/WPFDevelopersOrg.WPFDevelopers.Minimal"<br>        mc:Ignorable="d"  WindowStyle="ToolWindow" ResizeMode="NoResize"<br>        WindowStartupLocation="CenterScreen"<br>        Title="WPF 开发者-头像选择器" Height="450" Width="800"><br>    <Grid><br>        <Grid.ColumnDefinitions><br>            <ColumnDefinition/><br>            <ColumnDefinition/><br>        </Grid.ColumnDefinitions><br>        <Grid.RowDefinitions><br>            <RowDefinition/><br>            <RowDefinition Height="Auto"/><br>        </Grid.RowDefinitions><br>        <wpfdev:CropAvatar x:Name="MyCropAvatar"/><br>        <Image Grid.Column="1" Name="CropAvatarImage" Source="{Binding ElementName=MyCropAvatar,Path=OutImageSource}" <br>               Stretch="Fill" Width="200" Height="200"><br>            <Image.Clip><br>                <EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/><br>            </Image.Clip><br>        </Image><br>        <UniformGrid Grid.Row="1" Grid.ColumnSpan="2" <br>                     HorizontalAlignment="Center" <br>                     VerticalAlignment="Center"><br>            <Button  Content="保存" Click="btnSave_Click" Style="{StaticResource PrimaryButton}" Margin="4,0"/><br>            <Button  Content="关闭" Click="btnClose_Click" Margin="4,0"/><br>        </UniformGrid><br>    </Grid><br></ws:Window><br><br>
复制代码
4) CropAvatarWindow.xaml.cs 代码如下;
  1. using System.Windows;<br><br>namespace WPFDevelopers.Samples.ExampleViews<br>{<br>    /// <summary><br>    /// CropAvatarWindow.xaml 的交互逻辑<br>    /// </summary><br>    public partial class CropAvatarWindow <br>    {<br>        public CropAvatarWindow()<br>        {<br>            InitializeComponent();<br>        }<br><br>        private void btnSave_Click(object sender, RoutedEventArgs e)<br>        {<br>            DialogResult = true;<br>        }<br><br>        private void btnClose_Click(object sender, RoutedEventArgs e)<br>        {<br>            DialogResult = false;<br>        }<br>    }<br>}<br><br>
复制代码
5) CropAvatarExample.xaml 使用如下;
  1. <UserControl x:Class="WPFDevelopers.Samples.ExampleViews.CropAvatarExample"<br>             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"<br>             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"<br>             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" <br>             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" <br>             xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers"<br>             xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews"<br>             mc:Ignorable="d" <br>             d:DesignHeight="450" d:DesignWidth="800"><br>    <Grid><br>        <Grid.ColumnDefinitions><br>            <ColumnDefinition/><br>            <ColumnDefinition/><br>        </Grid.ColumnDefinitions><br>        <Button Content="图像选择器" VerticalAlignment="Center" HorizontalAlignment="Center" Click="Button_Click"/><br>        <Image Grid.Column="1" Name="MyImage"<br>               Stretch="Fill" Width="200" Height="200"><br>            <Image.Clip><br>                <EllipseGeometry Center="100,100" RadiusX="100" RadiusY="100"/><br>            </Image.Clip><br>        </Image><br>    </Grid><br></UserControl><br><br>
复制代码
6) CropAvatarExample.xaml.cs 代码如下;
  1. using System.Windows.Controls;<br><br>namespace WPFDevelopers.Samples.ExampleViews<br>{<br>    /// <summary><br>    /// CropAvatarExample.xaml 的交互逻辑<br>    /// </summary><br>    public partial class CropAvatarExample : UserControl<br>    {<br>        public CropAvatarExample()<br>        {<br>            InitializeComponent();<br>        }<br><br>        private void Button_Click(object sender, System.Windows.RoutedEventArgs e)<br>        {<br>            var cropAvatarWindow = new CropAvatarWindow();<br>            if (cropAvatarWindow.ShowDialog() == true)<br>            {<br>                MyImage.Source = cropAvatarWindow.CropAvatarImage.Source;<br>            }<br>        }<br>    }<br>}<br><br>
复制代码
参考资料

[1] CombinedGeometry: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.combinedgeometry?view=netframework-4.0
[2] BitmapFrame: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.imaging.bitmapframe?view=windowsdesktop-6.0
[3] CroppedBitmap: https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.media.imaging.croppedbitmap?view=windowsdesktop-6.0
[4] Github: https://github.com/WPFDevelopersOrg/WPFDevelopers
[5] Gitee: https://gitee.com/WPFDevelopersOrg/WPFDevelopers

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

尚未崩坏

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表