【问题标题】:Exclude label text from border从边框中排除标签文本
【发布时间】:2024-11-08 10:25:01
【问题描述】:

我有一个银色边框,上面有我想排除的文本(从边框剪切文本)以透视边框后面的背景,它应用了阴影位图效果。 这是否可能,无需去 Photoshop 来创建一个基本上做同样事情的图像,又不那么灵活?

如果可能,我将如何完成这样的任务?

【问题讨论】:

  • 我不完全明白你想要做什么。您能否发布屏幕截图或一些示例代码?
  • 感谢您的回复...我把它放在了 Photoshop 中,希望对您有所帮助...users.indytel.com/~jsmain/downloads/NowPlaying.png。我想通过银色边框看到背景,以及边框的阴影效果,就像这张图片一样。我似乎无法弄清楚如何从 xaml 的边框中排除文本几何图形。

标签: c# wpf label border


【解决方案1】:

我对此进行了一些尝试,最终得到了以下结果。
棘手的部分是为边界“反转” OpacityMask。我创建了一个派生自 Image 的类,它添加了一些依赖属性,如 Text、FontFamily 和 EmSize。然后它使用this link 的方法将文本转换为几何图形。

您可以随意使用 Text、FontFamily、EmSize、Width 和 Height,直到获得所需的结果。当然,您也可以向 InvertOpacityText 添加额外的 DP 以增加灵活性。

我最终得到了这个

<Grid Background="Blue">
    <Border Background="Gray" CornerRadius="8,8,8,8" Width="240" Height="220">
        <Border.Effect>
            <DropShadowEffect ShadowDepth="10"
                              Direction="310"
                              Color="Black"
                              Opacity="0.8"
                              BlurRadius="4"/>
        </Border.Effect>
        <Border.OpacityMask>
            <VisualBrush>
                <VisualBrush.Visual>
                    <local:InvertOpacityText Text=" Now Playing"
                                             EmSize="70"
                                             Stretch="Fill"
                                             Width="510"
                                             Height="414"
                                             FontFamily="Broadway">
                        <local:InvertOpacityText.LayoutTransform>
                            <RotateTransform Angle="-90"/>
                        </local:InvertOpacityText.LayoutTransform>
                    </local:InvertOpacityText>
                </VisualBrush.Visual>
            </VisualBrush>
        </Border.OpacityMask>
        <Image Margin="45,5,5,5" Source="C:\PhilCollins.png"/>
    </Border>
</Grid>

InvertOpacityText.cs

public class InvertOpacityText : Image
{
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text",
                                    typeof(string),
                                    typeof(InvertOpacityText),
                                    new FrameworkPropertyMetadata(string.Empty,
                                                                  TargetPropertyChanged));
    public static readonly DependencyProperty EmSizeProperty =
        DependencyProperty.Register("EmSize",
                                    typeof(double),
                                    typeof(InvertOpacityText),
                                    new FrameworkPropertyMetadata(70.0,
                                                                  TargetPropertyChanged));
    public static readonly DependencyProperty FontFamilyProperty =
        DependencyProperty.Register("FontFamily",
                                    typeof(FontFamily),
                                    typeof(InvertOpacityText),
                                    new FrameworkPropertyMetadata(new FontFamily(),
                                                                  TargetPropertyChanged));

    private static void TargetPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        InvertOpacityText invertOpacityText = (InvertOpacityText)source;
        invertOpacityText.OnTextChanged();
    }

    public string Text
    {
        get { return (string)base.GetValue(TextProperty); }
        set { base.SetValue(TextProperty, value); }
    }
    public double EmSize
    {
        get { return (double)base.GetValue(EmSizeProperty); }
        set { base.SetValue(EmSizeProperty, value); }
    }
    public FontFamily FontFamily
    {
        get { return (FontFamily)base.GetValue(FontFamilyProperty); }
        set { base.SetValue(FontFamilyProperty, value); }
    }

    private void OnTextChanged()
    {
        if (Source == null)
        {
            Source = CreateBitmapSource();
        }

        FormattedText tx = new FormattedText(Text,
                                             Thread.CurrentThread.CurrentUICulture,
                                             FlowDirection.LeftToRight,
                                             new Typeface(FontFamily,
                                                          FontStyles.Normal,
                                                          FontWeights.Bold,
                                                          FontStretches.Normal),
                                             EmSize,
                                             Brushes.Black);
        Geometry textGeom = tx.BuildGeometry(new Point(0, 0));
        Rect boundingRect = new Rect(new Point(-100000, -100000), new Point(100000, 100000));
        RectangleGeometry boundingGeom = new RectangleGeometry(boundingRect);
        GeometryGroup group = new GeometryGroup();
        group.Children.Add(boundingGeom);
        group.Children.Add(textGeom);
        Clip = group;
    }
    private BitmapSource CreateBitmapSource()
    {
        int width = 128;
        int height = width;
        int stride = width / 8;
        byte[] pixels = new byte[height * stride];
        List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>();
        colors.Add(System.Windows.Media.Colors.Red);
        colors.Add(System.Windows.Media.Colors.Blue);
        colors.Add(System.Windows.Media.Colors.Green);
        BitmapPalette myPalette = new BitmapPalette(colors);
        return BitmapSource.Create(width,
                                   height,
                                   96,
                                   96,
                                   PixelFormats.Indexed1,
                                   myPalette,
                                   pixels,
                                   stride);
    }
}

【讨论】:

  • Meleak,这绝对是美丽的!谢谢!
【解决方案2】:

这是一个解决方案,它通过创建一个名为HollowTextBlock 的新FrameworkElement 来工作,该HollowTextBlock 用它的Background 填充其分配的大小,并剪掉Text 使其保持透明。一个成熟的实现需要支持更多的属性,但这证明了这个概念是有效的。

首先,这里是一些示例 XAML:

<Grid>
    <Grid>
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                <GradientStop Color="#FF0000" Offset="0" />
                <GradientStop Color="#0000FF" Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
    </Grid>
    <Grid>
        <local:HollowTextBlock Width="200" Height="50" Text="Hello, world!" Background="White" HorizontalAlignment="Center"/>
    </Grid>
</Grid>

它在文本后面定义了一个彩色渐变背景,因此我们可以看到它正在工作。然后我们创建我们的HollowTextBlock 并且在原型中WidthHeightTextBackground 都必须指定。

那么这里是我们对HollowTextBlock的实现:

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 UIPropertyMetadata(string.Empty));

    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));

    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("Arial");
        var size = 32;
        var ft = new FormattedText(Text, Thread.CurrentThread.CurrentUICulture, FlowDirection.LeftToRight, face, size, Brushes.Black);
        var hole = ft.BuildGeometry(new Point((RenderSize.Width - ft.Width) / 2, (RenderSize.Height - ft.Height) / 2));
        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();
    }
}

渲染后的输出如下所示:

注意使用它时,您必须小心不要将它放在会遮盖您想要显示的真实背景的东西之上。

【讨论】:

  • 谢谢瑞克!我试试看!感谢您的帮助。
  • 不确定我是否真的可以在玩过它之后做我想做的事。恐怕我还不明白那里发生了什么。我需要像我的示例中那样旋转文本,并且绝对希望将其放在动态大小的边框内以容纳我的专辑封面(CD 和 DVD)。