Search code examples
c#winformsregion

Create combined Region for UserControl


I want my UserControl to automatically update its Region property. I want it to be a combination of child controls' regions merged together.

Here what I have so far:

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);

    Region region = new Region(new Rectangle(Point.Empty, Size.Empty));

    foreach (Control control in Controls)
    {
        if (control.Region != null)
            region.Union(control.Region);
        else
            region.Union(control.Bounds);
    }

    Region = region;
    Invalidate();
}

Problem is that it does not work: Line region.Union(control.Region); must be changed because Region does not include information about left and top offset of the control.

What can I do?


Solution

  • You have a choice of either going for the Rectangles that actually make up a Region. You can get them via GetRegionScans. You can see them in this post.

    Or of using the GraphicsPaths your child controls' Regions originate from..

    In both methods you can move the controls' region data by its location: Either by offsetting each rectangle or by translating the whole graphicspath.

    Here is a code example for the 1st method:

    if (control.Region != null)
    {
        Matrix matrix = new Matrix();  // default, unscaled screen-resolution matrix
        var rex = control.Region.GetRegionScans(matrix);  // get rectangles
        foreach (var r in rex)   // use each of them
        {
            r.Offset(control.Location);  //  move by the location offsets
            region.Union(r);
        }
    else
    {
        region.Union(control.Bounds);
    }
    

    The problem is that this tends to get slower and slower with the 'vertical' size and the complexity of the Region shapes..

    The other way is to keep track of the GraphicsPaths of the child controls.

    Assuming a class PathControl with a control property

    public GraphicsPath path { get; set; }
    

    You could change the loop maybe to this:

    foreach (Control control in Controls)
    {
        if (control is PathControl)
        {
            // use a clone, so the original path won't be changed!
            GraphicsPath gp = (GraphicsPath)(control as PathControl).path.Clone();
    
            Matrix matrix = new Matrix();
            matrix.Translate(control.Left, control.Top);
            gp.Transform(matrix);  // here we move by the location offsets
    
            region.Union(gp);
        else
        {
            region.Union(control.Bounds);
        }
    }