ThinkGeo.com    |     Documentation    |     Premium Support

Exception drawing AdornmentLayer in v10

I’m finally back trying to get our map ported to your v10 library.

My latest boondoggle appears to be related to drawing an adornment layer previously based on your sample for the Scale Units Adornment (or something like that, it just shows the scale of the map) layer. Everything seems fine up to the point where I try to draw the scale units adornment layer in an overloaded DrawCore of the our class derived from AdornmentLayer.

Ultimately our type structure has a UserControl derived class that contains your WinformsMap. In it’s OnLoad we use BeginInvoke to defer (POST Message) loading all the static layers (layers (shape, etc) and adornment layers). Eventually this results in a call to draw the Scale Units by calling WinformsMap.Refresh( WinformsMap.AdornmentOverlay ). Class name WinformsMap substituted for our instance variable name. Anyway, that calls DrawCore, and we call canvas.DrawArea (argument values confirmed to be reasonable) resulting in the following exception (cleaned methods for our portion). Without knowing what was null in your code, I’m at a loss to figure this out.

Obfuscation bites us again by making what should be easy hard when any intellectual property having enough value to be worth stealing only slightly harder to figure out.

“Object reference not set to an instance of an object.”

at 4EU=.30U=.5UU=(GeoColor color)
at 4EU=.30U=.6EU=(GeoSolidBrush brush)
at ThinkGeo.MapSuite.Drawing.PlatformGeoCanvas.2kU=(GeoBrush brush, IEnumerable1 areaPointsCache) at ThinkGeo.MapSuite.Drawing.PlatformGeoCanvas.1UU=(IEnumerable1 screenPoints, GeoPen outlinePen, GeoBrush fillBrush, Single xOffset, Single yOffset, PenBrushDrawingOrder penBrushDrawingOrder, Graphics graphics)
at ThinkGeo.MapSuite.Drawing.PlatformGeoCanvas.DrawAreaCore(IEnumerable1 screenPoints, GeoPen outlinePen, GeoBrush fillBrush, DrawingLevel drawingLevel, Single xOffset, Single yOffset, PenBrushDrawingOrder penBrushDrawingOrder) at ThinkGeo.MapSuite.Drawing.GeoCanvas.DrawArea(IEnumerable1 screenPoints, GeoPen outlinePen, GeoBrush fillBrush, DrawingLevel drawingLevel, Single xOffset, Single yOffset, PenBrushDrawingOrder penBrushDrawingOrder)
at CustomUnitScaleBarAdornmentLayer.DrawOpaqueBackground(GeoCanvas canvas, Single xPos, Single yPos, Single barLength) in CustomUnitScaleBarAdornmentLayer.cs:line 86
at CustomUnitScaleBarAdornmentLayer.DrawCore(GeoCanvas canvas, Collection1 labelsInAllLayers) in CustomUnitScaleBarAdornmentLayer.cs:line 65 at ThinkGeo.MapSuite.Layers.Layer.ZyU=(GeoCanvas canvas, Collection1 labelsInAllLayers)
at ThinkGeo.MapSuite.Layers.Layer.Draw(GeoCanvas canvas, Collection1 labelsInAllLayers) at ThinkGeo.MapSuite.WinForms.AdornmentOverlay.DrawCore(GeoCanvas canvas) at ThinkGeo.MapSuite.WinForms.Overlay.EFM=(GeoCanvas canvas) at ThinkGeo.MapSuite.WinForms.Overlay.Draw(GeoCanvas canvas) at ThinkGeo.MapSuite.WinForms.WinformsMap.y1I=(Graphics graphics, RectangleShape extent, Overlay overlay) at ThinkGeo.MapSuite.WinForms.WinformsMap.xFI=(RectangleShape drawingExtent, RectangleShape extent) at ThinkGeo.MapSuite.WinForms.WinformsMap.41I=(RectangleShape extent) at ThinkGeo.MapSuite.WinForms.WinformsMap.gRU=(Int32 delayInterval, RectangleShape extent) at ThinkGeo.MapSuite.WinForms.WinformsMap.Refresh(RectangleShape extent, IEnumerable1 overlays)
at ThinkGeo.MapSuite.WinForms.WinformsMap.Refresh(RectangleShape extent, Overlay overlay)
at ThinkGeo.MapSuite.WinForms.WinformsMap.Refresh(Overlay overlay)
at StaticMap.set_ShowCompass(Boolean value) in StaticMap.cs:line 128
at CustomMap.LoadStateFromConfigFile() in CustomMap.cs:line 317
at CustomMap.Initialize() in CustomMap.cs:line 106
at CustomMapControl.InitializeStatic() in CustomMapControl.cs:line 345

Hi Russ,

Thanks for let us know your question.

Your description is a little complex, we tried to create a simple sample for reproduce it but failed get exception, that maybe because we missed something in your custom code.

Then we read your call stack and view related code, it looks the exception is because the Brush.Color is null in your function CustomUnitScaleBarAdornmentLayer.DrawOpaqueBackground when you call the DrawArea and set it as parameter.

Could you please double check the parameter “fillBrush” in CustomUnitScaleBarAdornmentLayer.DrawOpaqueBackground to make sure its “color” is valid value?

If you make sure the fillBrush.Color is OK but the exception still be thrown, please sent us a simple sample which can reproduce that, our developer will work on it.

Regards,

Don

I’m well aware of the value of a reduce repro, but have already spent an unbelievable amount of time trying to port our map based on your samples to v10, and I see no reasonable path to even attempting a reduced repro (which after that effort might not repro anyway because of some dependency I am unaware of). So no, I can’t provide a reduced repro. But I can say that this same code, with only minimal modifications to compile with V10 throws that exception where the v9 does not.

But that said, what you provided was just what I needed. Thank you.

The background color used to create the brush used to be a struct with default value as transparent. Now, it is a class. So it turns out that some unrelated code was causing a refresh of the adornment layer before the code ran that set the value of the background color. I had checked for valid arguements in that the brush itself was not null, but the GeoColor used to initialize the brush WAS still null!

I’ve complained before about the lack of documentation describing all breaking changes and THIS should certainly be in such a document (has one ever been made available?). You simply cannot make changes like this silently behind the scenes. The compiler is happy to just take that and go, so there was nothing to tell me what my problem was without thinking to look deeper into that brush instance.

GeoSolidBrush brush = new GeoSolidBrush( backgroundColor );

Furthermore, if a GeoSolidBrush instance is not valid and will cause such a masked exception, then the GeoSolidBrush should be throwing an exception when initialized with a null color, something that could NOT happen in v9 because it was a struct.

So back to the attempt at a reduced repro, in all likelihood I would not have included that “init from config” portion in that attempt. It probably would have just used the constant GeoColor.StandardColors.Transparent (which is the correct default if not changed via config), and after spending the time to try to create would still have had no idea why my attempt at a repro worked, but the main code base did not.

Note that without the obfuscation I would have been able to easily figure that out myself with the very first time I saw the exception. Baring that, the brush ctor throwing an exception due to invalid color would have also immediately pointed out the problem. Or throwing a meaningful exception from DrawArea would have been useful. And with TG’s main product being a mapping library to paying consumers, having converted a struct to a class your code should be using defensive programming patterns and checking for null where relevant now that it’s become a potential invalid state and throwing a meaningful exception. But failing all that, all I got was a mangled call stack and “null ref exception” with no idea what happened…

Making changes like this (struct to class) is a very significant semantic (and ultimately syntactic) change and should not in any way be allowed to impact users like this.

I now wonder how many other runtime errors will appear to other such changes. I suppose my only recourse is to laboriously examine our entire code base for places where TG making such hidden changes may have impacted. Or perhaps just chase down exception after exception, keeping this experience in mind, until there are no more, and then hope that there are no untested code paths that generate runtime exceptions later.

Hi Russ,

I am sorry to hear that the change takes confused in new version, and I am glad to hear that solved your problem.

Your suggestions is very helpful for us, I will let our developer know them.

Please keep concern our product and any question please feel free to let us know.

Regards,

Don