WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)
不知从什么时候开始,头像流行使用圆形了,于是各个平台开始追逐显示圆形裁剪图像的技术。WPF 作为一个优秀的 UI 框架,当然有其内建的机制支持这种圆形裁剪。
不过,内建的机制仅支持画刷,而如果被裁剪的元素支持交互,或者拥有普通画刷无法达到的显示效果,那么就需要本文介绍的更加通用的解决方法了。
UWP 的圆形裁剪请左转参考:UWP 将图片裁剪成圆形(椭圆)。
WPF 的 UIElement 提供了 Clip 依赖项属性,可以使用一个 Geometry 来裁剪任意的 UIElement。由于 Geometry 几乎可以表示任意形状,这意味着我们可以才建成任意想要的样子。
于是,我们可以利用这一点,使用 EllipseGeometry 将任意 UIElement 裁剪成圆形或者椭圆形。比如,写成下面这样:
<Grid>
<Grid.Clip>
<EllipseGeometry Center="120 180" RadiusX="120" RadiusY="180" />
</Grid.Clip>
<Image Source="demo.jpg" Stretch="Fill" />
<TextBlock Text="https://walterlv.github.io" Foreground="White" Margin="171,172,51,21"/>
</Grid>
最终可以出现如下的效果。
不过,稍微改变下窗口的大小,就会发现裁剪的范围不对了。因为我们写死了圆形裁剪的中心点和两个不同方向的半径(这里可不好说是长半轴还是短半轴啊)。
我们需要一个可以自动修改裁剪圆形的一种机制,于是,我们想到了 Binding。为了使 XAML 的代码好看一点,我将 Binding 封装到了一个单独的类中处理,使用附加属性提供 API。
我封装好的类如下:
/// <summary>
/// 提供将任意控件裁剪为圆形或椭圆的附加属性。
/// </summary>
public static class EllipseClipper
{
/// <summary>
/// 标识 IsClipping 的附加属性。
/// </summary>
public static readonly DependencyProperty IsClippingProperty = DependencyProperty.RegisterAttached(
"IsClipping", typeof(bool), typeof(EllipseClipper), new PropertyMetadata(false, OnIsClippingChanged));
public static void SetIsClipping(DependencyObject element, bool value)
=> element.SetValue(IsClippingProperty, value);
public static bool GetIsClipping(DependencyObject element)
=> (bool) element.GetValue(IsClippingProperty);
private static void OnIsClippingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source = (UIElement) d;
if (e.NewValue is false)
{
// 如果 IsClipping 附加属性被设置为 false,则清除 UIElement.Clip 属性。
source.ClearValue(UIElement.ClipProperty);
return;
}
// 如果 UIElement.Clip 属性被用作其他用途,则抛出异常说明问题所在。
var ellipse = source.Clip as EllipseGeometry;
if (source.Clip != null && ellipse == null)
{
throw new InvalidOperationException(
$"{typeof(EllipseClipper).FullName}.{IsClippingProperty.Name} " +
$"is using {source.GetType().FullName}.{UIElement.ClipProperty.Name} " +
"for clipping, dont use this property manually.");
}
// 使用 UIElement.Clip 属性。
ellipse = ellipse ?? new EllipseGeometry();
source.Clip = ellipse;
// 使用绑定来根据控件的宽高更新椭圆裁剪范围。
var xBinding = new Binding(FrameworkElement.ActualWidthProperty.Name)
{
Source = source,
Mode = BindingMode.OneWay,
Converter = new HalfConverter(),
};
var yBinding = new Binding(FrameworkElement.ActualHeightProperty.Name)
{
Source = source,
Mode = BindingMode.OneWay,
Converter = new HalfConverter(),
};
var xyBinding = new MultiBinding
{
Converter = new SizeToClipCenterConverter(),
};
xyBinding.Bindings.Add(xBinding);
xyBinding.Bindings.Add(yBinding);
BindingOperations.SetBinding(ellipse, EllipseGeometry.RadiusXProperty, xBinding);
BindingOperations.SetBinding(ellipse, EllipseGeometry.RadiusYProperty, yBinding);
BindingOperations.SetBinding(ellipse, EllipseGeometry.CenterProperty, xyBinding);
}
private sealed class SizeToClipCenterConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
=> new Point((double) values[0], (double) values[1]);
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
private sealed class HalfConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> (double) value / 2;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
}
在 XAML 中只需要很简单的一个属性赋值即可达到圆形或椭圆形裁剪。
<Grid local:EllipseClipper.IsClipping="True">
<Image Source="fluentdesign-app-header.jpg" Stretch="Fill" />
<TextBlock Text="https://walterlv.github.io" Foreground="White" Margin="171,172,51,21"/>
</Grid>
而且才控件的大小改变的时候也能够正常更新裁剪范围。
这篇博客的核心代码我也贴在了 StackOverflow 上:c# - WPF displaying a gif in an ellipse - Stack Overflow
WPF 中使用附加属性,将任意 UI 元素或控件裁剪成圆形(椭圆)的更多相关文章
- WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法
原文:WPF中ContextMenu(右键菜单)使用Command在部分控件上默认为灰色的处理方法 问题描述 今天发现如果我想在一个TextBlock弄一个右键菜单,并且使用Command绑定,结果发 ...
- Android开发——fragment中数据传递与刷新UI(更改控件)
数据传递: 1.通过数据库进行数据的传递 如在fragment中将数据保存在数据库中,之后其他的fragment或者activity直接读取数据库中的数据,数据库使用还算简单,这里就不多说,建议使用l ...
- WPF中如何使用代码操作数据模板生成的控件
有一个Listbox,里面的Item是通过数据模板生成的,如下所示: <Border Margin="15" BorderBrush="Aqua" Bor ...
- WPF中获取Hwnd与窗体,Uid获取控件
void MapControl_Loaded(object sender, RoutedEventArgs e) { this.OnApplyTemplate(); CurrentMapChanged ...
- OC中使用UI自己定义控件实现计算器的设计(版本号1简单的加减乘除,连加,连减,连除,连乘)
OC中使用UI自己定义控件实现计算器的设计(版本号1简单的加减乘除,连加.连减,连除,连乘) #import <UIKit/UIKit.h> @interface ViewControll ...
- qt 在ui界面添加控件后在cpp文件中无法调用?
问题:qt 在ui界面添加控件后在cpp文件中无法调用? 解决方法:在build选项中选择“重新build项目”,再次在cpp中调用添加的控件发现可以调用了. 还有一种情况导致添加控件后无法调用,就是 ...
- 《Programming WPF》翻译 第3章 3.内嵌控件
原文:<Programming WPF>翻译 第3章 3.内嵌控件 WPF提供了一系列内嵌控件.其中大多数符合标准的你已经熟悉的Windows控件类型.注意到没有一个是包装在旧的Win32 ...
- [WinForm]WinForm跨线程UI操作常用控件类大全
前言 在C#开发的WinForm窗体程序开发的时候,经常会使用多线程处理一些比较耗时之类的操作.不过会有一个问题:就是涉及到跨线程操作UI元素. 相信才开始接触的人一定会遇上这个问题. 为了解决这个问 ...
- iOS开发UI篇—UITableview控件简单介绍
iOS开发UI篇—UITableview控件简单介绍 一.基本介绍 在众多移动应⽤用中,能看到各式各样的表格数据 . 在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView,UIT ...
随机推荐
- hiho 1318 非法二进制数 dp
#1318 : 非法二进制数 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 如果一个二进制数包含连续的两个1,我们就称这个二进制数是非法的. 小Hi想知道在所有 n 位 ...
- 日期插件My97DatePicker
因为项目中需要选中日期,所以就找到了My97DatePicker这个插件,用法非常的简单,但是因为各个公司的要求不同,我们公司使用js拼代码,然后渲染到页面上的,所以遇到了一点问题… 1.My97Da ...
- 浅谈NodeJs的模块机制
J历史 我们都知道,js在刚被创建的时候,只是为了在网页上写一些小脚本而已,比如网页特效,表单验证等等,创立者也许没觉悟到以后的js会发展到如此规模.这是web1.0时代. 在web 2.0时代,各种 ...
- em和px的区别一次彻底搞清楚!
在国内网站中,包括三大门户,以及“引领”中国网站设计潮流的蓝色理想,ChinaUI等都是使用了px作为字体单位.只有百度好歹做了个可调的表率.而 在大洋彼岸,几乎所有的主流站点都使用em作为字体单位, ...
- 在阿里云服务器上搭建 Apache Tomat 应用
在阿里云上购买一台服务器,系统采用 window 2008 Server 企业版,64位 1.下载Java7 JRE,安装 http://www.java.com/zh_CN/download/man ...
- Struts2中的包的作用描述
asm-3.3.jar作用:操作java字节码的类库包路径及主要类:未提供 asm-commons-3.3.jar作用:提供了基于事件的表现形式包路径及主要类:未提供 asm-tree-3.3.jar ...
- canvas之图形的变化(平移,缩放,旋转)
1.保存与恢复canvas状态 ctx.save();暂时将当前的状态保存到堆中 ctx.restore();该方法用于将上一个保存的状态从堆中再次取出,恢复该状态的所有设置. <!DOCTYP ...
- Vim代码缩进设置
前段配置VPS,无奈只能使用Vim编辑Python代码,比较头疼的没法设置自动缩进,所以搜索了相关的配置,特记录如下. 将以下的设置加入到~/etc/vim/.vimrc中: set sw=4 set ...
- C++复习5.指针数组字符串
C/C++ 指针.数组和字符串 本次学习指针.数组.字符串.引用的内存映像. 1.指针 指针的本质:可以执行的程序是由指令.数据和地址组成的.当CPU访问内存单元的时候,不论是读取还是写入,首先要把内 ...
- HDU 3473 Minimum Sum 划分树,数据结构 难度:1
http://acm.hdu.edu.cn/showproblem.php?pid=3473 划分树模板题目,需要注意的是划分树的k是由1开始的 划分树: 参考:http://blog.csdn.ne ...