假设我需要在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>
以下是它的外观:
(来源: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>
一切看起来都像预期的那样:
(来源:ailon.org)
现在我们在画布上添加一条线,在我们的边框左侧粘贴10个像素:
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"
/>
掩码向左移10个像素:
(来源:ailon.org)
Update2 :作为一种解决方法,我在边界外添加了一个非常大的透明矩形并相应地调整了我的面具,但这是一个非常讨厌的解决方法。
Update3 :注意:带有矩形和直线的画布就像某个对象的示例一样,它有一些超出界限的对象。在此示例的上下文中,它应被视为某种黑盒子。您无法更改其属性来解决一般问题。这与移动线条相同,因此它不会突出。
答案 0 :(得分:8)
确实有趣的问题 - 这就是我的想法:你所经历的效果似乎取决于Viewport
的TileBrush
概念/行为(完整见Viewbox
}图片)。显然,bounding box的隐式FrameworkElement
(即你的情况下的画布)受到以微妙方式突出界限的元素的影响/扩展,即框的尺寸扩大但是坐标框的系统不会缩放,而是扩展到越界方向。
以图形方式说明可能更容易,但由于时间限制,我将首先提供一个解决方案,并解释我目前采取的步骤,以便让您入门:
<强>解决方案:强>
<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>
您通过使用None
覆盖Stretch
来强制执行DrawingBrush大小,同时保持基本图块的位置和尺寸为默认值,相对于其边界框。此外,您(可以理解)覆盖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);
有关这两个主题的更多详细信息,请参阅以下链接:
答案 1 :(得分:1)
在Canvas对象上添加ClipToBounds =“True”。
<Canvas ClipToBounds="True">
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White" />
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"/>
</Canvas>
答案 2 :(得分:0)
可能比您当前更理想的一种解决方法是简单地在更高级别应用OpacityMask
。例如,使用此演示代码,您可以从Border
中删除掩码并将其应用于Window
。通过一些调整,它适合:
<Window.OpacityMask>
<DrawingBrush AlignmentX="Left" AlignmentY="Top" Stretch="None">
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#30000000">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="0,0,300,300"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
<GeometryDrawing Brush="Black">
<GeometryDrawing.Geometry>
<RectangleGeometry Rect="92,82,50,50"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Window.OpacityMask>
在Window
调整大小时,您必须编写一些代码来移动掩码,因此您可能最好在代码隐藏中动态生成掩码。
我的问题是,为什么你需要处理超出Canvas
范围的几何?
答案 3 :(得分:0)
由于你有一些从控件中伸出的部分,一个想法就是将控制图像与控制面板分开。
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Border Padding="20" Background="DarkGray" Width="240" Height="240"> <!-- user container -->
<Grid> <!-- the control -->
<Border Background="LightBlue" HorizontalAlignment="Stretch"> <!-- control mask-->
<Canvas>
<Rectangle Canvas.Left="50" Canvas.Top="50" Width="50" Height="50"
Stroke="Red" StrokeThickness="2"
Fill="White"
/>
<Canvas.OpacityMask>
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top" TileMode="None">
<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>
</Canvas.OpacityMask>
</Canvas>
</Border>
<Canvas> <!-- control image-->
<Line X1="-10" Y1="150" X2="120" Y2="150" Stroke="Red" StrokeThickness="2"/>
</Canvas>
</Grid>
</Border>
</Window>