2

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?

TaW
  • 53,122
  • 8
  • 69
  • 111
walruz
  • 1,135
  • 1
  • 13
  • 33
  • How do you create the Regions of the controls? By a GraphicsPath? In one go or progressively? – TaW Jul 29 '17 at 18:32
  • @Taw - Yes, using GraphicsPath in one go. Does it matter? – walruz Jul 29 '17 at 18:57
  • You can store those GraphicsPaths, maybe in the Tags and then use them for the Union. You can move them with a Matrix. – TaW Jul 29 '17 at 19:06
  • @TaW - I am wondering if GetRegionScans method could be helpful in this situation? – walruz Jul 29 '17 at 19:13
  • Yes it could but, depending on the size of the controls it might get really slow. See [here](https://stackoverflow.com/questions/43139118/region-isvisiblepointf-has-very-slow-performance-for-large-floating-point-valu/43180365?s=1|0.1496#43180365) for a discussion! – TaW Jul 29 '17 at 19:17
  • @TaW - You are right - too slow. Is it possible to move region using matrix transformation? I mean get region data while applying matrix transformation and then feed it back to region constructor? – walruz Jul 29 '17 at 19:27
  • You need to move the GraphicsPath. You can store it in a Property of the child Controls. Seems to work here- I'll post wat I have if you want to see it.. – TaW Jul 29 '17 at 19:30
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150494/discussion-between-walruz-and-taw). – walruz Jul 29 '17 at 19:36
  • @TaW - I didn't want child controls to be special. I was trying to avoid that. I want my UserControl to work with just any control without requiring extra properties or stored data in tag field. – walruz Jul 29 '17 at 19:42
  • Well in that case I guess you need to go for the GetRegionScans method.. But of course any control does have a Tag, so when you set the Region you could make of it. – TaW Jul 29 '17 at 19:43
  • @TaW - thank you for your help! I believe you saved me some time. – walruz Jul 29 '17 at 19:51
  • No problem, a pleasure tackling an interesting task. You may want ot check the code examples in my answer post.. – TaW Jul 29 '17 at 19:52

1 Answers1

1

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);
    }
}
TaW
  • 53,122
  • 8
  • 69
  • 111