在Windows 10的UWP(通用Windows应用)中创建自定义形状控件 [英] Create custom Shape Control in UWP (Universal Windows Apps), Windows 10

查看:119
本文介绍了在Windows 10的UWP(通用Windows应用)中创建自定义形状控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个自定义的Shape控件,该控件根据某些自定义属性绘制不同的形状,例如PolygonEllipseRectangle等.

I want to create a custom Shape control, that paints different shapes like Polygon, Ellipse, Rectangle, etc, depending on some custom properties.

我能够像这样创建自定义模板控件ColorShape:

I was able to create a custom template control ColorShape like this:

<Style TargetType="local:CustomShape">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:CustomShape">
                <ContentControl x:Name="shapeParent">
                </ContentControl>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

然后,覆盖OnTemplateChanged方法,并在shapeParent ContentControl

And then, override the OnTemplateChanged method, and insert a corresponding Shape control inside the shapeParent ContentControl

但是我想要实际上是扩展Shape,所以我可以用相同的方式来处理所有形状(框架和自定义).

But what I'd like is to actually extend the Shape, so I can treat all the shapes, framewok and custom, in the same way.

在WPF中,我们能够扩展Shape并覆盖属性DefiningGeometry. 在UWP中,不存在任何要覆盖的DefiningGeometry属性.

In WPF we were able to extend the Shape and override the property DefiningGeometry. In UWP it doesn´t exist any DefiningGeometry property to override.

如何创建自定义Shape控件并定义相应的Geometry?

How is it possible to create a custom Shape control and define the corresponding Geometry?

推荐答案

我发现在UWP中创建自定义形状的唯一方法是扩展

The only way I found to create custom shapes in UWP is to extend the Path class and set its Data property.

在布置相关部分(如LayoutUpdated事件或ArrangeOverride方法)时,请勿完成更新Data属性以解决其他依赖项属性(例如Width)变化的问题.

Updating the Data property to account for changes in other dependency properties (like Width) must not be done in layouting relevant sections, like the LayoutUpdated event or the ArrangeOverride method.

设置Data会导致另一个布局运行,因此将其设置为在此期间调用的任何内容都会导致异常:

Setting Data leads to another layout run, so setting it in anything that is called during that would lead to an exception:

检测到布局周期.布局无法完成​​

Layout cycle detected. Layout could not complete

我使用的方法是为属性更改的事件注册处理程序,并在其中更新Data.

The way I use is to register handler for property changed events and update Data in them.

我写了一个博客文章对此进行了更详细的说明.

I have written a blog post that explains it in a bit more detail.

这是我使用的示例:

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));

    /// <summary>
    /// Defines how many Pixel should be drawn for one Point
    /// </summary>
    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);
    }
}

这篇关于在Windows 10的UWP(通用Windows应用)中创建自定义形状控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆