2

我想创建一个自定义控件,根据一些自定义属性Shape绘制不同的形状,如PolygonEllipse、等。Rectangle

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

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

然后,重写该OnTemplateChanged方法,并ShapeshapeParent ContentControl

但我想要实际扩展Shape,因此我可以以相同的方式处理所有形状、框架和自定义。

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

如何创建自定义控件Shape并定义相应的几何图形?

4

1 回答 1

3

我发现在 UWP 中创建自定义形状的唯一方法是扩展Path类并设置其Data属性。

在布局相关部分(如事件或方法)时,不得更新Data属性以考虑其他依赖属性(如 )的更改。WidthLayoutUpdatedArrangeOverride

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

检测到布局周期。布局无法完成

我使用的方式是为属性更改事件注册处理程序并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));

    /// <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);
    }
}
于 2016-03-09T21:29:56.487 回答