C#, WPF - VisualBrush and OpacityMask

9.9k views Asked by At

I'm needing to create a UserControl, that has a portion of the control's background transparent. The transparent portion is cutout in the shape of a Border with CornerRadius of 2--it's required because of the design.

Here is my code that is not working:

    <UserControl Margin="1" x:Name="Box">
        <UserControl.Resources>
            <Style TargetType="UserControl">
                <Setter Property="Height" Value="16" />
            </Style>
        </UserControl.Resources>
        <Grid>
            <Border CornerRadius="2" BorderThickness="0">
                <Border.Background>
                    <SolidColorBrush Color="Black" Opacity=".3" />
                </Border.Background>
                <Border.OpacityMask>
                    <VisualBrush>
                        <VisualBrush.Visual>
                            <Grid 
                                Background="Black" 
                                Width="{Binding ElementName=Box, Path=ActualWidth}"
                                Height="{Binding ElementName=Box, Path=ActualHeight}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="50" />
                                    <ColumnDefinition />
                                </Grid.ColumnDefinitions>
                                <Border Grid.Column="1" Margin="1" CornerRadius="2" Background="Transparent" BorderThickness="0" />
                            </Grid>
                        </VisualBrush.Visual>
                    </VisualBrush>
                </Border.OpacityMask>
            </Border>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="50" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>

                <TextBlock 
                    VerticalAlignment="Center" TextAlignment="Right" FontSize="10" Margin="2"
                    Foreground="White" Text="Property" />

                <TextBlock 
                    Grid.Column="1" VerticalAlignment="Center" TextAlignment="Center" FontSize="10" Margin="2"
                    Text="Value" />
            </Grid>
        </Grid>
    </UserControl>

I made a few changes, so you guys should be able to drop this straight into XamlPad.

For some reason my VisualBrush that is set to the Border's OpacityMask is not working at all. The OpacityMask is just displaying everything fully visible. For a test, I dropped a quick LinearGradientBrush in and it worked as expected.

Is there some issue using VisualBrush and OpacityMask together? What is going wrong here?

Here is a screenshot of what I'm trying to achieve:

ScreenShot http://monitor.utopiaselfscan.com/Screen.png

The UserControl are the headers saying Entity No, Progress, Hours, etc. They are black with 30% transparency and have a rounded rectangle opacity mask cutout. I normally use images to render stuff like this, b/c our graphic artist can get crazy with glass-looking effects.

2

There are 2 answers

0
jonathanpeppers On BEST ANSWER

I am using a created Path to create this opacity mask.

You can find the object I use as a mask from this post:

Odd-Shape Geometry

2
Anvaka On

Who is the Box in your code? Could you also add an image of what you want to achieve?

Have you tried Paths to get what you want? E.g. the following path:

<Path Stroke="Black" Stretch="Fill" StrokeThickness="1" Fill="#CCCCFF">
    <Path.Data>
     <GeometryGroup FillRule="EvenOdd" >
      <EllipseGeometry Center="40,70" RadiusX="30" RadiusY="30" />              
      <RectangleGeometry Rect="30,55 100 30" />
    </GeometryGroup>
  </Path.Data>
</Path>

Gives you this cutout: alt text http://img704.imageshack.us/img704/928/cutw.jpg

EDIT: Here is one way to achieve your design:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Page.Resources>
      <SolidColorBrush x:Key="FillBrush" Color="Gray" Opacity="0.3"/>
   </Page.Resources>
   <Grid>
   <!-- Set background image here, instead of border-->
      <Border>
         <Border.Background>
            <LinearGradientBrush>
               <GradientStop Color="#FFcacaca"/>
               <GradientStop Offset="1" Color="#FF353535"/>
            </LinearGradientBrush>
         </Border.Background>
      </Border>
   <!-- Content goes here -->
      <Grid>
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
         </Grid.ColumnDefinitions>
         <Border
            MinHeight="24"
            MinWidth="100"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            Background="{StaticResource FillBrush}"
            CornerRadius="15, 0, 0, 15"
            Padding="0, 0, 5, 0">
            <TextBlock
               HorizontalAlignment="Right"
               VerticalAlignment="Center"
               Foreground="White"
               Text="From"/>
         </Border>
         <Border
            MinHeight="24"
            MinWidth="100"
            Grid.Column="1"
            HorizontalAlignment="Left"
            VerticalAlignment="Top"
            BorderBrush="{StaticResource FillBrush}"
            BorderThickness="1"
            CornerRadius="0, 15, 15, 0">
            <TextBox
               Margin="5, 0, 5, 0"
               VerticalAlignment="Center"
               Background="Transparent"
               BorderThickness="0"
               Foreground="#2f3740"
               Text="Verylongname, Johnathan"/>
         </Border>
      </Grid>
   </Grid>
</Page>

The main point is to use the two borders and one brush. For header fields you paint border's background and for content fields you paint border's border :).

Cheers, Anvaka.