11

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.

Ben
  • 3,972
  • 8
  • 43
  • 82

4 Answers4

8

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.

Clemens
  • 123,504
  • 12
  • 155
  • 268
2

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.

kwesolowski
  • 695
  • 8
  • 18
Daniel Sklenitzka
  • 2,116
  • 1
  • 17
  • 25
2

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.stack.imgur.com/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>
Athari
  • 33,702
  • 16
  • 105
  • 146
1

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>
Andrzej Turski
  • 626
  • 4
  • 5