在 WPF 中剪切非不透明的双进度条
Clipping non-opaque double progress bar in WPF
我正在尝试在 WPF 中创建一个具有两个值(第二个值始终等于或高于第一个值)的自定义进度条。基本栏工作正常,如下所示:
<wpft:ClippingBorder BorderBrush="{StaticResource Border}"
Background="{StaticResource Background}"
BorderThickness="1" CornerRadius="4">
<Grid Margin="-1" x:Name="Bars">
<Border BorderBrush="{StaticResource Border}"
Background="{Binding Value2Brush}"
BorderThickness="1" CornerRadius="4"
HorizontalAlignment="Left"
Width="{Binding Value2Width}" />
<Border BorderBrush="{StaticResource Border}"
Background="{Binding Value1Brush}"
BorderThickness="1" CornerRadius="4"
HorizontalAlignment="Left"
Width="{Binding Value1Width}" />
</Grid>
</wpft:ClippingBorder>
(其中ClippingBorder
为this。用于防止值接近0时外角出现毛刺。)
最终结果是一个漂亮的圆形显示:
放大视图,更清楚地显示圆角:
特别注意,两个内部条共享相同的外部边框,并且它们的右边缘向左弯曲,就像外部边框一样。
之所以可行,是因为它先绘制较长的条,然后在其上绘制较短的条。但是,它只有在画笔完全不透明时才能可靠地工作——特别是如果 Value1Brush
是部分透明的,那么一些 Value2Brush
会透过它显示出来,这是我不想要的。
理想情况下,我希望较长的条形图仅绘制自身超出较短条形图的那部分——或者等效地,将较长条形图的 clipping/opacity 掩码设置为透明的区域绘制较短的条。
但我不确定如何在不丢失圆角的情况下做到这一点。
我会尝试使用网格的列。在网格中放置两列,首先是 width="auto" 第二个是“*”。将短边框放在第一列中,另一个放在第二列中。当您更改边框的宽度时,列的大小将相应地调整为边框的宽度。
不幸的是,这不是一个通用的解决方案,但它似乎适用于这种情况。我必须给每个内部边框一个 x:Name
然后把它放在后面的代码中:
构造函数:
DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Border))
.AddValueChanged(OuterBar, Child_ActualWidthChanged);
DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Border))
.AddValueChanged(InnerBar, Child_ActualWidthChanged);
处理程序:
private void Child_ActualWidthChanged(object sender, EventArgs e)
{
var outerRect = new Rect(OuterBar.RenderSize);
outerRect.Inflate(5, 5);
var outer = new RectangleGeometry(outerRect);
var corner = InnerBar.CornerRadius.TopLeft;
var inner = new RectangleGeometry(new Rect(InnerBar.RenderSize), corner, corner);
OuterBar.Clip = new GeometryGroup()
{
Children = { outer, inner }
};
}
基本思路是从一个略大于外部条形图想要绘制的矩形开始,然后添加一个与内部条形图想要绘制的完全匹配的矩形——这会将它从几何图形中剪掉。然后将整体用作外部条的剪辑区域,这样它就不能绘制到内部条的区域内。
我最初尝试在 XAML 中使用以下方法来执行此操作,但没有成功(宽度更改时未调用转换器);我不确定为什么,但只是为了后代:
<Border.Clip>
<GeometryGroup>
<RectangleGeometry Rect="{Binding ElementName=OuterBar, Path=RenderSize,
Converter={StaticResource BoundsConverter}, ConverterParameter=5.0}" />
<RectangleGeometry Rect="{Binding ElementName=InnerBar, Path=RenderSize,
Converter={StaticResource BoundsConverter}}" RadiusX="4" RadiusY="4" />
</GeometryGroup>
</Border.Clip>
(此处转换器将采用 RenderSize
并生成 Rect
,可选 inflation,类似于上面的代码。)
我正在尝试在 WPF 中创建一个具有两个值(第二个值始终等于或高于第一个值)的自定义进度条。基本栏工作正常,如下所示:
<wpft:ClippingBorder BorderBrush="{StaticResource Border}"
Background="{StaticResource Background}"
BorderThickness="1" CornerRadius="4">
<Grid Margin="-1" x:Name="Bars">
<Border BorderBrush="{StaticResource Border}"
Background="{Binding Value2Brush}"
BorderThickness="1" CornerRadius="4"
HorizontalAlignment="Left"
Width="{Binding Value2Width}" />
<Border BorderBrush="{StaticResource Border}"
Background="{Binding Value1Brush}"
BorderThickness="1" CornerRadius="4"
HorizontalAlignment="Left"
Width="{Binding Value1Width}" />
</Grid>
</wpft:ClippingBorder>
(其中ClippingBorder
为this。用于防止值接近0时外角出现毛刺。)
最终结果是一个漂亮的圆形显示:
放大视图,更清楚地显示圆角:
特别注意,两个内部条共享相同的外部边框,并且它们的右边缘向左弯曲,就像外部边框一样。
之所以可行,是因为它先绘制较长的条,然后在其上绘制较短的条。但是,它只有在画笔完全不透明时才能可靠地工作——特别是如果 Value1Brush
是部分透明的,那么一些 Value2Brush
会透过它显示出来,这是我不想要的。
理想情况下,我希望较长的条形图仅绘制自身超出较短条形图的那部分——或者等效地,将较长条形图的 clipping/opacity 掩码设置为透明的区域绘制较短的条。
但我不确定如何在不丢失圆角的情况下做到这一点。
我会尝试使用网格的列。在网格中放置两列,首先是 width="auto" 第二个是“*”。将短边框放在第一列中,另一个放在第二列中。当您更改边框的宽度时,列的大小将相应地调整为边框的宽度。
不幸的是,这不是一个通用的解决方案,但它似乎适用于这种情况。我必须给每个内部边框一个 x:Name
然后把它放在后面的代码中:
构造函数:
DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Border))
.AddValueChanged(OuterBar, Child_ActualWidthChanged);
DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Border))
.AddValueChanged(InnerBar, Child_ActualWidthChanged);
处理程序:
private void Child_ActualWidthChanged(object sender, EventArgs e)
{
var outerRect = new Rect(OuterBar.RenderSize);
outerRect.Inflate(5, 5);
var outer = new RectangleGeometry(outerRect);
var corner = InnerBar.CornerRadius.TopLeft;
var inner = new RectangleGeometry(new Rect(InnerBar.RenderSize), corner, corner);
OuterBar.Clip = new GeometryGroup()
{
Children = { outer, inner }
};
}
基本思路是从一个略大于外部条形图想要绘制的矩形开始,然后添加一个与内部条形图想要绘制的完全匹配的矩形——这会将它从几何图形中剪掉。然后将整体用作外部条的剪辑区域,这样它就不能绘制到内部条的区域内。
我最初尝试在 XAML 中使用以下方法来执行此操作,但没有成功(宽度更改时未调用转换器);我不确定为什么,但只是为了后代:
<Border.Clip>
<GeometryGroup>
<RectangleGeometry Rect="{Binding ElementName=OuterBar, Path=RenderSize,
Converter={StaticResource BoundsConverter}, ConverterParameter=5.0}" />
<RectangleGeometry Rect="{Binding ElementName=InnerBar, Path=RenderSize,
Converter={StaticResource BoundsConverter}}" RadiusX="4" RadiusY="4" />
</GeometryGroup>
</Border.Clip>
(此处转换器将采用 RenderSize
并生成 Rect
,可选 inflation,类似于上面的代码。)