【问题标题】:Create custom Shape Control in UWP (Universal Windows Apps), Windows 10在 UWP(通用 Windows 应用)、Windows 10 中创建自定义形状控件
【发布时间】:2016-02-29 11:07:34
【问题描述】:

我想创建一个自定义的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方法,并在shapeParentContentControl内插入一个对应的Shape控件

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

    我写了一个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);
        }
    }
    

    【讨论】:

    • 这涵盖了您的用例,但是当您想要自定义羞辱时会发生什么,其中一些部分是固定点,而其他部分取决于大小,像这样? stackoverflow.com/questions/43761186/…矩形部分取决于大小,但尖部分是固定的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    • 2019-11-26
    相关资源
    最近更新 更多