locked
How to achieve "exclude" clipping in WPF? RRS feed

  • Question


  • Hello,

    I'm having a problem with clipping in WPF. I need to clip the part of the drawing which is INSIDE a clipping polygon, while all WPF clipping facilities only allow to clip the visual elements which are OUTSIDE the clipping area.

    Let me describe my task in more detail. I have a group of polygons which are loaded from external files. Each polygon is represented by a PathGeometry object. I do not know in advance, how many polygons will be loaded, and how complex they are going to be.

    And there is one dedicated PathGeometry object which serves as a clipping region. It is also loaded from an external file, and its geometry is unpredictable.

    I need to display the first group of polygons on a DrawingContext, excluding any geometry which is inside the clipping region.

    Now, lets say I have a PathGeometry object named ClipPath. If call DrawingContext.PushClip(ClipPath), and then draw some polygons on that DrawingContext, only the parts of polygons that are INSIDE the ClipPath will be displayed. While I need to display everything that is OUTSIDE the ClipPath area.

    There may be some graphics in the background, so I can not just draw the ClipPath object with a white color on top of the other polygons.

    The WPF opacity masks do not work either, because:

    a) I'd have to create an opacity mask wide enough to cover the entire drawing, while it is very hard to precalculate the drawing extents in my environment.

    b) it seems to be impossible to dynamically generate an opacity mask which a transparent hole in the middle (please keep in mind that the hole geometry is not known statically).

    It was very easy to do this trick in GDI using the Graphics.ExcludeClip() method. Basically, I need some WPF analog of Graphics.ExcludeClip().

    Thanks in advance for any help.
    Friday, February 26, 2010 10:09 AM

Answers


  • Hello again,

    seems that I've found a solution. It is still a bit limited, because you need to know the total extents of your drawing. But in general the approach works well for me.

    The trick is to use CombinedGeometry as a clipping region. The CombinedGeometry must be wide enough to cover the entire drawing, and it must contain an "excluded" part in the middle - a hole, essentially. Here is the code that demonstrates it:

    protected override void OnRender(
    	System.Windows.Media.DrawingContext drawingContext)
    {
    	base.OnRender(drawingContext);
    
    	// Assume that we know the total extents of our drawing
    	RectangleGeometry totalDrawingExtents = new RectangleGeometry(
    		new System.Windows.Rect(0, 0, 400, 400));
    
    	// This is the geometry of a "hole"
    	RectangleGeometry hole = new RectangleGeometry(
    		new System.Windows.Rect(100, 100, 200, 200));
    
    	// Combine two objects, to get a total bounding box with a hole in the middle
    	CombinedGeometry combined = new CombinedGeometry(
    		GeometryCombineMode.Exclude, totalDrawingExtents, hole);
    
    	// Apply the clipping region
    	drawingContext.PushClip(combined);
    
    	// Display the geometrical objects that comprise the drawing
    	Pen pen1 = new Pen(new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)), 1.0);
    	Pen pen2 = new Pen(new SolidColorBrush(Color.FromArgb(255, 0, 0, 255)), 1.0);
    
    	drawingContext.DrawLine(pen1,
    		new System.Windows.Point(0, 0),
    		new System.Windows.Point(400, 400));
    
    	drawingContext.DrawLine(pen2,
    		new System.Windows.Point(400, 0),
    		new System.Windows.Point(0, 400));
    
    	// Discard the clipping region
    	drawingContext.Pop();
    }
    

    Hope this is going to be helpful for someone.
    • Marked as answer by Stanislav.S Saturday, February 27, 2010 9:06 AM
    Saturday, February 27, 2010 9:06 AM