如何在 WPF 中实现虚线或点线边框?

发布于 2024-11-10 15:11:10 字数 138 浏览 4 评论 0原文

我有一个 ListViewItem,我正在对其应用 Style,并且我想将一条灰色虚线作为底部 Border

我怎样才能在 WPF 中做到这一点?我只能看到纯色画笔。

I have a ListViewItem that I am applying a Style to and I would like to put a dotted grey line as the bottom Border.

How can I do this in WPF? I can only see solid color brushes.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

长途伴 2024-11-17 15:11:10

这在我们的应用程序中效果很好,允许我们使用真正的边框而不是乱用矩形:

<Border BorderThickness="1,0,1,1">
   <Border.BorderBrush>
      <DrawingBrush Viewport="0,0,8,8" ViewportUnits="Absolute" TileMode="Tile">
         <DrawingBrush.Drawing>
            <DrawingGroup>
               <GeometryDrawing Brush="Black">
                  <GeometryDrawing.Geometry>
                     <GeometryGroup>
                        <RectangleGeometry Rect="0,0,50,50" />
                        <RectangleGeometry Rect="50,50,50,50" />
                     </GeometryGroup>
                  </GeometryDrawing.Geometry>
               </GeometryDrawing>
            </DrawingGroup>
         </DrawingBrush.Drawing>
      </DrawingBrush>
   </Border.BorderBrush>

   <TextBlock Text="Content Goes Here!" Margin="5"/>
</Border>

请注意,视口决定线条中虚线的大小。在本例中,它生成八像素的虚线。 Viewport="0,0,4,4" 会给你四个像素的破折号。

This worked great in our application, allowing us to use a real Border and not mess around with Rectangles:

<Border BorderThickness="1,0,1,1">
   <Border.BorderBrush>
      <DrawingBrush Viewport="0,0,8,8" ViewportUnits="Absolute" TileMode="Tile">
         <DrawingBrush.Drawing>
            <DrawingGroup>
               <GeometryDrawing Brush="Black">
                  <GeometryDrawing.Geometry>
                     <GeometryGroup>
                        <RectangleGeometry Rect="0,0,50,50" />
                        <RectangleGeometry Rect="50,50,50,50" />
                     </GeometryGroup>
                  </GeometryDrawing.Geometry>
               </GeometryDrawing>
            </DrawingGroup>
         </DrawingBrush.Drawing>
      </DrawingBrush>
   </Border.BorderBrush>

   <TextBlock Text="Content Goes Here!" Margin="5"/>
</Border>

Note that the Viewport determines the size of the dashes in the lines. In this case, it generates eight-pixel dashes. Viewport="0,0,4,4" would give you four-pixel dashes.

歌入人心 2024-11-17 15:11:10

您可以使用矩形创建点线或短划线,如下面的代码所示

<Rectangle Stroke="#FF000000" Height="1" StrokeThickness="1" StrokeDashArray="4 4"
                                                       SnapsToDevicePixels="True"/>

开始使用并根据您的场景自定义列表视图

You can create a dotted or dashes line using a rectangle like in the code below

<Rectangle Stroke="#FF000000" Height="1" StrokeThickness="1" StrokeDashArray="4 4"
                                                       SnapsToDevicePixels="True"/>

Get started with this and customize your listview according to your scenario

心碎无痕… 2024-11-17 15:11:10

聚会有点晚了,但以下解决方案对我有用。它比其他两种解决方案稍微简单/更好:

<Border BorderThickness="1">
  <Border.BorderBrush>
    <VisualBrush>
      <VisualBrush.Visual>
        <Rectangle StrokeDashArray="4 2" Stroke="Gray" StrokeThickness="1"
                  Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
                  Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
      </VisualBrush.Visual>
    </VisualBrush>
  </Border.BorderBrush>

  <TextBlock Text="Whatever" />
</Border>

A bit late to the party, but the following solution worked for me. It is slightly simpler/better than both other solutions:

<Border BorderThickness="1">
  <Border.BorderBrush>
    <VisualBrush>
      <VisualBrush.Visual>
        <Rectangle StrokeDashArray="4 2" Stroke="Gray" StrokeThickness="1"
                  Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
                  Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
      </VisualBrush.Visual>
    </VisualBrush>
  </Border.BorderBrush>

  <TextBlock Text="Whatever" />
</Border>
一个人的旅程 2024-11-17 15:11:10

沙姆尔

<Grid>
<Grid.RowDefinitions><RowDefinition Height="auto"/></Grid.RowDefinitions>
<Grid.ColumnDefinitions><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions>
<Rectangle RadiusX="9" RadiusY="9" Fill="White" Stroke="Black" StrokeDashArray="1,2"/>
<TextBlock Padding = "4,2" Text="Whatever"/>
</Grid>

Xaml

<Grid>
<Grid.RowDefinitions><RowDefinition Height="auto"/></Grid.RowDefinitions>
<Grid.ColumnDefinitions><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions>
<Rectangle RadiusX="9" RadiusY="9" Fill="White" Stroke="Black" StrokeDashArray="1,2"/>
<TextBlock Padding = "4,2" Text="Whatever"/>
</Grid>
森林很绿却致人迷途 2024-11-17 15:11:10

我们的团队最近收到了这个需求,并通过创建自定义控件 DashedBorder 来解决它,它扩展了 Border 并添加了虚线边框功能。

它有 3 个新的依赖属性

  • UseDashedBorder (bool)
  • DashedBorderBrush (Brush)
  • StrokeDashArray (DoubleCollection)

像这样使用

<controls:DashedBorder UseDashedBorder="True"
                       DashedBorderBrush="#878787"
                       StrokeDashArray="2 1"
                       Background="#EBEBEB"                               
                       BorderThickness="3"
                       CornerRadius="10 10 10 10">
    <TextBlock Text="Dashed Border"
               Margin="6 2 6 2"/>
</controls:DashedBorder>

并产生这样的结果
输入图片此处描述

UseDashedBorder 设置为 true 时,它将创建一个带有 2 个矩形的 VisualBrush 并将其设置为 BorderBrush(这就是为什么我们需要一个额外的属性来表示实际 BorderBrush 的颜色)。第一个是创建虚线,第二个是用边框的背景填充空白。

它将 Rectangle dashing 属性映射到 DashedBorder 属性,如下所示

  • StrokeDashArray => StrokeDashArray
  • Stroke =>; DashedBorderBrush
  • StrokeThickness =>; BorderThickness.Left
  • RadiusX => CornerRadius.TopLeft
  • RadiusY => CornerRadius.TopLeft
  • 宽度 => 实际宽度
  • 高度 => ActualHeight

DashedBorder.cs

public class DashedBorder : Border
{
    private static DoubleCollection? emptyDoubleCollection;
    private static DoubleCollection EmptyDoubleCollection()
    {
        if (emptyDoubleCollection == null)
        {
            DoubleCollection doubleCollection = new DoubleCollection();
            doubleCollection.Freeze();
            emptyDoubleCollection = doubleCollection;
        }
        return emptyDoubleCollection;
    }

    public static readonly DependencyProperty UseDashedBorderProperty =
        DependencyProperty.Register(nameof(UseDashedBorder),
                                    typeof(bool),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(false, OnUseDashedBorderChanged));

    public static readonly DependencyProperty DashedBorderBrushProperty =
        DependencyProperty.Register(nameof(DashedBorderBrush),
                                    typeof(Brush),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(null));

    public static readonly DependencyProperty StrokeDashArrayProperty =
        DependencyProperty.Register(nameof(StrokeDashArray),
                                    typeof(DoubleCollection),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(EmptyDoubleCollection()));

    private static void OnUseDashedBorderChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DashedBorder dashedBorder = (DashedBorder)target;
        dashedBorder.UseDashedBorderChanged();
    }

    private Rectangle GetBoundRectangle()
    {
        Rectangle rectangle = new Rectangle();

        rectangle.SetBinding(Rectangle.StrokeThicknessProperty, new Binding() { Source = this, Path = new PropertyPath("BorderThickness.Left") });
        rectangle.SetBinding(Rectangle.RadiusXProperty, new Binding() { Source = this, Path = new PropertyPath("CornerRadius.TopLeft") });
        rectangle.SetBinding(Rectangle.RadiusYProperty, new Binding() { Source = this, Path = new PropertyPath("CornerRadius.TopLeft") });
        rectangle.SetBinding(Rectangle.WidthProperty, new Binding() { Source = this, Path = new PropertyPath(ActualWidthProperty) });
        rectangle.SetBinding(Rectangle.HeightProperty, new Binding() { Source = this, Path = new PropertyPath(ActualHeightProperty) });

        return rectangle;
    }

    private Rectangle GetBackgroundRectangle()
    {
        Rectangle rectangle = GetBoundRectangle();
        rectangle.SetBinding(Rectangle.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(BackgroundProperty) });
        return rectangle;
    }

    private Rectangle GetDashedRectangle()
    {
        Rectangle rectangle = GetBoundRectangle();
        rectangle.SetBinding(Rectangle.StrokeDashArrayProperty, new Binding() { Source = this, Path = new PropertyPath(StrokeDashArrayProperty) });
        rectangle.SetBinding(Rectangle.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(DashedBorderBrushProperty) });
        Panel.SetZIndex(rectangle, 2);
        return rectangle;
    }

    private VisualBrush CreateDashedBorderBrush()
    {
        VisualBrush dashedBorderBrush = new VisualBrush();
        Grid grid = new Grid();
        Rectangle backgroundRectangle = GetBackgroundRectangle();
        Rectangle dashedRectangle = GetDashedRectangle();
        grid.Children.Add(backgroundRectangle);
        grid.Children.Add(dashedRectangle);
        dashedBorderBrush.Visual = grid;
        return dashedBorderBrush;
    }

    private void UseDashedBorderChanged()
    {
        if (UseDashedBorder)
        {
            BorderBrush = CreateDashedBorderBrush();
        }
        else
        {
            ClearValue(BorderBrushProperty);
        }
    }

    public bool UseDashedBorder
    {
        get { return (bool)GetValue(UseDashedBorderProperty); }
        set { SetValue(UseDashedBorderProperty, value); }
    }

    public Brush DashedBorderBrush
    {
        get { return (Brush)GetValue(DashedBorderBrushProperty); }
        set { SetValue(DashedBorderBrushProperty, value); }
    }

    public DoubleCollection StrokeDashArray
    {
        get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
        set { SetValue(StrokeDashArrayProperty, value); }
    }
}

Our team got this as a requirement lately and we solved it by creating a custom control, DashedBorder which extends Border and adds the dashed border feature.

It has 3 new dependency properties

  • UseDashedBorder (bool)
  • DashedBorderBrush (Brush)
  • StrokeDashArray (DoubleCollection)

Usable like this

<controls:DashedBorder UseDashedBorder="True"
                       DashedBorderBrush="#878787"
                       StrokeDashArray="2 1"
                       Background="#EBEBEB"                               
                       BorderThickness="3"
                       CornerRadius="10 10 10 10">
    <TextBlock Text="Dashed Border"
               Margin="6 2 6 2"/>
</controls:DashedBorder>

And produces a result like this
enter image description here

When UseDashedBorder is set to true it will create a VisualBrush with 2 rectangles and set that as BorderBrush (that's why we need an extra property for the color of the actual BorderBrush). The first one is to create the dashing and the second of is to fill in the gaps with the Background of the border.

It maps the Rectangle dashing properties to the DashedBorder properties like this

  • StrokeDashArray => StrokeDashArray
  • Stroke => DashedBorderBrush
  • StrokeThickness => BorderThickness.Left
  • RadiusX => CornerRadius.TopLeft
  • RadiusY => CornerRadius.TopLeft
  • Width => ActualWidth
  • Height => ActualHeight

DashedBorder.cs

public class DashedBorder : Border
{
    private static DoubleCollection? emptyDoubleCollection;
    private static DoubleCollection EmptyDoubleCollection()
    {
        if (emptyDoubleCollection == null)
        {
            DoubleCollection doubleCollection = new DoubleCollection();
            doubleCollection.Freeze();
            emptyDoubleCollection = doubleCollection;
        }
        return emptyDoubleCollection;
    }

    public static readonly DependencyProperty UseDashedBorderProperty =
        DependencyProperty.Register(nameof(UseDashedBorder),
                                    typeof(bool),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(false, OnUseDashedBorderChanged));

    public static readonly DependencyProperty DashedBorderBrushProperty =
        DependencyProperty.Register(nameof(DashedBorderBrush),
                                    typeof(Brush),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(null));

    public static readonly DependencyProperty StrokeDashArrayProperty =
        DependencyProperty.Register(nameof(StrokeDashArray),
                                    typeof(DoubleCollection),
                                    typeof(DashedBorder),
                                    new FrameworkPropertyMetadata(EmptyDoubleCollection()));

    private static void OnUseDashedBorderChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DashedBorder dashedBorder = (DashedBorder)target;
        dashedBorder.UseDashedBorderChanged();
    }

    private Rectangle GetBoundRectangle()
    {
        Rectangle rectangle = new Rectangle();

        rectangle.SetBinding(Rectangle.StrokeThicknessProperty, new Binding() { Source = this, Path = new PropertyPath("BorderThickness.Left") });
        rectangle.SetBinding(Rectangle.RadiusXProperty, new Binding() { Source = this, Path = new PropertyPath("CornerRadius.TopLeft") });
        rectangle.SetBinding(Rectangle.RadiusYProperty, new Binding() { Source = this, Path = new PropertyPath("CornerRadius.TopLeft") });
        rectangle.SetBinding(Rectangle.WidthProperty, new Binding() { Source = this, Path = new PropertyPath(ActualWidthProperty) });
        rectangle.SetBinding(Rectangle.HeightProperty, new Binding() { Source = this, Path = new PropertyPath(ActualHeightProperty) });

        return rectangle;
    }

    private Rectangle GetBackgroundRectangle()
    {
        Rectangle rectangle = GetBoundRectangle();
        rectangle.SetBinding(Rectangle.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(BackgroundProperty) });
        return rectangle;
    }

    private Rectangle GetDashedRectangle()
    {
        Rectangle rectangle = GetBoundRectangle();
        rectangle.SetBinding(Rectangle.StrokeDashArrayProperty, new Binding() { Source = this, Path = new PropertyPath(StrokeDashArrayProperty) });
        rectangle.SetBinding(Rectangle.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(DashedBorderBrushProperty) });
        Panel.SetZIndex(rectangle, 2);
        return rectangle;
    }

    private VisualBrush CreateDashedBorderBrush()
    {
        VisualBrush dashedBorderBrush = new VisualBrush();
        Grid grid = new Grid();
        Rectangle backgroundRectangle = GetBackgroundRectangle();
        Rectangle dashedRectangle = GetDashedRectangle();
        grid.Children.Add(backgroundRectangle);
        grid.Children.Add(dashedRectangle);
        dashedBorderBrush.Visual = grid;
        return dashedBorderBrush;
    }

    private void UseDashedBorderChanged()
    {
        if (UseDashedBorder)
        {
            BorderBrush = CreateDashedBorderBrush();
        }
        else
        {
            ClearValue(BorderBrushProperty);
        }
    }

    public bool UseDashedBorder
    {
        get { return (bool)GetValue(UseDashedBorderProperty); }
        set { SetValue(UseDashedBorderProperty, value); }
    }

    public Brush DashedBorderBrush
    {
        get { return (Brush)GetValue(DashedBorderBrushProperty); }
        set { SetValue(DashedBorderBrushProperty, value); }
    }

    public DoubleCollection StrokeDashArray
    {
        get { return (DoubleCollection)GetValue(StrokeDashArrayProperty); }
        set { SetValue(StrokeDashArrayProperty, value); }
    }
}
本宫微胖 2024-11-17 15:11:10

正在处理用户控件......
我一直在尝试制作行进蚂蚁边界的故事板。由于没有交互,带有矩形和文本的基本网格工作正常。当尝试将按钮放入网格内时,矩形或按钮都可见,但两者都不可见。

来自另一篇文章:
高级 XAML 动画效果。脉冲、行进的蚂蚁、旋转。警报

使用 dotNet 的 VisualBrush 解决方案将矩形移动到边框,内部有一个按钮。这非常有效。

<UserControl.Resources>
    <ResourceDictionary>
        <Style TargetType="{x:Type TextBlock}" x:Key="LOC_DG_Cell_Mid" BasedOn="{StaticResource DG_TextBlock_Mid}" >
            <Setter Property="Margin" Value="5 0"/>
        </Style>
        <Storyboard x:Key="MarchingAnts">
            <DoubleAnimation BeginTime="00:00:00"
                Storyboard.TargetName="AlertBox"                                
                Storyboard.TargetProperty="StrokeThickness"
                To="4"
                Duration="0:0:0.25" />
            <!-- If you want to run counter-clockwise, just swap the 'From' and 'To' values. -->
            <DoubleAnimation BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetName="AlertBox" Storyboard.TargetProperty="StrokeDashOffset" 
                            Duration="0:3:0" From="1000" To="0"/>
        </Storyboard>
    </ResourceDictionary>

</UserControl.Resources>
<UserControl.Triggers>
    <EventTrigger RoutedEvent="FrameworkElement.Loaded">
        <BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
    </EventTrigger>
</UserControl.Triggers>

<Grid>
    <Border BorderThickness="1">
        <Border.BorderBrush>
            <VisualBrush>
                <VisualBrush.Visual>
                    <Rectangle x:Name="AlertBox" Stroke="Red" StrokeDashOffset="2" StrokeDashArray="5" Margin="5"
                      Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
                      Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
                </VisualBrush.Visual>
            </VisualBrush>
        </Border.BorderBrush>

        <Button x:Name="FinishedButton" Padding="0 5" Margin="0" Style="{StaticResource IconButton}" >
            <StackPanel Orientation="Horizontal" >
                <Label Style="{StaticResource ButtonLabel}" Content="Processing has Finished" />
            </StackPanel>
        </Button>
    </Border>
</Grid>

Working on a user control....
I have been trying a storyboard for a marching ants border. The basic grid with a rectangle and text works fine since there is no interaction. When trying to put a button inside the grid, then either the rectangle or button is visible but never both of them.

From another post:
Advanced XAML Animation effects. Pulse, Marching ants, Rotations. Alerts

Using dotNet's solution for the VisualBrush shifted the rectangle to the border with a button inside. This worked perfectly.

<UserControl.Resources>
    <ResourceDictionary>
        <Style TargetType="{x:Type TextBlock}" x:Key="LOC_DG_Cell_Mid" BasedOn="{StaticResource DG_TextBlock_Mid}" >
            <Setter Property="Margin" Value="5 0"/>
        </Style>
        <Storyboard x:Key="MarchingAnts">
            <DoubleAnimation BeginTime="00:00:00"
                Storyboard.TargetName="AlertBox"                                
                Storyboard.TargetProperty="StrokeThickness"
                To="4"
                Duration="0:0:0.25" />
            <!-- If you want to run counter-clockwise, just swap the 'From' and 'To' values. -->
            <DoubleAnimation BeginTime="00:00:00" RepeatBehavior="Forever" Storyboard.TargetName="AlertBox" Storyboard.TargetProperty="StrokeDashOffset" 
                            Duration="0:3:0" From="1000" To="0"/>
        </Storyboard>
    </ResourceDictionary>

</UserControl.Resources>
<UserControl.Triggers>
    <EventTrigger RoutedEvent="FrameworkElement.Loaded">
        <BeginStoryboard Storyboard="{StaticResource MarchingAnts}"/>
    </EventTrigger>
</UserControl.Triggers>

<Grid>
    <Border BorderThickness="1">
        <Border.BorderBrush>
            <VisualBrush>
                <VisualBrush.Visual>
                    <Rectangle x:Name="AlertBox" Stroke="Red" StrokeDashOffset="2" StrokeDashArray="5" Margin="5"
                      Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualWidth}"
                      Height="{Binding RelativeSource={RelativeSource AncestorType={x:Type Border}}, Path=ActualHeight}"/>
                </VisualBrush.Visual>
            </VisualBrush>
        </Border.BorderBrush>

        <Button x:Name="FinishedButton" Padding="0 5" Margin="0" Style="{StaticResource IconButton}" >
            <StackPanel Orientation="Horizontal" >
                <Label Style="{StaticResource ButtonLabel}" Content="Processing has Finished" />
            </StackPanel>
        </Button>
    </Border>
</Grid>
2024-11-17 15:11:10

如果您正在寻找像素完美的虚线

请注意,每行末尾没有阴影/模糊

public static class DashBrushFactory
    {
        public static Brush CreateBrush(double dpiScale, SolidColorBrush solidColorBrush)
        {
            const double dashLength = 4;
            const double dashSpace = 4;

            double dashLengthPixelSnapped = SnapToPixel(dashLength, dpiScale);
            double dashSpacePixelSnapped = SnapToPixel(dashSpace, dpiScale);

            ImageBrush imageBrush = new ImageBrush();
            DrawingImage drawingImage = new DrawingImage();
            GeometryDrawing geometryDrawing = new GeometryDrawing();
            GeometryGroup geometryGroup = new GeometryGroup();
            RectangleGeometry rectangleGeometry1 = new RectangleGeometry();
            RectangleGeometry rectangleGeometry2 = new RectangleGeometry();

            rectangleGeometry1.Rect = new Rect(0, 0, dashLengthPixelSnapped, dashLengthPixelSnapped);
            rectangleGeometry2.Rect = new Rect(dashLengthPixelSnapped, dashLengthPixelSnapped, dashSpacePixelSnapped, dashSpacePixelSnapped);

            rectangleGeometry1.Freeze();
            rectangleGeometry2.Freeze();

            geometryGroup.Children.Add(rectangleGeometry1);
            geometryGroup.Children.Add(rectangleGeometry2);
            geometryGroup.Freeze();

            geometryDrawing.Brush = solidColorBrush;
            geometryDrawing.Geometry = geometryGroup;
            geometryDrawing.Freeze();

            drawingImage.Drawing = geometryDrawing;
            drawingImage.Freeze();
            imageBrush.TileMode = TileMode.Tile;
            imageBrush.ViewportUnits = BrushMappingMode.Absolute;
            imageBrush.Viewport = new Rect(0, 0, dashLengthPixelSnapped * 2, dashSpacePixelSnapped * 2);
            imageBrush.ImageSource = drawingImage;
            imageBrush.Freeze();

            return imageBrush;
        }

        private static double SnapToPixel(double value, double dpiScale)
        {
            double newValue;

            // If DPI == 1, don't use DPI-aware rounding.
            if (DoubleUtil.AreClose(dpiScale, 1.0) == false)
            {
                newValue = Math.Round(value * dpiScale) / dpiScale;
                // If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
                if (DoubleUtil.IsNaN(newValue) ||
                    Double.IsInfinity(newValue) ||
                    DoubleUtil.AreClose(newValue, Double.MaxValue))
                {
                    newValue = value;
                }
            }
            else
            {
                newValue = Math.Round(value);
            }

            return newValue;
        }
    }

https://referencesource.microsoft.com/#WindowsBase/Shared/MS/内部/DoubleUtil.cs

If you are looking for pixel perfect dashed lines

Note that there is no shadow/blur at the end of of each line

public static class DashBrushFactory
    {
        public static Brush CreateBrush(double dpiScale, SolidColorBrush solidColorBrush)
        {
            const double dashLength = 4;
            const double dashSpace = 4;

            double dashLengthPixelSnapped = SnapToPixel(dashLength, dpiScale);
            double dashSpacePixelSnapped = SnapToPixel(dashSpace, dpiScale);

            ImageBrush imageBrush = new ImageBrush();
            DrawingImage drawingImage = new DrawingImage();
            GeometryDrawing geometryDrawing = new GeometryDrawing();
            GeometryGroup geometryGroup = new GeometryGroup();
            RectangleGeometry rectangleGeometry1 = new RectangleGeometry();
            RectangleGeometry rectangleGeometry2 = new RectangleGeometry();

            rectangleGeometry1.Rect = new Rect(0, 0, dashLengthPixelSnapped, dashLengthPixelSnapped);
            rectangleGeometry2.Rect = new Rect(dashLengthPixelSnapped, dashLengthPixelSnapped, dashSpacePixelSnapped, dashSpacePixelSnapped);

            rectangleGeometry1.Freeze();
            rectangleGeometry2.Freeze();

            geometryGroup.Children.Add(rectangleGeometry1);
            geometryGroup.Children.Add(rectangleGeometry2);
            geometryGroup.Freeze();

            geometryDrawing.Brush = solidColorBrush;
            geometryDrawing.Geometry = geometryGroup;
            geometryDrawing.Freeze();

            drawingImage.Drawing = geometryDrawing;
            drawingImage.Freeze();
            imageBrush.TileMode = TileMode.Tile;
            imageBrush.ViewportUnits = BrushMappingMode.Absolute;
            imageBrush.Viewport = new Rect(0, 0, dashLengthPixelSnapped * 2, dashSpacePixelSnapped * 2);
            imageBrush.ImageSource = drawingImage;
            imageBrush.Freeze();

            return imageBrush;
        }

        private static double SnapToPixel(double value, double dpiScale)
        {
            double newValue;

            // If DPI == 1, don't use DPI-aware rounding.
            if (DoubleUtil.AreClose(dpiScale, 1.0) == false)
            {
                newValue = Math.Round(value * dpiScale) / dpiScale;
                // If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue), use the original value.
                if (DoubleUtil.IsNaN(newValue) ||
                    Double.IsInfinity(newValue) ||
                    DoubleUtil.AreClose(newValue, Double.MaxValue))
                {
                    newValue = value;
                }
            }
            else
            {
                newValue = Math.Round(value);
            }

            return newValue;
        }
    }

https://referencesource.microsoft.com/#WindowsBase/Shared/MS/Internal/DoubleUtil.cs

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文