博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF进阶教程 - 使用Decorator自定义带三角形的边框
阅读量:4315 次
发布时间:2019-06-06

本文共 21467 字,大约阅读时间需要 71 分钟。

原文:

写下来,备忘。      

  Decorator,有装饰器、装饰品的意思,很容易让人联想到设计模式里面的装饰器模式。Decorator类负责包装某个UI元素,用来提供额外的行为。它有一个类型为UIElement的Child属性,其中含有待包装的内容。Decorator可以被用于添加简单的视觉装饰,比如Border边框,或者更为复杂的行为,比如ViewBox、AdornerDecorator。

       当我们继承Decorator时,也可以自定义添加一些依赖属性,比如Border就定义了BorderBrush,BorderThinckness等用来设置Border的样式。
 
在想到自定义带三角形的Border之前,我们会想到这么几个问题
1、边框如何根据里面的内容大小变化而变化
2、如何才能使三角形和矩形的连接处无缝对接
 
       在之前,我都是使用Grid布局,上面使用BorderThiness为0的Border来包裹文字或者其他空间,下面使用一个三角形Path,这样可以粗略的实现类似效果,但是这个有很大的一个问题就是不能设置BorderThiness,否则三角形Path和Border的连接处会有一根线,无法去除。之外这样写一点都不通用,很傻,但怎么办呢。冥冥中自有天意,无意中看到了Decorator,真是柳暗花明(其实还是自己基础知识不扎实,否则怎么会不知道Decorator)。
 
好了,在开始自定义控件之前,需要先了解Decorator的一个工作原理。要绘制边框,首先这个边框得先知道我里面包裹的Child元素到底有多大,这就涉及到容器的计算问题。
 
容器的计算规则
   
   
计算容器永远都是先测量(
MeasureOverride
),然后通知父元素分配控件,计算好控件后就需要设置子元素的大小与位置(ArrangeOverride),最后准备工作都做好了之后,就要开始绘制了(OnRender)。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows;using System.Windows.Controls;using System.Windows.Media;namespace WpfDemo{    public sealed class AngleBorder : Decorator    {        public enum EnumPlacement        {            ///             /// 左上            ///             LeftTop,            ///             /// 左中            ///             LeftBottom,            ///             /// 左下            ///             LeftCenter,            ///             /// 右上            ///             RightTop,            ///             /// 右下            ///             RightBottom,            ///             /// 右中            ///             RightCenter,            ///             /// 上左            ///             TopLeft,            ///             /// 上中            ///             TopCenter,            ///             /// 上右            ///             TopRight,            ///             /// 下左            ///             BottomLeft,            ///             /// 下中            ///             BottomCenter,            ///             /// 下右            ///             BottomRight,        }        #region 依赖属性        public static readonly DependencyProperty PlacementProperty =            DependencyProperty.Register("Placement", typeof(EnumPlacement), typeof(AngleBorder),            new FrameworkPropertyMetadata(EnumPlacement.RightCenter, FrameworkPropertyMetadataOptions.AffectsRender, OnDirectionPropertyChangedCallback));        public EnumPlacement Placement        {            get { return (EnumPlacement)GetValue(PlacementProperty); }            set { SetValue(PlacementProperty, value); }        }        public static readonly DependencyProperty TailWidthProperty =            DependencyProperty.Register("TailWidth", typeof(double), typeof(AngleBorder), new PropertyMetadata(10d));        ///         /// 尾巴的宽度,默认值为7        ///         public double TailWidth        {            get { return (double)GetValue(TailWidthProperty); }            set { SetValue(TailWidthProperty, value); }        }        public static readonly DependencyProperty TailHeightProperty =            DependencyProperty.Register("TailHeight", typeof(double), typeof(AngleBorder), new PropertyMetadata(10d));        ///         /// 尾巴的高度,默认值为10        ///         public double TailHeight        {            get { return (double)GetValue(TailHeightProperty); }            set { SetValue(TailHeightProperty, value); }        }        public static readonly DependencyProperty TailVerticalOffsetProperty =            DependencyProperty.Register("TailVerticalOffset", typeof(double), typeof(AngleBorder), new PropertyMetadata(13d));        ///         /// 尾巴距离顶部的距离,默认值为10        ///         public double TailVerticalOffset        {            get { return (double)GetValue(TailVerticalOffsetProperty); }            set { SetValue(TailVerticalOffsetProperty, value); }        }        public static readonly DependencyProperty TailHorizontalOffsetProperty =            DependencyProperty.Register("TailHorizontalOffset", typeof(double), typeof(AngleBorder),                 new PropertyMetadata(12d));        ///         /// 尾巴距离顶部的距离,默认值为10        ///         public double TailHorizontalOffset        {            get { return (double)GetValue(TailHorizontalOffsetProperty); }            set { SetValue(TailHorizontalOffsetProperty, value); }        }        public static readonly DependencyProperty BackgroundProperty =            DependencyProperty.Register("Background", typeof(Brush), typeof(AngleBorder)                , new PropertyMetadata(new SolidColorBrush(Color.FromRgb(255, 255, 255))));        ///         /// 背景色,默认值为#FFFFFF,白色        ///         public Brush Background        {            get { return (Brush)GetValue(BackgroundProperty); }            set { SetValue(BackgroundProperty, value); }        }        public static readonly DependencyProperty PaddingProperty =            DependencyProperty.Register("Padding", typeof(Thickness), typeof(AngleBorder)                , new PropertyMetadata(new Thickness(10, 5, 10, 5)));        ///         /// 内边距        ///         public Thickness Padding        {            get { return (Thickness)GetValue(PaddingProperty); }            set { SetValue(PaddingProperty, value); }        }        public static readonly DependencyProperty BorderBrushProperty =            DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(AngleBorder)                , new PropertyMetadata(default(Brush)));        ///         /// 边框颜色        ///         public Brush BorderBrush        {            get { return (Brush)GetValue(BorderBrushProperty); }            set { SetValue(BorderBrushProperty, value); }        }        public static readonly DependencyProperty BorderThicknessProperty =            DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(AngleBorder), new PropertyMetadata(new Thickness(1d)));        ///         /// 边框大小        ///         public Thickness BorderThickness        {            get { return (Thickness)GetValue(BorderThicknessProperty); }            set { SetValue(BorderThicknessProperty, value); }        }        public static readonly DependencyProperty CornerRadiusProperty =            DependencyProperty.Register("CornerRadius", typeof(System.Windows.CornerRadius)                , typeof(AngleBorder), new PropertyMetadata(new CornerRadius(0)));        ///         /// 边框大小        ///         public System.Windows.CornerRadius CornerRadius        {            get { return (System.Windows.CornerRadius)GetValue(CornerRadiusProperty); }            set { SetValue(CornerRadiusProperty, value); }        }        #endregion        #region 方法重写        ///         /// 该方法用于测量整个控件的大小        ///         ///         /// 
控件的大小
protected override Size MeasureOverride(Size constraint) { Thickness padding = this.Padding; Size result = new Size(); if (Child != null) { //测量子控件的大小 Child.Measure(constraint); //三角形在左边与右边的,整个容器的宽度则为:里面子控件的宽度 + 设置的padding + 三角形的宽度 //三角形在上面与下面的,整个容器的高度则为:里面子控件的高度 + 设置的padding + 三角形的高度 switch (Placement) { case EnumPlacement.LeftTop: case EnumPlacement.LeftBottom: case EnumPlacement.LeftCenter: case EnumPlacement.RightTop: case EnumPlacement.RightBottom: case EnumPlacement.RightCenter: result.Width = Child.DesiredSize.Width + padding.Left + padding.Right + this.TailWidth; result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom; break; case EnumPlacement.TopLeft: case EnumPlacement.TopCenter: case EnumPlacement.TopRight: case EnumPlacement.BottomLeft: case EnumPlacement.BottomCenter: case EnumPlacement.BottomRight: result.Width = Child.DesiredSize.Width + padding.Left + padding.Right; result.Height = Child.DesiredSize.Height + padding.Top + padding.Bottom + this.TailHeight; break; default: break; } } return result; } /// /// 设置子控件的大小与位置 /// /// ///
protected override Size ArrangeOverride(Size arrangeSize) { Thickness padding = this.Padding; if (Child != null) { switch (Placement) { case EnumPlacement.LeftTop: case EnumPlacement.LeftBottom: case EnumPlacement.LeftCenter: Child.Arrange(new Rect(new Point(padding.Left + this.TailWidth, padding.Top), Child.DesiredSize)); //ArrangeChildLeft(); break; case EnumPlacement.RightTop: case EnumPlacement.RightBottom: case EnumPlacement.RightCenter: ArrangeChildRight(padding); break; case EnumPlacement.TopLeft: case EnumPlacement.TopRight: case EnumPlacement.TopCenter: Child.Arrange(new Rect(new Point(padding.Left, this.TailHeight + padding.Top), Child.DesiredSize)); break; case EnumPlacement.BottomLeft: case EnumPlacement.BottomRight: case EnumPlacement.BottomCenter: Child.Arrange(new Rect(new Point(padding.Left, padding.Top), Child.DesiredSize)); break; default: break; } } return arrangeSize; } private void ArrangeChildRight(Thickness padding) { double x = padding.Left; double y = padding.Top; if (!Double.IsNaN(this.Height) && this.Height != 0) { y = (this.Height - (Child.DesiredSize.Height)) / 2; } Child.Arrange(new Rect(new Point(x, y), Child.DesiredSize)); } /// /// 绘制控件 /// /// protected override void OnRender(DrawingContext drawingContext) { if (Child != null) { Geometry cg = null; Brush brush = null; //DpiScale dpi = base.getd(); Pen pen = new Pen(); pen.Brush = this.BorderBrush; //pen.Thickness = BorderThickness * 0.5; pen.Thickness = AngleBorder.RoundLayoutValue(BorderThickness.Left, DoubleUtil.DpiScaleX); switch (Placement) { case EnumPlacement.LeftTop: case EnumPlacement.LeftBottom: case EnumPlacement.LeftCenter: //生成小尾巴在左侧的图形和底色 cg = CreateGeometryTailAtLeft(); brush = CreateFillBrush(); break; case EnumPlacement.RightTop: case EnumPlacement.RightCenter: case EnumPlacement.RightBottom: //生成小尾巴在右侧的图形和底色 cg = CreateGeometryTailAtRight(); brush = CreateFillBrush(); break; case EnumPlacement.TopLeft: case EnumPlacement.TopCenter: case EnumPlacement.TopRight: //生成小尾巴在右侧的图形和底色 cg = CreateGeometryTailAtTop(); brush = CreateFillBrush(); break; case EnumPlacement.BottomLeft: case EnumPlacement.BottomCenter: case EnumPlacement.BottomRight: //生成小尾巴在右侧的图形和底色 cg = CreateGeometryTailAtBottom(); brush = CreateFillBrush(); break; default: break; } GuidelineSet guideLines = new GuidelineSet(); drawingContext.PushGuidelineSet(guideLines); drawingContext.DrawGeometry(brush, pen, cg); } } #endregion private static double RoundLayoutValue(double value, double dpiScale) { double num; if (!AngleBorder.AreClose(dpiScale, 1.0)) { num = Math.Round(value * dpiScale) / dpiScale; if (double.IsInfinity(num) || AngleBorder.AreClose(num, 1.7976931348623157E+308)) { num = value; } } else { num = Math.Round(value); } return num; } static bool AreClose(double value1, double value2) { if (value1 == value2) { return true; } double num = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * 2.2204460492503131E-16; double num2 = value1 - value2; return -num < num2 && num > num2; } #region 私有方法 private Geometry CreateGeometryTailAtRight() { CombinedGeometry result = new CombinedGeometry(); //三角形默认居中 this.TailVerticalOffset = (this.ActualHeight - this.TailHeight) / 2; #region 绘制三角形 Point arcPoint1 = new Point(this.ActualWidth - TailWidth, TailVerticalOffset); Point arcPoint2 = new Point(this.ActualWidth, TailVerticalOffset + TailHeight / 2); Point arcPoint3 = new Point(this.ActualWidth - TailWidth, TailVerticalOffset + TailHeight); LineSegment as1_2 = new LineSegment(arcPoint2, false); LineSegment as2_3 = new LineSegment(arcPoint3, false); PathFigure pf1 = new PathFigure(); pf1.IsClosed = false; pf1.StartPoint = arcPoint1; pf1.Segments.Add(as1_2); pf1.Segments.Add(as2_3); PathGeometry pg1 = new PathGeometry(); pg1.Figures.Add(pf1); #endregion #region 绘制矩形边框 RectangleGeometry rg2 = new RectangleGeometry(new Rect(0, 0, this.ActualWidth - TailWidth, this.ActualHeight) , CornerRadius.TopLeft, CornerRadius.BottomRight, new TranslateTransform(0.5, 0.5)); #endregion #region 合并两个图形 result.Geometry1 = pg1; result.Geometry2 = rg2; result.GeometryCombineMode = GeometryCombineMode.Union; #endregion return result; } private Geometry CreateGeometryTailAtLeft() { CombinedGeometry result = new CombinedGeometry(); switch (this.Placement) { case EnumPlacement.LeftTop: //不做任何处理 break; case EnumPlacement.LeftBottom: this.TailVerticalOffset = this.ActualHeight - this.TailHeight - this.TailVerticalOffset; break; case EnumPlacement.LeftCenter: this.TailVerticalOffset = (this.ActualHeight - this.TailHeight) / 2; break; } #region 绘制三角形 Point arcPoint1 = new Point(TailWidth, TailVerticalOffset); Point arcPoint2 = new Point(0, TailVerticalOffset + TailHeight / 2); Point arcPoint3 = new Point(TailWidth, TailVerticalOffset + TailHeight); LineSegment as1_2 = new LineSegment(arcPoint2, false); LineSegment as2_3 = new LineSegment(arcPoint3, false); PathFigure pf = new PathFigure(); pf.IsClosed = false; pf.StartPoint = arcPoint1; pf.Segments.Add(as1_2); pf.Segments.Add(as2_3); PathGeometry g1 = new PathGeometry(); g1.Figures.Add(pf); #endregion #region 绘制矩形边框 RectangleGeometry g2 = new RectangleGeometry(new Rect(TailWidth, 0, this.ActualWidth - this.TailWidth, this.ActualHeight) , CornerRadius.TopLeft, CornerRadius.BottomRight); #endregion #region 合并两个图形 result.Geometry1 = g1; result.Geometry2 = g2; result.GeometryCombineMode = GeometryCombineMode.Union; #endregion return result; } private Geometry CreateGeometryTailAtTop() { CombinedGeometry result = new CombinedGeometry(); switch (this.Placement) { case EnumPlacement.TopLeft: break; case EnumPlacement.TopCenter: this.TailHorizontalOffset = (this.ActualWidth - this.TailWidth) / 2; break; case EnumPlacement.TopRight: this.TailHorizontalOffset = this.ActualWidth - this.TailWidth - this.TailHorizontalOffset; break; } #region 绘制三角形 Point anglePoint1 = new Point(this.TailHorizontalOffset, this.TailHeight); Point anglePoint2 = new Point(this.TailHorizontalOffset + (this.TailWidth / 2), 0); Point anglePoint3 = new Point(this.TailHorizontalOffset + this.TailWidth, this.TailHeight); LineSegment as1_2 = new LineSegment(anglePoint2, true); LineSegment as2_3 = new LineSegment(anglePoint3, true); PathFigure pf = new PathFigure(); pf.IsClosed = false; pf.StartPoint = anglePoint1; pf.Segments.Add(as1_2); pf.Segments.Add(as2_3); PathGeometry g1 = new PathGeometry(); g1.Figures.Add(pf); #endregion #region 绘制矩形边框 RectangleGeometry g2 = new RectangleGeometry(new Rect(0, this.TailHeight, this.ActualWidth, this.ActualHeight - this.TailHeight) , CornerRadius.TopLeft, CornerRadius.BottomRight); #endregion #region 合并 result.Geometry1 = g1; result.Geometry2 = g2; result.GeometryCombineMode = GeometryCombineMode.Union; #endregion return result; } private Geometry CreateGeometryTailAtBottom() { CombinedGeometry result = new CombinedGeometry(); switch (this.Placement) { case EnumPlacement.BottomLeft: break; case EnumPlacement.BottomCenter: this.TailHorizontalOffset = (this.ActualWidth - this.TailWidth) / 2; break; case EnumPlacement.BottomRight: this.TailHorizontalOffset = this.ActualWidth - this.TailWidth - this.TailHorizontalOffset; break; } #region 绘制三角形 Point anglePoint1 = new Point(this.TailHorizontalOffset, this.ActualHeight - this.TailHeight); Point anglePoint2 = new Point(this.TailHorizontalOffset + this.TailWidth / 2, this.ActualHeight); Point anglePoint3 = new Point(this.TailHorizontalOffset + this.TailWidth, this.ActualHeight - this.TailHeight); LineSegment as1_2 = new LineSegment(anglePoint2, true); LineSegment as2_3 = new LineSegment(anglePoint3, true); PathFigure pf = new PathFigure(); pf.IsClosed = false; pf.StartPoint = anglePoint1; pf.Segments.Add(as1_2); pf.Segments.Add(as2_3); PathGeometry g1 = new PathGeometry(); g1.Figures.Add(pf); #endregion #region 绘制矩形边框 RectangleGeometry g2 = new RectangleGeometry(new Rect(0, 0, this.ActualWidth, this.ActualHeight - this.TailHeight) , CornerRadius.TopLeft, CornerRadius.BottomRight); #endregion #region 合并 result.Geometry1 = g1; result.Geometry2 = g2; result.GeometryCombineMode = GeometryCombineMode.Union; #endregion return result; } private Brush CreateFillBrush() { Brush result = null; GradientStopCollection gsc = new GradientStopCollection(); gsc.Add(new GradientStop(((SolidColorBrush)this.Background).Color, 0)); LinearGradientBrush backGroundBrush = new LinearGradientBrush(gsc, new Point(0, 0), new Point(0, 1)); result = backGroundBrush; return result; } /// /// 根据三角形方向设置消息框的水平位置,偏左还是偏右 /// /// /// public static void OnDirectionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var self = d as AngleBorder; self.HorizontalAlignment = ((EnumPlacement)e.NewValue == EnumPlacement.RightCenter) ? HorizontalAlignment.Right : HorizontalAlignment.Left; } #endregion }}

  

这里面使用了RectangleGeometry与LineSegment,其中使用
RectangleGeometry是为了利用其Radius属性可以用来设置圆角,
LineSegment则用来绘制三角形的几条直线边,最后利用CombinedGeometry的GeometryCombineMode属性将两个图形进行合并,这样它们连接处就不会有边框存在了,看起来就是一个整体
result.GeometryCombineMode=GeometryCombineMode.Union;

效果图:

源码下载:

链接: https://pan.baidu.com/s/1gfcHLp5 密码: 5b4e
 
posted on
2018-09-06 15:43 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/lonelyxmas/p/9598784.html

你可能感兴趣的文章
“此人不存在”
查看>>
github.com加速节点
查看>>
解密zend-PHP凤凰源码程序
查看>>
python3 序列分片记录
查看>>
Atitit.git的存储结构and 追踪
查看>>
atitit 读书与获取知识资料的attilax的总结.docx
查看>>
B站 React教程笔记day2(3)React-Redux
查看>>
找了一个api管理工具
查看>>
Part 2 - Fundamentals(4-10)
查看>>
使用Postmark测试后端存储性能
查看>>
NSTextView 文字链接的定制化
查看>>
第五天站立会议内容
查看>>
CentOs7安装rabbitmq
查看>>
(转))iOS App上架AppStore 会遇到的坑
查看>>
解决vmware与主机无法连通的问题
查看>>
做好产品
查看>>
项目管理经验
查看>>
笔记:Hadoop权威指南 第8章 MapReduce 的特性
查看>>
JMeter响应数据出现乱码的处理-三种解决方式
查看>>
获取设备实际宽度
查看>>