gpt4 book ai didi

wpf - 精确的不透明蒙版

转载 作者:行者123 更新时间:2023-12-04 01:06:38 25 4
gpt4 key购买 nike

假设我需要在 WPF 控件上设置一个不透明蒙版,以在精确位置突出显示它的一部分(假设在 (50;50) 位置有一个 50x50 的正方形)。为此,我创建了一个包含 2 个 GeometryDrawing 对象的 DrawingGroup:1 个半透明矩形用于控件的整个实际大小,1 个不透明矩形用于突出显示区域。然后我从这个 DrawingGroup 创建一个 DrawingBrush,将它的 Stretch 属性设置为 None 并将这个画笔设置为需要屏蔽的控件的 OpacityMask。

所有这些都工作正常,而没有任何东西“超出”所述控制范围。但是,如果控件在其边界之外绘制某些内容,则外部点将成为应用不透明蒙版的起点(如果画笔与该侧对齐),并且整个蒙版会移动该距离,从而导致意外行为。

我似乎无法找到一种方法来强制从控件的边界应用蒙版,或者至少无法获得控件的实际边界(包括粘贴部分),因此我可以相应地调整我的蒙版。

任何想法都非常感谢!

更新:这是一个简单的测试用例 XAML 和演示该问题的屏幕截图:

我们有 2 个嵌套的 Borders 和 Canvas 在上面提到的正方形的最后一个中:

<Border Padding="20" Background="DarkGray" Width="240" Height="240">
<Border Background="LightBlue">
<Canvas>
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White"
/>
</Canvas>
</Border>
</Border>

这是它的外观:

no mask
(来源: ailon.org)

现在我们在第二个边框上添加一个 OpacityMask,这样除了我们的正方形之外,它的每个部分都是半透明的:
<Border.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,200,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="50,50,50,50" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>

一切看起来都符合预期:

masked
(来源: ailon.org)

现在我们在 Canvas 上添加一条线,在边界左侧突出 10 个像素:
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"
/>

并且蒙版向左移动 10 个像素:

shifted mask
(来源: ailon.org)

更新 2 :作为一种解决方法,我在边界外添加了一个大得离谱的透明矩形并相应地调整了我的蒙版,但这是一个非常讨厌的解决方法。

更新 3 :注意:带有矩形和线条的 Canvas 只是作为某些对象超出其边界的示例。在本示例的上下文中,它应该被视为某种黑匣子。你不能改变它的属性来解决一般问题。这与只是移动线使其不突出是一样的。

最佳答案

确实很有趣的问题 - 这就是我的想法:您所经历的影响似乎是由 Viewport 决定的。 TileBrush 的概念/行为(有关完整图片,请参见 Viewbox )。显然是隐含的 bounding box FrameworkElement (即您的情况下的 Canvas )受到元素以微妙的方式超出边界的影响/扩展,即框的尺寸扩展但框的坐标系不缩放,而是扩展到超出范围界方向。

以图形方式说明可能更容易,但由于时间限制,我将首先提供一个解决方案,并解释我目前采取的步骤以帮助您入门:

解决方案:

<Border Background="LightBlue" Width="198" Height="198">
<Border.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="-10,0,220,200" />
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">...</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.OpacityMask>
<Canvas x:Name="myGrid">...</Canvas>
</Border>

请注意,为了像素精度,我在这里和那里调整了 +/- 2 个像素的单位,但不知道偏移的来源,但我认为为了示例的目的,可以忽略这一点,并在需要时稍后解决。

说明 :

为了简化说明,通常应该首先明确所有相关的隐含/自动属性。

内边框从外边框接收 198 的自动尺寸(240 - 20 填充 - 由实验推导出的 2 个像素;不知道它们的来源,但现在可以忽略),也就是说,如果您将其指定如下,则不会发生任何变化,而使用其他值会产生图形变化:
<Border Background="LightBlue" Width="198" Height="198">...</Border>

进一步默认暗示 Viewport ViewportUnits 像这样:
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top" 
Viewport="0,0,1,1" ViewportUnits="RelativeToBoundingBox">...</DrawingBrush>

您正在通过覆盖 Stretch 来强制执行 DrawingBrush 大小与 None , 同时保持基础图块的位置和尺寸为默认值和 相对于其边界框 .此外,您(可以理解)正在覆盖 AlignmentX / AlignmentY ,它决定了基础图块内的位置,即在其边界框内。将它们重置为默认值 Center已经说明了:掩码相应地移动,意思是 它必须小于边界框 ,否则他们将无所作为。

这可以通过更改 ViewportUnits 进一步实现。至 Absolute ,当然,在正确调整单位之前,它根本不会产生任何图形;同样,通过实验,以下显式值与自动值匹配,而使用其他值会产生图形变化:
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center" 
Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush>

现在 不透明蒙版已与控件正确对齐 .显然还有一个问题,因为掩码现在正在剪断线,考虑到它的大小和没有任何 Stretch ,这并不奇怪。影响。相应地调整其大小和位置可解决此问题:
<RectangleGeometry Rect="-10,0,220,200" />


<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center" 
Viewport="-10,0,222,202" ViewportUnits="Absolute">...</DrawingBrush>

最后是 不透明度掩码匹配控件边界 如预期的!

补充:

可以在运行时通过 VisualTreeHelper Class 检索由上述解释中的推导和实验确定的所需偏移量。 :
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);

根据您的视觉元素组成和需求,您可能需要考虑 LayoutInformation Class 并建立两者的联合以获得无所不包的边界框:
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Rect layoutSlot = LayoutInformation.GetLayoutSlot(myGrid);
Rect boundingBox = descendantBounds;
boundingBox.Union(layoutSlot);

有关这两个主题的更多详细信息,请参阅以下链接:
  • Windows Presentation FoundationGraphics Rendering Overview ,
    特别VisualTreeHelperClass
  • The Layout System , 尤其
    Element Bounding Boxes
  • 关于wpf - 精确的不透明蒙版,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1301195/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com