c# - 在 UWP(通用 Windows 应用程序)、Windows 10 中创建自定义形状控件

标签 c# xaml win-universal-app template-control

我想创建一个自定义的Shape控件,它可以绘制不同的形状,例如PolygonEllipseRectangle等,具体取决于一些自定义属性。

我能够创建一个自定义模板控件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方法,并在shapeParent内插入相应的Shape控件ContentControl

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

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

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

最佳答案

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

更新 Data 属性以考虑其他依赖项属性(例如 Width)的更改不得在布局相关部分(例如 LayoutUpdated)中完成code> 事件或 ArrangeOverride 方法。

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

Layout cycle detected. Layout could not complete

我使用的方法是注册属性更改事件的处理程序并更新其中的数据

我写了一个blog post这更详细地解释了它。

这是我使用的示例:

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

关于c# - 在 UWP(通用 Windows 应用程序)、Windows 10 中创建自定义形状控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35697991/

相关文章:

c# - 如何在 Entity Framework Code First 中建立多对多关联

c# - 如何为 asp.net 控件选择不同的事件

c# - WPF 某些图像在加载时旋转

c# - 带有图像的 XamlReader

c# - 在 Windows Phone 中使用语义缩放

c# - 如何旋转放置在 map 控件上的图像

c# - ajax超时问题

.net - WF4 AssignActivity - 多行语句中断设计器

windows - 拍摄人像照片时图像旋转 90 度

c# - 如何在 C# 中使用 ODBC 连接执行 SqlBulkCopy(等效)?