Ben
Ben

Reputation: 4110

Fill the outside of a rectangle

I would like to draw a rectangle in WPF (by code) and to fill the outside of it.

Here is an example :

enter image description here

The outside of the rectangle is grey (with low opacity), and the fill of the rectangle is trasparent.

Upvotes: 11

Views: 4899

Answers (4)

Andrzej Turski
Andrzej Turski

Reputation: 636

This is a variation on the solution using OpacityMask. Instead of doing it in the code, it is done in XAML. Also, it reverses the logic: instead of drawing 4 border rectangles, it draws 2 rectangles on top of each other. Finally, the important property of this solution is that the size of the central transparent rectangle is relative to the image size (rather than in absolute pixels). You do not need to know the actual image size or how it is stretched/positioned (particularly important for Stretch="Uniform"). Here, I have specified the image size (maskRect) as 1,1 and used fractional numbers as relative mask size and position (transpRect). You may as well specify the image size as 100,100 and use percentage values for the mask (or, even use the actual pixel values).

          <Grid Background="#FFF4F4F5" >
            <Image Name="PhotoImage" Source="...">
                <Image.OpacityMask>
                    <DrawingBrush>
                        <DrawingBrush.Drawing>
                            <DrawingGroup>
                                <GeometryDrawing>
                                    <GeometryDrawing.Geometry>
                                        <RectangleGeometry x:Name="maskRect" Rect="0,0,1,1"/>
                                    </GeometryDrawing.Geometry>
                                    <GeometryDrawing.Brush>
                                        <SolidColorBrush Color="#60000000" />
                                    </GeometryDrawing.Brush>
                                </GeometryDrawing>
                                <GeometryDrawing>
                                    <GeometryDrawing.Geometry>
                                        <RectangleGeometry x:Name="transpRect" Rect=".25,.20,.40,.40"/>
                                    </GeometryDrawing.Geometry>
                                    <GeometryDrawing.Brush>
                                        <SolidColorBrush Color="Black" />
                                    </GeometryDrawing.Brush>
                                </GeometryDrawing>
                            </DrawingGroup>
                        </DrawingBrush.Drawing>
                    </DrawingBrush>
                </Image.OpacityMask>
            </Image>
        </Grid>

Upvotes: 1

Clemens
Clemens

Reputation: 128106

You may also overlay your image with a semi-transparent Path element that uses a CombinedGeometry which combines one very large outer rectangle with an inner rectangle:

<Grid>
    <Image Name="image" Source="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"/>
    <Path Fill="#AAFFFFFF">
        <Path.Data>
            <CombinedGeometry GeometryCombineMode="Xor">
                <CombinedGeometry.Geometry1>
                    <RectangleGeometry Rect="0,0,10000,10000"/>
                </CombinedGeometry.Geometry1>
                <CombinedGeometry.Geometry2>
                    <RectangleGeometry x:Name="transparentRect" Rect="150,100,200,100"/>
                </CombinedGeometry.Geometry2>
            </CombinedGeometry>
        </Path.Data>
    </Path>
</Grid>

You would now programatically adjust the Rect property of the transparentRect member as needed.

Upvotes: 8

Daniel Sklenitzka
Daniel Sklenitzka

Reputation: 2236

You can use a combination of OpacityMask and DrawingBrush:

XAML:

<Grid Background="Gray">
    <Image Name="image"Source="...">
        <Image.OpacityMask>
            <DrawingBrush x:Name="mask"/>
        </Image.OpacityMask>
    </Image>
</Grid>

Code-behind:

    private void UpdateOpactiyMask()
    {
        Point topLeft = new Point();
        Point bottomRight = new Point(image.ActualWidth, image.ActualHeight);

        GeometryDrawing left = new GeometryDrawing();
        left.Brush = borderBrush;
        left.Geometry = new RectangleGeometry(new Rect(topLeft, new Point(SelectedArea.Left, bottomRight.Y)));

        GeometryDrawing right = new GeometryDrawing();
        right.Brush = borderBrush;
        right.Geometry = new RectangleGeometry(new Rect(new Point(SelectedArea.Right, topLeft.Y), bottomRight));

        GeometryDrawing top = new GeometryDrawing();
        top.Brush = borderBrush;
        top.Geometry = new RectangleGeometry(new Rect(new Point(SelectedArea.Left, topLeft.Y), new Point(SelectedArea.Right, SelectedArea.Top)));

        GeometryDrawing bottom = new GeometryDrawing();
        bottom.Brush = borderBrush;
        bottom.Geometry = new RectangleGeometry(new Rect(new Point(SelectedArea.Left, SelectedArea.Bottom), new Point(SelectedArea.Right, bottomRight.Y)));

        GeometryDrawing center = new GeometryDrawing();
        center.Brush = selectionBrush;
        center.Geometry = new RectangleGeometry(SelectedArea);

        DrawingGroup drawing = new DrawingGroup();
        drawing.Children.Add(left);
        drawing.Children.Add(right);
        drawing.Children.Add(top);
        drawing.Children.Add(bottom);
        drawing.Children.Add(center);

        mask.Drawing = drawing;
    }

SelectedArea is a Rect.

Upvotes: 2

Athari
Athari

Reputation: 34293

You can use UIElement.Clip property:

<Window x:Class="So17720970_RectangularBoublik.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        SizeToContent="WidthAndHeight">
    <Grid Width="500" Height="500">
        <Image Source="https://i.sstatic.net/Py65S.jpg"/>  <!-- image -->
        <Rectangle Fill="#AA000000">                          <!-- selection -->
            <Rectangle.Clip>
                <GeometryGroup FillRule="Nonzero">            <!-- selection clip: -->
                    <RectangleGeometry Rect="0 0 500 200"/>   <!--   top -->
                    <RectangleGeometry Rect="0 0 100 500"/>   <!--   left -->
                    <RectangleGeometry Rect="0 300 500 200"/> <!--   bottom -->
                    <RectangleGeometry Rect="400 0 100 500"/> <!--   right -->
                </GeometryGroup>
            </Rectangle.Clip>
        </Rectangle>
        <Rectangle StrokeThickness="1" Stroke="Black" StrokeDashArray="1 2" SnapsToDevicePixels="True"
                Margin="100 200 100 200"/>                    <!-- "ants" -->
    </Grid>
</Window>

Upvotes: 2

Related Questions