Wpf 精密不透明掩模
假设我需要在WPF控件上设置一个不透明度遮罩,使其在精确位置高亮显示一部分(假设在(50;50)位置有一个50x50正方形)。为此,我创建了一个DrawingGroup,其中包含2个GeometryDrawing对象:1个半透明矩形表示控件的整个实际大小,1个不透明矩形表示高亮显示区域。然后我从这个DrawingGroup创建了一个DrawingBrush,将它的Stretch属性设置为None,并将这个笔刷设置为需要屏蔽的控件的OpacityMask 所有这些都可以正常工作,但没有任何东西“超出”上述控制范围。但是,如果控件在其边界之外绘制某些内容,则外部点将成为应用不透明度遮罩的起点(如果笔刷与该侧对齐),并且整个遮罩将移动该距离,从而导致意外行为 我似乎找不到一种方法来强制从控件的边界应用遮罩,或者至少获得控件的实际边界(包括粘贴部分),以便我可以相应地调整遮罩 高度赞赏任何想法 更新:下面是一个简单的测试用例XAML和屏幕截图,演示了该问题: 我们在最后一个中有两个嵌套的边框和画布,带有上述正方形:Wpf 精密不透明掩模,wpf,layout,opacity,mask,viewport,Wpf,Layout,Opacity,Mask,Viewport,假设我需要在WPF控件上设置一个不透明度遮罩,使其在精确位置高亮显示一部分(假设在(50;50)位置有一个50x50正方形)。为此,我创建了一个DrawingGroup,其中包含2个GeometryDrawing对象:1个半透明矩形表示控件的整个实际大小,1个不透明矩形表示高亮显示区域。然后我从这个DrawingGroup创建了一个DrawingBrush,将它的Stretch属性设置为None,并将这个笔刷设置为需要屏蔽的控件的OpacityMask 所有这些都可以正常工作,但没有任何东西“超
<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>
下面是它的外观:
(来源:) 现在,我们在第二个边框上添加一个不透明材质,使其除正方形外的所有部分都是半透明的:
<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>
一切如预期:
(来源:) 现在我们在画布上添加一条线,在边框左侧突出10个像素:
<Line X1="-10" Y1="150" X2="120" Y2="150"
Stroke="Red" StrokeThickness="2"
/>
遮罩向左移动10个像素:
(来源:) 更新2:作为一种解决方法,我在边界外添加了一个大得离谱的透明矩形,并相应地调整了我的遮罩,但这是一种非常糟糕的解决方法
更新3:注意:带有矩形和线条的画布就在那里,作为某个对象的示例,该对象的某些内容超出了它的边界。在本示例的上下文中,它应该被视为某种黑匣子。您无法更改其属性来解决一般问题。这与移动线条使其不突出是一样的。一个比当前解决方案更理想的解决方案是在更高的级别上简单地应用
OpacityMask
。例如,使用此演示代码,您可以从边框
中删除掩码,并将其应用于窗口
。稍微调整一下,它就合适了:
<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>
当调整窗口的大小时,您必须编写一些代码来移动掩码,因此您最好在代码隐藏中动态生成掩码
我的问题是,为什么需要处理超出画布
边界的几何体?在画布对象上添加cliptobunds=“True”
由于控件中有突出的部分,一种方法是将控件图像与控件遮罩分离
<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>
确实是一个有趣的问题——我认为:你所经历的效果似乎是由的概念/行为决定的(完整的图片也请参见)。显然,a的隐式(即,在您的案例中的画布)会受到以微妙方式伸出边界的元素的影响/扩展,即,长方体的尺寸会扩展,但长方体的坐标系不会缩放,而是会扩展到超出边界的方向
用图形说明可能更容易,但由于时间限制,我将首先提供一个解决方案,并将解释我目前采取的步骤,以便让您开始:
解决方案:
<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>
通过使用None
覆盖,同时将基础平铺的位置和尺寸保持为默认值,并相对于其边界框,可以强制使用DrawingBrush大小。此外,您(可以理解)正在覆盖/,这决定了基础平铺中的位置,即其边界框中的位置。将它们重置为默认值中心
已经说明了这一点:遮罩会相应地移动,这意味着它必须小于边界框,否则它们的中心将不存在
这可以通过更改为绝对值
(绝对值)
(绝对值)(绝对值)(绝对值)(绝对值)(绝对值)(绝对值)>(绝对值)>(绝对值)(绝对值);同样,通过实验,以下显式值与自动值匹配,而使用其他值会产生图形变化:
<Border Background="LightBlue" Width="198" Height="198">...</Border>
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush>
及
根据您的视觉元素组成和需要,您可能需要
<DrawingBrush Stretch="None" AlignmentX="Left" AlignmentY="Top"
Viewport="0,0,1,1" ViewportUnits="RelativeToBoundingBox">...</DrawingBrush>
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="0,0,202,202" ViewportUnits="Absolute">...</DrawingBrush>
<RectangleGeometry Rect="-10,0,220,200" />
<DrawingBrush Stretch="None" AlignmentX="Center" AlignmentY="Center"
Viewport="-10,0,222,202" ViewportUnits="Absolute">...</DrawingBrush>
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(myGrid);
Rect layoutSlot = LayoutInformation.GetLayoutSlot(myGrid);
Rect boundingBox = descendantBounds;
boundingBox.Union(layoutSlot);