反转 OpacityMask

4
考虑以下 Xaml 代码:
<Grid Background="Blue">
    <Border Width="100" Height="60" BorderBrush="Black" BorderThickness="2">
        <Border Background="Red">
            <Border.OpacityMask>
                <VisualBrush>
                    <VisualBrush.Visual>
                        <TextBlock Text="Text"
                                   Foreground="#FF000000"
                                   Background="#00000000"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Border.OpacityMask>
        </Border>
    </Border>
</Grid>

由于 OpacityMask 的非透明部分只有 TextBlock 的前景,所以它会看起来像这样。
alt text 现在如果我将 TextBlock 中的前景和背景颜色进行切换,就会变成这样。
<TextBlock Text="Text"
           Foreground="#00000000"
           Background="#FF000000"/>

我明白这是因为虽然前景是透明的,但背后的背景不是,导致无用的OpacityMask :)
alt text 有没有办法获得这个效果?基本上是反转的OpacityMask
alt text 我是否错过了其他实现方式?
更新
澄清一下,尽管我的示例是关于TextBlock的,但它可以是任何东西。Ellipse / Image / Path等。我想要的功能是“反转OpacityMask”。
1个回答

4
你可以使用我的HollowTextBlock,它是对同一个问题的不同答案:
<Grid Background="Blue">
    <Border Width="100" Height="60" BorderBrush="Black" BorderThickness="2">
        <Border Background="Red">
            <Border.OpacityMask>
                <VisualBrush Stretch="None">
                    <VisualBrush.Visual>
                        <local:HollowTextBlock Width="200" Height="50" Text="Text" Background="White" HorizontalAlignment="Center"/>
                    </VisualBrush.Visual>
                </VisualBrush>
            </Border.OpacityMask>
        </Border>
    </Border>
</Grid>

更新:

这是一个更加完善的 HollowTextBlock 版本,具备适当的测量能力、通常文本属性的属性值继承,以及一个新的 VerticalTextAlignment 属性,用于在其分配的空间中垂直居中文本:

public class HollowTextBlock : FrameworkElement
{
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(HollowTextBlock), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(HollowTextBlock.OnTextChanged), new CoerceValueCallback(HollowTextBlock.CoerceText)));

    public Brush Background
    {
        get { return (Brush)GetValue(BackgroundProperty); }
        set { SetValue(BackgroundProperty, value); }
    }

    public static readonly DependencyProperty BackgroundProperty =
        TextElement.BackgroundProperty.AddOwner(typeof(HollowTextBlock), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));

    public double FontSize
    {
        get { return (double)GetValue(FontSizeProperty); }
        set { SetValue(FontSizeProperty, value); }
    }

    public static readonly DependencyProperty FontSizeProperty =
        TextElement.FontSizeProperty.AddOwner(typeof(HollowTextBlock));

    public FontFamily FontFamily
    {
        get { return (FontFamily)GetValue(FontFamilyProperty); }
        set { SetValue(FontFamilyProperty, value); }
    }

    public static readonly DependencyProperty FontFamilyProperty =
        TextElement.FontFamilyProperty.AddOwner(typeof(HollowTextBlock));

    public FontStyle FontStyle
    {
        get { return (FontStyle)GetValue(FontStyleProperty); }
        set { SetValue(FontStyleProperty, value); }
    }

    public static readonly DependencyProperty FontStyleProperty =
        TextElement.FontStyleProperty.AddOwner(typeof(HollowTextBlock));

    public FontWeight FontWeight
    {
        get { return (FontWeight)GetValue(FontWeightProperty); }
        set { SetValue(FontWeightProperty, value); }
    }

    public static readonly DependencyProperty FontWeightProperty =
        TextElement.FontWeightProperty.AddOwner(typeof(HollowTextBlock));

    public FontStretch FontStretch
    {
        get { return (FontStretch)GetValue(FontStretchProperty); }
        set { SetValue(FontStretchProperty, value); }
    }

    public static readonly DependencyProperty FontStretchProperty =
        TextElement.FontStretchProperty.AddOwner(typeof(HollowTextBlock));

    public TextAlignment TextAlignment
    {
        get { return (TextAlignment)GetValue(TextAlignmentProperty); }
        set { SetValue(TextAlignmentProperty, value); }
    }

    public static readonly DependencyProperty TextAlignmentProperty =
        Block.TextAlignmentProperty.AddOwner(typeof(HollowTextBlock));

    public VerticalAlignment VerticalTextAlignment
    {
        get { return (VerticalAlignment)GetValue(VerticalTextAlignmentProperty); }
        set { SetValue(VerticalTextAlignmentProperty, value); }
    }

    public static readonly DependencyProperty VerticalTextAlignmentProperty =
        DependencyProperty.Register("VerticalTextAlignment", typeof(VerticalAlignment), typeof(HollowTextBlock), new FrameworkPropertyMetadata(VerticalAlignment.Top, FrameworkPropertyMetadataOptions.AffectsRender));

    private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        OnTextChanged(d, (string)e.NewValue);
    }

    private static void OnTextChanged(DependencyObject d, string newText)
    {
    }

    private static object CoerceText(DependencyObject d, object baseValue)
    {
        return baseValue;
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        var face = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
        var size = FontSize;
        var ft = new FormattedText(Text, Thread.CurrentThread.CurrentUICulture, FlowDirection.LeftToRight, face, size, Brushes.Black);
        return new Size(ft.Width, ft.Height);
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        var extent = new RectangleGeometry(new Rect(0.0, 0.0, RenderSize.Width, RenderSize.Height));
        var face = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch);
        var size = FontSize;
        var ft = new FormattedText(Text, Thread.CurrentThread.CurrentUICulture, FlowDirection.LeftToRight, face, size, Brushes.Black);
        var originX = GetHorizontalOrigin(ft.Width, RenderSize.Width);
        var originY = GetVerticalOrigin(ft.Height, RenderSize.Height);
        var hole = ft.BuildGeometry(new Point(originX, originY));
        var combined = new CombinedGeometry(GeometryCombineMode.Exclude, extent, hole);
        drawingContext.PushClip(combined);
        drawingContext.DrawRectangle(Background, null, new Rect(0.0, 0.0, RenderSize.Width, RenderSize.Height));
        drawingContext.Pop();
    }

    private double GetHorizontalOrigin(double textWidth, double renderWidth)
    {
        switch (TextAlignment)
        {
            case TextAlignment.Center:
                return (renderWidth - textWidth) / 2;
            case TextAlignment.Left:
                return 0;
            case TextAlignment.Right:
                return renderWidth - textWidth;
        }
        return 0;
    }

    private double GetVerticalOrigin(double textHeight, double renderHeight)
    {
        switch (VerticalTextAlignment)
        {
            case VerticalAlignment.Center:
                return (renderHeight - textHeight) / 2;
            case VerticalAlignment.Top:
                return 0;
            case VerticalAlignment.Bottom:
                return renderHeight - textHeight;
        }
        return 0;
    }
}

2
+1,太棒了!我试过了,它可以用 :) 然而,这个解决方案也感觉有点“hacky”,就像我的那样,我希望框架中有内置的东西可以让你只需使用 "InvertOpacityMask="True" 或类似的东西。现在,我知道没有这样的功能,除非有人有一种解决方法,否则我将在几天后将其标记为已接受。谢谢 - Fredrik Hedblad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接