制作一个用户头像选择器仿 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 代码如下;- <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 代码如下;- 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使用如下;- <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 代码如下;- 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 使用如下;- <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 代码如下;- 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |