• 利用Clip制作打洞效果


    起因

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

    Snap1

    代码实现

    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的存在。应根据实际需求选择不同的实现方式。

  • 相关阅读:
    解决win8无法成功安装Windows Phone 7 sdk的问题
    决定专心写博,学习
    时间管理的首要原则:专注力
    Windows Phone 7 开发环境的搭建
    Windows Phone 8 开发环境的搭建
    学习使用ErrorProvider 转载
    SetTimer函数
    网络工程课程笔记
    IP地址分类及特殊IP地址
    windows消息处理机制
  • 原文地址:https://www.cnblogs.com/yiyan127/p/WPF-HoleWithClip.html
Copyright © 2020-2023  润新知