起因

如上篇博文所说,连线原型需要在中间文字上下各留15像素的空白。设计师完成原型之后,问我有没有办法实现,我说,我能想到两种实现方式。其中一种就是上篇博文所说的OpacityMask。第二种就是使用Clip了。下面是效果图:

代码实现

Clip

Clip定义在UIElement中,类型为Geometry 。MSDN中的解释是获取或设置用于定义元素内容边框的几何图形。实际上不光可以在边框处留住空白,在UI元素里面留出空白也是可以的,只要定义好相关的形状。

矩形空洞

在一个大矩形中去除一个小矩形就行了。

主要代码在RectangleHoleConverter中,代码如下:

namespace HoleWithClip
{
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media; /// <summary>
/// 矩形空洞的转换器
/// </summary>
public class RectangleHoleConverter : IMultiValueConverter
{
/// <summary>
/// 转换成矩形空洞
/// </summary>
/// <param name="values">
/// 转换值列表,第一个表示起始宽度,第二个表示起始高度,
/// 第三个表示总宽度,第四个表示总高度
/// 第五个表示宿主宽度,第六个表示宿主宽度
/// </param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 6 || values.HaveNullItem()
|| !values.IsAllInstanceOfType(typeof(double)))
{
return DependencyProperty.UnsetValue;
} var maskStartWidth = (double)values[0];
var maskStartHeight = (double)values[1];
var maskTotalWidth = (double)values[2];
var maskTotalHeight = (double)values[3];
var hostWidth = (double)values[4];
var hostHeight = (double)values[5];
if (hostWidth == 0.0 || hostHeight == 0.0)
{
return null;
} var maskRectangle = new RectangleGeometry(new Rect(new Size(hostWidth, hostHeight)));
var maskEllipse = new RectangleGeometry(new Rect(
new Point(maskStartWidth, maskStartHeight),
new Size(maskTotalWidth, maskTotalHeight)));
var combinedGeometry = Geometry.Combine(maskRectangle, maskEllipse, GeometryCombineMode.Exclude, null); return combinedGeometry;
} public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new[] { Binding.DoNothing };
}
}
}

其中用到了两个扩展方法如下:

namespace HoleTest
{
using System;
using System.Collections.Generic;
using System.Linq; public static class EnumerableExtension
{
/// <summary>
/// 枚举器中是否存在null条目
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="enumerable">元素枚举</param>
/// <returns>存在null条目返回true,否则返回false</returns>
public static bool HaveNullItem<T>(this IEnumerable<T> enumerable)
{
return enumerable.Any(item => item == null);
} /// <summary>
/// 枚举器中是否全为指定类型的实例
/// </summary>
/// <typeparam name="T">元素类型</typeparam>
/// <param name="enumerable">元素枚举</param>
/// <returns>全为指定类型的实例返回true,否则返回false</returns>
public static bool IsAllInstanceOfType<T>(this IEnumerable<T> enumerable, Type type)
{
return enumerable.All(item => type.IsInstanceOfType(item));
}
}
}

椭圆形空洞

跟矩形空洞类似,在大的矩形中间排除一个椭圆。

主要代码在EllipseHoleConverter中,代码如下:

namespace HoleWithClip
{
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media; /// <summary>
/// 椭圆形空洞的转换器
/// </summary>
public class EllipseHoleConverter : IMultiValueConverter
{
/// <summary>
/// 转换成矩形空洞
/// </summary>
/// <param name="values">
/// 转换值列表,第一个表示起始宽度,第二个表示起始高度,
/// 第三个表示总宽度,第四个表示总高度
/// 第五个表示宿主宽度,第六个表示宿主宽度
/// </param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 6 || values.HaveNullItem()
|| !values.IsAllInstanceOfType(typeof(double)))
{
return DependencyProperty.UnsetValue;
} var maskEllipseCenterX = (double)values[0];
var maskEllipseCenterY = (double)values[1];
var maskRadiusX = (double)values[2];
var maskRadiusY = (double)values[3];
var hostWidth = (double)values[4];
var hostHeight = (double)values[5];
if (hostWidth == 0.0 || hostHeight == 0.0)
{
return null;
} var maskRectangle = new RectangleGeometry(new Rect(new Size(hostWidth, hostHeight)));
var maskEllipse = new EllipseGeometry(
new Point(maskEllipseCenterX, maskEllipseCenterY),
maskRadiusX,
maskRadiusY);
var combinedGeometry = Geometry.Combine(maskRectangle, maskEllipse, GeometryCombineMode.Exclude, null); return combinedGeometry;
} public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new[] { Binding.DoNothing };
}
}
}

不规则空洞

我们各种Shape中最常用的是Path,因为许多复杂的效果能够在设计工具中绘制成Path。与此类似,Geometry中最强大的还是PathGeometry。上面图片中的五角形就是将Path数据转换成PathGeometry的。XAML代码如下:

<Border Width="180"
Height="180"
Margin="3"
Background="Aquamarine">
<Border Background="LightPink" Clip="M90.000003,0.5 L111.12693,68.873594 L179.50001,68.871913 L124.18408,111.12744 L145.31405,179.49999 L90.000003,137.24175 L34.685959,179.49999 L55.815923,111.12744 L0.50000013,68.871913 L68.87308,68.873594 z" />
</Border>

话说,只看XAML代码真看不出这是什么鬼东西。

下载链接

博客园:HoleWithClip

OpacityMask与Clip区别

可以看出,通过OpacityMask和Clip都能实现打洞效果。但是通过OpactiyMask实现的效果在空洞中间会引发相应的事件,而Clip则不会,原因是在于UIElement中作命中测试考虑到了Clip的存在。应根据实际需求选择不同的实现方式。

最新文章

  1. Leetcode 57: Insert Interval 让代码更好读, 更容易测试.
  2. MS SQL专用管理员连接DAC
  3. java中观察者模式Observable和Observer
  4. windows无提示关闭页面
  5. web页面的回流,认识与避免
  6. POJ 3255
  7. How do I create an installation log?
  8. 一周一话题之一(EF-CodeFirst、MEF、T4框架搭建学习)
  9. 创建 序列 存储过程 job
  10. 解压版mysql安装--windows系统
  11. iOS开发之Block
  12. Docker学习笔记 - Docker的简介
  13. SSRS2008中控件ID冲突问题
  14. 驱动调试(一)-printk
  15. zookeeper 动态管理nginx配置
  16. ret和retf
  17. HTTP响应状态码 含义
  18. Javassist学习总结
  19. mac上textstudio在系统升级以后不能编译
  20. 解决Vue中&quot;This dependency was not found&quot;的方法

热门文章

  1. ASP.NET Web API获取Model元数据
  2. mysql并发insert deadlock分析以及解决,无delete/update/for update
  3. C# Winform 窗体美化
  4. 备份一张iPhone拍照写入exif中的orientation图片
  5. AutoCAD .NET二次开发(四)
  6. 在cmd中获取ip地址和主机名
  7. iOS 模仿微信的照片选择器
  8. org.apache.http.conn.HttpHostConnectException: Connection to http://localhost refused in android
  9. myeclipse2013 安装 egit
  10. (ios实战)单个ViewControl适配不同ios版本xib文件实现