我有一个
ListViewItem
,我正在应用一个Style
,我想把一条灰色虚线作为底部Border
.
我如何在 WPF 中执行此操作?我只能看到纯色画笔。
这在我们的应用程序中非常有效,允许我们使用真正的边框而不是乱用矩形:
<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" 会给你四个像素的破折号。
您可以像下面的代码一样使用矩形创建虚线或虚线
<Rectangle Stroke="#FF000000" Height="1" StrokeThickness="1" StrokeDashArray="4 4"
SnapsToDevicePixels="True"/>
开始使用它并根据您的场景自定义您的列表视图
晚会有点晚,但以下解决方案对我有用。它比其他两种解决方案稍微简单/更好:
<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>
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>
我们的团队最近将此作为一项要求,我们通过创建自定义控件
DashedBorder
来解决它,它扩展了 Border
并添加了虚线边框功能。
它有 3 个新的依赖属性
UseDashedBorder
(布尔)DashedBorderBrush
(刷子)StrokeDashArray
(双重收藏)可以这样用
<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
的颜色)。第一个是创建破折号,第二个是用边框的Background
填充空白。
它将
Rectangle
破折号属性映射到 DashedBorder
属性,如下所示
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); }
}
}
正在处理用户控件.... 我一直在尝试为行进的蚂蚁边界制作故事板。带有矩形和文本的基本网格工作正常,因为没有交互。当试图在网格内放置一个按钮时,矩形或按钮是可见的,但不会同时显示它们。
来自另一篇文章: 高级 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>
如果您正在寻找像素完美的虚线
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