XAML WPF矩形边界与两个破折号的连接线的角

gpnt7bae  于 2023-06-27  发布在  其他
关注(0)|答案(3)|浏览(99)

在我的WPF应用程序中,我想为dag设置rectangle的样式并删除该文件。这个盒子的外观应该是这样的。

在XAML中可以实现这个结果吗?
到目前为止,我已经成功地做到了这一点。

问题出在角落里。角的外观应类似于两个破折号的连接线。例如,左下角-“L”可以调整矩形的大小。
下面是我目前的代码,它是在这个答案的帮助下创建的:
How can I achieve a dashed or dotted border in WPF?

<Rectangle 
        Fill="LightGray"
        AllowDrop="True"
        Stroke="#FF000000"
        StrokeThickness="2" 
        StrokeDashArray="5 4"
        SnapsToDevicePixels="True"
        MinHeight="200"
        MinWidth="200"
        />
0s7z1bwu

0s7z1bwu1#

为了在WPF中获得这些漂亮的L形角,您必须分别绘制水平和垂直边界,因为StrokeDashArray不会(总是)两者相同。
您对StrokeDashArray的要求是:

  • 每一行都应该以一个完整的破折号开始和结束
  • 破折号的长度应保持不变
  • 应通过拉伸虚线之间的空间来填充多余/缺失的距离

要得到精确的长度需要画一条线,你必须计算的行数(+1)和空格在你的破折号线,例如。就像这样:

private IEnumerable<double> GetDashArray(double length)
{
    double useableLength = length - StrokeDashLine;
    int lines = (int)Math.Round(useableLength/(StrokeDashLine + StrokeDashSpace));
    useableLength -= lines*StrokeDashLine;

    double actualSpacing = useableLength/lines;

    yield return StrokeDashLine / StrokeThickness;
    yield return actualSpacing / StrokeThickness;
}

将其 Package 在自定义控件中,您将获得类似于以下内容的内容:

<local:NiceCornersControl Fill="LightGray" Stroke="Black" 
  StrokeThickness="2" StrokeDashLine="5" StrokeDashSpace="5">
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" 
      Text="Drop files here"/>
</local:NiceCornersControl>

有几件事你应该知道:

  • 要使线条位于矩形的“内部”,需要将它们偏移StrokeThickness / 2
  • DashStyle可随您的StrokeThickness扩展
  • 对于半透明的描边颜色,这可能看起来很奇怪

控件的完整代码:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace InsertNamespaceHere
{
    public class NiceCornersControl : ContentControl
    {
        public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
            "Stroke", typeof(Brush), typeof(NiceCornersControl), new PropertyMetadata(default(Brush), OnVisualPropertyChanged));

        public Brush Stroke
        {
            get { return (Brush)GetValue(StrokeProperty); }
            set { SetValue(StrokeProperty, value); }
        }

        public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
            "StrokeThickness", typeof(double), typeof(NiceCornersControl), new PropertyMetadata(default(double), OnVisualPropertyChanged));

        public double StrokeThickness
        {
            get { return (double)GetValue(StrokeThicknessProperty); }
            set { SetValue(StrokeThicknessProperty, value); }
        }

        public static readonly DependencyProperty StrokeDashLineProperty = DependencyProperty.Register(
            "StrokeDashLine", typeof(double), typeof(NiceCornersControl), new PropertyMetadata(default(double), OnVisualPropertyChanged));

        public double StrokeDashLine
        {
            get { return (double)GetValue(StrokeDashLineProperty); }
            set { SetValue(StrokeDashLineProperty, value); }
        }

        public static readonly DependencyProperty StrokeDashSpaceProperty = DependencyProperty.Register(
            "StrokeDashSpace", typeof(double), typeof(NiceCornersControl), new PropertyMetadata(default(double), OnVisualPropertyChanged));

        public double StrokeDashSpace
        {
            get { return (double)GetValue(StrokeDashSpaceProperty); }
            set { SetValue(StrokeDashSpaceProperty, value); }
        }

        public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
            "Fill", typeof(Brush), typeof(NiceCornersControl), new PropertyMetadata(default(Brush), OnVisualPropertyChanged));

        public Brush Fill
        {
            get { return (Brush)GetValue(FillProperty); }
            set { SetValue(FillProperty, value); }
        }

        private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((NiceCornersControl)d).InvalidateVisual();
        }

        public NiceCornersControl()
        {
            SnapsToDevicePixels = true;
            UseLayoutRounding = true;
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            double w = ActualWidth;
            double h = ActualHeight;
            double x = StrokeThickness / 2.0;

            Pen horizontalPen = GetPen(ActualWidth - 2.0 * x);
            Pen verticalPen = GetPen(ActualHeight - 2.0 * x);

            drawingContext.DrawRectangle(Fill, null, new Rect(new Point(0, 0), new Size(w, h)));

            drawingContext.DrawLine(horizontalPen, new Point(x, x), new Point(w - x, x));
            drawingContext.DrawLine(horizontalPen, new Point(x, h - x), new Point(w - x, h - x));

            drawingContext.DrawLine(verticalPen, new Point(x, x), new Point(x, h - x));
            drawingContext.DrawLine(verticalPen, new Point(w - x, x), new Point(w - x, h - x));
        }

        private Pen GetPen(double length)
        {
            IEnumerable<double> dashArray = GetDashArray(length);
            return new Pen(Stroke, StrokeThickness)
            {
                DashStyle = new DashStyle(dashArray, 0),
                EndLineCap = PenLineCap.Square,
                StartLineCap = PenLineCap.Square,
                DashCap = PenLineCap.Flat
            };
        }

        private IEnumerable<double> GetDashArray(double length)
        {
            double useableLength = length - StrokeDashLine;
            int lines = (int)Math.Round(useableLength / (StrokeDashLine + StrokeDashSpace));
            useableLength -= lines * StrokeDashLine;
            double actualSpacing = useableLength / lines;

            yield return StrokeDashLine / StrokeThickness;
            yield return actualSpacing / StrokeThickness;
        }
    }
}
jhkqcmku

jhkqcmku2#

这不是答案,而是建议。事实上,我不确定这是否可以用一种简单的方法来实现(也许你可以通过获取控件的宽度和高度来计算StrokeDashArray)。但是,您可以使用动画:

<Grid Margin="3">
    <Rectangle  Name="Rect"
    Fill="LightGray"
    AllowDrop="True"
    Stroke="#FF000000"
    StrokeThickness="2" 
    StrokeDashArray="5 4"
    SnapsToDevicePixels="True"
    MinHeight="200"
    MinWidth="200"
    >
        <Rectangle.Triggers>
            <EventTrigger RoutedEvent="Window.Loaded">
                <BeginStoryboard>
                    <Storyboard >
                        <DoubleAnimation To="100" Duration="0:0:10" RepeatBehavior="Forever" By="1" 
                 Storyboard.TargetProperty="StrokeDashOffset" Storyboard.TargetName="Rect"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Rectangle.Triggers>
    </Rectangle>
</Grid>
up9lanfz

up9lanfz3#

我修改了接受的答案(@Manfred Radlwimmer),使其看起来像Rectangle而不是ContentControl

<c:AlignDashCornerRect Stroke="#7FFF0000"
                       StrokeDashCap="Round"
                       StrokeDashLine="5"
                       StrokeDashSpace="10"
                       StrokeEndLineCap="Round"
                       StrokeStartLineCap="Round"
                       StrokeThickness="4" />

public class AlignDashCornerRect : FrameworkElement
{
    public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
        "Fill", typeof(Brush), typeof(AlignDashCornerRect), new PropertyMetadata(default(Brush), OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeDashCapProperty =
        DependencyProperty.Register("StrokeDashCap", typeof(PenLineCap), typeof(AlignDashCornerRect), new PropertyMetadata(PenLineCap.Flat, OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeDashLineProperty = DependencyProperty.Register(
            "StrokeDashLine", typeof(double), typeof(AlignDashCornerRect), new PropertyMetadata(default(double), OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeDashSpaceProperty = DependencyProperty.Register(
        "StrokeDashSpace", typeof(double), typeof(AlignDashCornerRect), new PropertyMetadata(default(double), OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeEndLineCapProperty =
        DependencyProperty.Register("StrokeEndLineCap", typeof(PenLineCap), typeof(AlignDashCornerRect), new PropertyMetadata(PenLineCap.Flat, OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeLineJoinProperty =
        DependencyProperty.Register("StrokeLineJoin", typeof(PenLineJoin), typeof(AlignDashCornerRect), new PropertyMetadata(PenLineJoin.Miter, OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeMiterLimitProperty =
        DependencyProperty.Register("StrokeMiterLimit", typeof(double), typeof(AlignDashCornerRect), new PropertyMetadata(10.0d, OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
                    "Stroke", typeof(Brush), typeof(AlignDashCornerRect), new PropertyMetadata(default(Brush), OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeStartLineCapProperty =
        DependencyProperty.Register("StrokeStartLineCap", typeof(PenLineCap), typeof(AlignDashCornerRect), new PropertyMetadata(PenLineCap.Flat, OnVisualPropertyChanged));

    public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
            "StrokeThickness", typeof(double), typeof(AlignDashCornerRect), new PropertyMetadata(default(double), OnVisualPropertyChanged));

    public Brush Fill
    {
        get => (Brush)GetValue(FillProperty);
        set => SetValue(FillProperty, value);
    }

    public Brush Stroke
    {
        get => (Brush)GetValue(StrokeProperty);
        set => SetValue(StrokeProperty, value);
    }

    public PenLineCap StrokeDashCap
    {
        get => (PenLineCap)GetValue(StrokeDashCapProperty);
        set => SetValue(StrokeDashCapProperty, value);
    }

    public double StrokeDashLine
    {
        get => (double)GetValue(StrokeDashLineProperty);
        set => SetValue(StrokeDashLineProperty, value);
    }

    public double StrokeDashSpace
    {
        get => (double)GetValue(StrokeDashSpaceProperty);
        set => SetValue(StrokeDashSpaceProperty, value);
    }

    public PenLineCap StrokeEndLineCap
    {
        get => (PenLineCap)GetValue(StrokeEndLineCapProperty);
        set => SetValue(StrokeEndLineCapProperty, value);
    }

    public PenLineJoin StrokeLineJoin
    {
        get => (PenLineJoin)GetValue(StrokeLineJoinProperty);
        set => SetValue(StrokeLineJoinProperty, value);
    }

    public double StrokeMiterLimit
    {
        get => (double)GetValue(StrokeMiterLimitProperty);
        set => SetValue(StrokeMiterLimitProperty, value);
    }

    public PenLineCap StrokeStartLineCap
    {
        get => (PenLineCap)GetValue(StrokeStartLineCapProperty);
        set => SetValue(StrokeStartLineCapProperty, value);
    }

    public double StrokeThickness
    {
        get => (double)GetValue(StrokeThicknessProperty);
        set => SetValue(StrokeThicknessProperty, value);
    }

    public AlignDashCornerRect()
    {
        SnapsToDevicePixels = true;
        UseLayoutRounding = true;
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        double w = ActualWidth;
        double h = ActualHeight;
        double x = StrokeThickness / 2.0;

        var horizontalPen = GetPen(ActualWidth - (2.0 * x));
        var verticalPen = GetPen(ActualHeight - (2.0 * x));

        drawingContext.DrawRectangle(Fill, null, new Rect(new Point(0, 0), new Size(w, h)));

        drawingContext.DrawLine(horizontalPen, new Point(x, x), new Point(w - x, x));
        drawingContext.DrawLine(horizontalPen, new Point(x, h - x), new Point(w - x, h - x));

        drawingContext.DrawLine(verticalPen, new Point(x, x), new Point(x, h - x));
        drawingContext.DrawLine(verticalPen, new Point(w - x, x), new Point(w - x, h - x));
    }

    private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((AlignDashCornerRect)d).InvalidateVisual();
    }

    private IEnumerable<double> GetDashArray(double length)
    {
        double useableLength = length - StrokeDashLine;
        int lines = (int)Math.Round(useableLength / (StrokeDashLine + StrokeDashSpace));
        useableLength -= lines * StrokeDashLine;
        double actualSpacing = useableLength / lines;

        yield return StrokeDashLine / StrokeThickness;
        yield return actualSpacing / StrokeThickness;
    }

    private Pen GetPen(double length)
    {
        var dashArray = GetDashArray(length);

        return new Pen(Stroke, StrokeThickness) {
            DashStyle = new DashStyle(dashArray, 0),
            DashCap = StrokeDashCap,
            StartLineCap = StrokeStartLineCap,
            EndLineCap = StrokeEndLineCap,
            LineJoin = StrokeLineJoin,
            MiterLimit = StrokeMiterLimit
        };
    }
}

相关问题