Array of Regions. Elements seem to disappear. [modified]
-
Weird... unless I'm not understanding something about Regions. I build an array of Region objects (constructed from GraphicsPath objects). When I access any array element (to set the Region property of a control), it works fine the first time, but if I try to access that same element a second time, it gives me an Invalid Parameter exception. It's as if the array is read-once. I work around this by saving an array of RegionData objects, and constructing Regions on-the-fly from elements of this array. Clutzy at best. Does anyone have any insight on what is going on? (Or what I'm doing wrong?) Thanks! Tom -- modified at 17:36 Monday 10th September, 2007
-
Weird... unless I'm not understanding something about Regions. I build an array of Region objects (constructed from GraphicsPath objects). When I access any array element (to set the Region property of a control), it works fine the first time, but if I try to access that same element a second time, it gives me an Invalid Parameter exception. It's as if the array is read-once. I work around this by saving an array of RegionData objects, and constructing Regions on-the-fly from elements of this array. Clutzy at best. Does anyone have any insight on what is going on? (Or what I'm doing wrong?) Thanks! Tom -- modified at 17:36 Monday 10th September, 2007
Have you tried debugging? Perhaps you should post some code.
#region signature my articles #endregion
-
Have you tried debugging? Perhaps you should post some code.
#region signature my articles #endregion
Here's a simple project with a form having one button, and a UserControl. Initially, control looks like a blue circle (as expected). Click the button, sets currRegionIndex to 1, and the control is a blue square (as expected). Click the button again sets currRegionIndex back to 0, and ArgumentException ("Parameter is not valid") results. Weird, huh? ------------------------------------- // Form1.cs using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace RegionTest { public partial class Form1 : Form { private Region[] region = new Region[2]; private int currRegionIndex = 1; private MyControl myControl = new MyControl(); public Form1() { InitializeComponent(); GraphicsPath gp0 = new GraphicsPath(); gp0.AddEllipse(myControl.ClientRectangle); region[0] = new Region(gp0); GraphicsPath gp1 = new GraphicsPath(); gp1.AddRectangle(myControl.ClientRectangle); region[1] = new Region(gp1); myControl.Region = region[1]; Controls.Add(myControl); } private void button1_Click(object sender, EventArgs e) { currRegionIndex = (currRegionIndex + 1) % 2; myControl.Region = region[currRegionIndex]; myControl.Refresh(); } } } ------------------------------------- // Mycontrol.cs using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace RegionTest { public partial class MyControl : UserControl { public MyControl() { InitializeComponent(); BackColor = Color.Blue; } } } ------------------------------------------------------ Incidentally, I'm using VS2005 Thanks for your help, Tom -- modified at 18:33 Monday 10th September, 2007
-
Here's a simple project with a form having one button, and a UserControl. Initially, control looks like a blue circle (as expected). Click the button, sets currRegionIndex to 1, and the control is a blue square (as expected). Click the button again sets currRegionIndex back to 0, and ArgumentException ("Parameter is not valid") results. Weird, huh? ------------------------------------- // Form1.cs using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace RegionTest { public partial class Form1 : Form { private Region[] region = new Region[2]; private int currRegionIndex = 1; private MyControl myControl = new MyControl(); public Form1() { InitializeComponent(); GraphicsPath gp0 = new GraphicsPath(); gp0.AddEllipse(myControl.ClientRectangle); region[0] = new Region(gp0); GraphicsPath gp1 = new GraphicsPath(); gp1.AddRectangle(myControl.ClientRectangle); region[1] = new Region(gp1); myControl.Region = region[1]; Controls.Add(myControl); } private void button1_Click(object sender, EventArgs e) { currRegionIndex = (currRegionIndex + 1) % 2; myControl.Region = region[currRegionIndex]; myControl.Refresh(); } } } ------------------------------------- // Mycontrol.cs using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; namespace RegionTest { public partial class MyControl : UserControl { public MyControl() { InitializeComponent(); BackColor = Color.Blue; } } } ------------------------------------------------------ Incidentally, I'm using VS2005 Thanks for your help, Tom -- modified at 18:33 Monday 10th September, 2007
A little smart debugging and a glance in Reflector exposes the problem. If you place a breakpoint at this line:
myControl.Region = region[currRegionIndex];
and examine the region array, each Region object has a Non-Public member called "nativeRegion". This is an IntPtr handle to a Win32 Rgn object. The first time you use the region, that IntPtr value is not zero - that is, item != IntPtr.Zero. Once you assign the region to the control, that same value becomes 0. Looking at the code for Control.Region in Reflector exposes why:IntPtr zero = IntPtr.Zero; try { if (value != null) { zero = this.GetHRgn(value); } if (this.IsActiveX) { zero = this.ActiveXMergeRegion(zero); } if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, this.Handle), new HandleRef(this, zero), SafeNativeMethods.IsWindowVisible(new HandleRef(this, this.Handle))) != 0) { zero = IntPtr.Zero; } } finally { if (zero != IntPtr.Zero) { SafeNativeMethods.DeleteObject(new HandleRef(null, zero)); } }
You see what's going on. The region is being deleted once it is used successfully. The solution, then, is to assign a copy of each region instead, using the Region.Clone method:
public Form1() { InitializeComponent(); GraphicsPath gp0 = new GraphicsPath(); gp0.AddEllipse(myControl.ClientRectangle); region[0] = new Region(gp0); GraphicsPath gp1 = new GraphicsPath(); gp1.AddRectangle(myControl.ClientRectangle); region[1] = new Region(gp1); myControl.Region = region[1].Clone(); Controls.Add(myControl); } private void button1_Click(object sender, EventArgs e) { currRegionIndex = (currRegionIndex + 1) % 2; myControl.Region = region[currRegionIndex].Clone(); myControl.Refresh(); }
-
A little smart debugging and a glance in Reflector exposes the problem. If you place a breakpoint at this line:
myControl.Region = region[currRegionIndex];
and examine the region array, each Region object has a Non-Public member called "nativeRegion". This is an IntPtr handle to a Win32 Rgn object. The first time you use the region, that IntPtr value is not zero - that is, item != IntPtr.Zero. Once you assign the region to the control, that same value becomes 0. Looking at the code for Control.Region in Reflector exposes why:IntPtr zero = IntPtr.Zero; try { if (value != null) { zero = this.GetHRgn(value); } if (this.IsActiveX) { zero = this.ActiveXMergeRegion(zero); } if (UnsafeNativeMethods.SetWindowRgn(new HandleRef(this, this.Handle), new HandleRef(this, zero), SafeNativeMethods.IsWindowVisible(new HandleRef(this, this.Handle))) != 0) { zero = IntPtr.Zero; } } finally { if (zero != IntPtr.Zero) { SafeNativeMethods.DeleteObject(new HandleRef(null, zero)); } }
You see what's going on. The region is being deleted once it is used successfully. The solution, then, is to assign a copy of each region instead, using the Region.Clone method:
public Form1() { InitializeComponent(); GraphicsPath gp0 = new GraphicsPath(); gp0.AddEllipse(myControl.ClientRectangle); region[0] = new Region(gp0); GraphicsPath gp1 = new GraphicsPath(); gp1.AddRectangle(myControl.ClientRectangle); region[1] = new Region(gp1); myControl.Region = region[1].Clone(); Controls.Add(myControl); } private void button1_Click(object sender, EventArgs e) { currRegionIndex = (currRegionIndex + 1) % 2; myControl.Region = region[currRegionIndex].Clone(); myControl.Refresh(); }
Wow... thanks, Patrick. Hmm, this is not the behaviour one would expect, but who says life should be predictable :sigh:. My workaround was: ----------------------------- using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; namespace RegionTest { public partial class Form1 : Form { RegionData[] regionData = new RegionData[2]; int currRegionIndex = 0; MyControl myControl = new MyControl(); public Form1() { InitializeComponent(); GraphicsPath gp0 = new GraphicsPath(); gp0.AddEllipse(myControl.ClientRectangle); regionData[0] = (new Region(gp0)).GetRegionData(); GraphicsPath gp1 = new GraphicsPath(); gp1.AddRectangle(myControl.ClientRectangle); regionData[1] = (new Region(gp1)).GetRegionData(); myControl.Region = new Region(region[0]); Controls.Add(myControl); } private void button1_Click(object sender, EventArgs e) { currRegionIndex = (currRegionIndex + 1) % 2; myControl.Region = new Region(regionData[currRegionIndex]); myControl.Refresh(); } } } ----------------------------- which in the end is similar to yours in efficiency (I guess) but yours is more elegant. Tom