如何在WPF中的非透明用户控件上制作透明部分

How to make transparent part on non-transparent user control in WPF

在我的 MainWindow.xaml 上有用户控制图块,每个图块的背景设置如下:

ucTile.Background = new SolidColorBrush(Colors.Some);

取决于各种用户设置。

现在我想向我的应用程序添加一个新功能:可以为主 window 设置自定义背景图像。

但是我需要我的图块的某些部分是透明的,以显示它们下面的图像。 像这样:

这是我的瓷砖现在的样子:

如何实现?我不能将我的 ucTile 背景设置为透明,因为如果应该保留颜色。 是否有一些组件 inf WPF 可以充当 "hole" 显示非透明元素下的内容?

简单的解决方案:

<Window Background="Transparent">
    <Polygon Fill="LightBlue"/>
</Window>

您需要添加 Points 以获得所需的形状!

阅读更多关于多边形的内容here!

这里有一些细节,因为有些人觉得无缘无故地否决是好的...

用户控件

<UserControl x:Class="WpfApp1.FolderControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:WpfApp1"
         mc:Ignorable="d">
<Grid Margin="10">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Polygon Fill="Red"
             Grid.RowSpan="2"
             Points="0,0 , 50,0 , 55,10 , 100,10 , 100,80 , 0,80 " />
    <Image Source="if_desktop_83651.png" Margin="5,20,5,5" Height="24" Width="24"></Image>
    <Border Background="Green"
            Grid.Row="1"
            Margin="5">
        <TextBlock Text="Caption" 
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   Foreground="White"/>
    </Border>
</Grid>

Window

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525"
    Background="Green">
<local:FolderControl HorizontalAlignment="Center"
                     VerticalAlignment="Center"/>

结果

优点:

  • 你可以做任何你想要的形状

缺点:

  • 不会调整大小 -> 在代码
  • 中计算 Points

您可以在其 ControlTemplate 中为 UserControl 的大纲设置一个 Path 元素。 Path 的 FillStrokeStrokeThickness 属性绑定到 UserControl 的 BackgroundBorderBrush 和(新定义的)OutlineThickness,因此您可以照常设置它们。

<UserControl x:Class="YourNamespace.Tile" ...>
    <UserControl.Template>
        <ControlTemplate TargetType="local:Tile">
            <Grid>
                <Path Fill="{TemplateBinding Background}"
                      Stroke="{TemplateBinding BorderBrush}"
                      StrokeLineJoin="Round"
                      StrokeThickness="{TemplateBinding OutlineThickness}"
                      Data="{TemplateBinding Outline}"/>

                <ContentPresenter
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Grid>
        </ControlTemplate>
    </UserControl.Template>
    <Grid>
        ...
    </Grid>
</UserControl>

Path 绑定到 Tile 控件的两个依赖项 属性:

public partial class Tile : UserControl
{
    public Tile()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty OutlineProperty =
        DependencyProperty.Register(
            nameof(Outline), typeof(Geometry), typeof(Tile));

    public Geometry Outline
    {
        get { return (Geometry)GetValue(OutlineProperty); }
        set { SetValue(OutlineProperty, value); }
    }

    public static readonly DependencyProperty OutlineThicknessProperty =
        DependencyProperty.Register(
            nameof(OutlineThickness), typeof(double), typeof(Tile), 
            new PropertyMetadata(1.0, (o, e) => ((Tile)o).UpdateOutline()));

    public double OutlineThickness
    {
        get { return (double)GetValue(OutlineThicknessProperty); }
        set { SetValue(OutlineThicknessProperty, value); }
    }

    protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
    {
        base.OnRenderSizeChanged(sizeInfo);
        UpdateOutline();
    }

    private void UpdateOutline()
    {
        var geometry = new StreamGeometry();
        var size = RenderSize;
        var border = OutlineThickness / 2d;

        using (var sgc = geometry.Open())
        {
            sgc.BeginFigure(new Point(border, border), true, true);
            sgc.LineTo(new Point(size.Width - 20, border), true, true);
            sgc.LineTo(new Point(size.Width - border, 20), true, true);
            sgc.LineTo(new Point(size.Width - border, size.Height - border), true, true);
            sgc.LineTo(new Point(0, size.Height - border), true, true);
        }

        Outline = geometry;
    }
}

现在根据需要扩展 UpdateOutline 方法。


如果 XAML 设计者抱怨 ControlTemplate 的 TargetType,你不妨使用这个:

<UserControl.Template>
    <ControlTemplate TargetType="UserControl">
        <Grid>
            <Path Fill="{TemplateBinding Background}"
                  Stroke="{TemplateBinding BorderBrush}"
                  StrokeLineJoin="Round"
                  StrokeThickness="{Binding OutlineThickness, RelativeSource={RelativeSource TemplatedParent}}"
                  Data="{Binding Outline, RelativeSource={RelativeSource TemplatedParent}}"/>

            <ContentPresenter
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Grid>
    </ControlTemplate>
</UserControl.Template>