在UWP(通用Windows应用程序),Windows 10中创建自定义形状控件
我想创建一个自定义的Shape
控件,它根据一些自定义属性绘制不同的形状,如Polygon
, Ellipse
, Rectangle
等。
我能够像这样创建一个自定义模板控件ColorShape
:
然后,重写OnTemplateChanged
方法,并在shapeParent
ContentControl
插入相应的Shape
控件
但我想要的是实际扩展Shape
,所以我可以用同样的方式处理所有的形状,框架和自定义。
在WPF中,我们能够扩展Shape
并覆盖属性DefiningGeometry
。 在UWP中,不存在要覆盖的任何DefiningGeometry
属性。
如何创建自定义Shape
控件并定义相应的Geometry?
我发现在UWP中创建自定义形状的唯一方法是扩展Path
类并设置其Data
属性。
在布局相关部分(如LayoutUpdated
事件或ArrangeOverride
方法)时,不得更新Data
属性以考虑其他依赖项属性(如Width
)中的更改。
设置Data
会导致另一个布局运行,因此将其设置为在此期间调用的任何内容都会导致exception:
检测到布局周期。 布局无法完成
我使用的方法是为属性更改事件注册处理程序并更新其中的Data
。
我写了一篇博文 ,更详细地解释了它。
这是我使用的例子:
public class CandlestickShape : Path { public double StartValue { get { return Convert.ToDouble(GetValue(StartValueProperty)); } set { SetValue(StartValueProperty, value); } } public static readonly DependencyProperty StartValueProperty = DependencyProperty.Register("StartValue", typeof(double), typeof(CandlestickShape), new PropertyMetadata(0)); public double EndValue { get { return Convert.ToDouble(GetValue(EndValueProperty)); } set { SetValue(EndValueProperty, value); } } public static readonly DependencyProperty EndValueProperty = DependencyProperty.Register("EndValue", typeof(double), typeof(CandlestickShape), new PropertyMetadata(0)); public double MinValue { get { return Convert.ToDouble(GetValue(MinValueProperty)); } set { SetValue(MinValueProperty, value); } } public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof(double), typeof(CandlestickShape), new PropertyMetadata(0)); public double MaxValue { get { return Convert.ToDouble(GetValue(MaxValueProperty)); } set { SetValue(MaxValueProperty, value); } } public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(double), typeof(CandlestickShape), new PropertyMetadata(0)); /// /// Defines how many Pixel should be drawn for one Point /// public double PixelPerPoint { get { return Convert.ToDouble(GetValue(PointsPerPixelProperty)); } set { SetValue(PointsPerPixelProperty, value); } } public static readonly DependencyProperty PointsPerPixelProperty = DependencyProperty.Register("PixelPerPoint", typeof(double), typeof(CandlestickShape), new PropertyMetadata(0)); public CandlestickShape() { this.RegisterPropertyChangedCallback(CandlestickShape.WidthProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); this.RegisterPropertyChangedCallback(CandlestickShape.StartValueProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); this.RegisterPropertyChangedCallback(CandlestickShape.EndValueProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); this.RegisterPropertyChangedCallback(CandlestickShape.MinValueProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); this.RegisterPropertyChangedCallback(CandlestickShape.MaxValueProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); this.RegisterPropertyChangedCallback(CandlestickShape.PointsPerPixelProperty, new DependencyPropertyChangedCallback(RenderAffectingPropertyChanged)); } private void RenderAffectingPropertyChanged(DependencyObject o, DependencyProperty e) { (o as CandlestickShape)?.SetRenderData(); } private void SetRenderData() { var maxBorderValue = Math.Max(this.StartValue, this.EndValue); var minBorderValue = Math.Min(this.StartValue, this.EndValue); double topLineLength = (this.MaxValue - maxBorderValue) * this.PixelPerPoint; double bottomLineLength = (minBorderValue - this.MinValue) * this.PixelPerPoint; double bodyLength = (this.EndValue - this.StartValue) * this.PixelPerPoint; var fillColor = new SolidColorBrush(Colors.Green); if (bodyLength < 0) fillColor = new SolidColorBrush(Colors.Red); bodyLength = Math.Abs(bodyLength); var bodyGeometry = new RectangleGeometry { Rect = new Rect(new Point(0, topLineLength), new Point(this.Width, topLineLength + bodyLength)), }; var topLineGeometry = new LineGeometry { StartPoint = new Point(this.Width / 2, 0), EndPoint = new Point(this.Width / 2, topLineLength) }; var bottomLineGeometry = new LineGeometry { StartPoint = new Point(this.Width / 2, topLineLength + bodyLength), EndPoint = new Point(this.Width / 2, topLineLength + bodyLength + bottomLineLength) }; this.Data = new GeometryGroup { Children = new GeometryCollection { bodyGeometry, topLineGeometry, bottomLineGeometry } }; this.Fill = fillColor; this.Stroke = new SolidColorBrush(Colors.Black); } protected override Size ArrangeOverride(Size finalSize) { double height = (MaxValue - MinValue) * PixelPerPoint; return new Size(this.Width, height); } protected override Size MeasureOverride(Size availableSize) { double height = (MaxValue - MinValue) * PixelPerPoint; return new Size(this.Width, height); } }