האם עוד צריך את VB.NET לתכנת מול אופיס?

עברו כמה שנים מאז שעבדתי עם Visual Basic.NET, והייתי בטוח שבימינו, ב-2013, נגמרו כבר הויכוחים של “איזו שפה יותר טובה”. שתיהן שפות עם יכולות דומות, והבחירה ביניהן היא בעיקר העדפה סגנונית. אבל מידי פעם אני עדיין רואה בפורומים או ב-Stack Overflow שאלה בנוגע ליתרונות של שפה אחת על השניה, וספציפית על יכולות ה-COM Interop של VB. האם היא באמת שפה נוחה יותר?

המצב טרום .NET 4.0

לפני .NET 4.0 (שיצאה, יש להזכיר, לפני כמעט 3 שנים), התשובה היתה “כן”, אם כי “כן” קצת דחוק. VB הגיעה מתוך עולם ה-Late Binding, ולא היה לה בעיה להעביר לרכיב רק חלק מהפרמטרים שהם צריכים, ולדאוג לשאר לברירות מחדל מאחורי הקלעים. היתרון הזה היה בולט במיוחד כשעובדים מול אובייקטים של אופיס, שמכילים מתודות שמקבלות חמישה, עשרה, ולפעמים שישה-עשר פרמטרים אופציונאליים, כשברוב המקרים אנחנו נרצה להעביר רק אחד או שניים מהם, אם בכלל. ההבדל בין C# ל-VB במקרה כזה הוא ההבדל בין הקוד הזה ב-C#:

Code Snippet
  1. myWordDocument.SaveAs(ref fileName, ref missingValue,
  2.                  ref missingValue, ref missingValue,
  3.                  ref missingValue, ref missingValue,
  4.                  ref missingValue, ref missingValue,
  5.                  ref missingValue, ref missingValue,
  6.                  ref missingValue, ref missingValue,
  7.                  ref missingValue, ref missingValue,
  8.                  ref missingValue, ref missingValue);

לבין זה ב-VB:

Code Snippet
  1. MyWordDocument.SaveAs(FileName: fileName)

שזה, אתם חייבים להודות, הבדל לא קטן.

אבל מצד שני, לא כל ממשק COM עובד ככה. למען האמת, בכל שנותי אני חושב שנתקלתי בסגנון כתיבה הזה רק בספריות של אופיס, ולא בשום רכיב אחר, בין אם של מיקרוסופט (כמו רכיבי DirectShow שאיתם עבדתי לאחרונה) או של חברות אחרות. ברוב המקרים, לעשות COM Interop ב-C# פשוט באותה מידה כמו ב-VB.

.NET 4.0 – גם C# זוכה ליחס

היתרון הקטן הזה של VB נהיה גם הוא פחות רלבנטי החל מ-C# 4.0 ו-Visual Studio 2010, משתי סיבות:

הראשונה היא מילת הקסם שהתווספה לה לשפה, dynamic. כשאנחנו מגדירים אובייקט כדינאמי, אנחנו מוותרים על הרבה מה-static type checking שהקומפיילר עושה בשבילנו, ונותנים למנגנון ה-late binding (שסופח ל-C# באמצעות ה-DLR, ה-Dynamic Language Runtime שנבנה לתמוך בשפות דינאמיות כמו Python או Ruby) לעשות בשבילנו את העבודה ב-runtime. מכיוון שיש late binding, אפשר להתעלם מהפרמטרים המיותרים ולתת ל-DLR להבין לבד מה לחבר לאיפה. אפשר למצוא הסבר כללי על שימוש ב-dynamic במאמר הזה, והסבר יותר ספציפי על השימוש בו ל-COM Interop במאמר הזה.

אבל אם אנחנו שקלנו לעבור ל-VB רק בשביל לעבוד מול רכיבי אופיס, אז C# 4.0 מאפשר לנו להקל על החיים גם בלי לצלול לעולם העכור של dynamic languages. אנחנו עדיין לא יכולים להתעלם מ-ref parameters במתודות של מחלקות, אבל אנחנו כן יכולים להתעלם מהפרמטרים הללו בקריאות למתודות על interfaces. אני לא יכול להגיד שאני מבין לגמרי למה זה ככה (טרם התעמקתי) אבל זה אומר שאם מה שיש לנו ביד זה לא אובייקט DocumentClass של אופיס, אלא interface מסוג Document, אז אפשר לקרוא למתודות שעליו כמו ב-VB.

לנוחותינו, ספריות ה-Interop של אופיס חושפות לנו את ה-interfaces האלה, דרך property בשם InnerObject שיש על רוב האובייקטים המעניינים. (כן, זה property בשם InnerObject שחושף את ה-Interface. אני יודע. זה מבלבל. זה מה יש). אז את הקוד שכתבנו למעלה  אפשר להחליף, ב-C# 4.0, בזה:

Code Snippet
  1. myWordDocument.InnerObject.SaveAs(FileName: fileName);

וזהו! אמנם לא כל אובייקט של אופיס זוכה ל-InnerObject הזה, אבל המרכזיים והגדולים (שהם גם אלה עם המתודות המפלצתיות) דווקא כן. ואם אין ברירה, נעבוד גם עם dynamic – או עם Missing.Value.

אז מתי בכל זאת VB?

כל המלל לעיל לא נועד לשכנע מישהו לבחור דווקא ב-C# או דווקא לא ב-VB. שתי השפות יכולות לעשות פחות או יותר כל אחת את מה שהשניה יכולה לעשות, והבחירה בה צריכה להיות של העדפה אישית לסגנון כתיבה, לא בגלל יכולת קריטיות לכאן או לכאן. זה מה שניסיתי להעביר כאן – כמו ש-VB יישרה קו עם C# לאורך השנים בכמה פיצ’רים שהיו חסרים, כך גם C# השלימה את החסר לה בנוגע לעבודה עם אופיס. הבחירה היא, שוב, סגנונית בלבד.

האם עוד צריך את VB.NET לתכנת מול אופיס?

עברו כמה שנים מאז שעבדתי עם Visual Basic.NET, והייתי בטוח שבימינו, ב-2013, נגמרו כבר הויכוחים של “איזו שפה יותר טובה”. שתיהן שפות עם יכולות דומות, והבחירה ביניהן היא בעיקר העדפה סגנונית. אבל מידי פעם אני עדיין רואה בפורומים או ב-Stack Overflow שאלה בנוגע ליתרונות של שפה אחת על השניה, וספציפית על יכולות ה-COM Interop של VB. האם היא באמת שפה נוחה יותר?

המצב טרום .NET 4.0

לפני .NET 4.0 (שיצאה, יש להזכיר, לפני כמעט 3 שנים), התשובה היתה “כן”, אם כי “כן” קצת דחוק. VB הגיעה מתוך עולם ה-Late Binding, ולא היה לה בעיה להעביר לרכיב רק חלק מהפרמטרים שהם צריכים, ולדאוג לשאר לברירות מחדל מאחורי הקלעים. היתרון הזה היה בולט במיוחד כשעובדים מול אובייקטים של אופיס, שמכילים מתודות שמקבלות חמישה, עשרה, ולפעמים שישה-עשר פרמטרים אופציונאליים, כשברוב המקרים אנחנו נרצה להעביר רק אחד או שניים מהם, אם בכלל. ההבדל בין C# ל-VB במקרה כזה הוא ההבדל בין הקוד הזה ב-C#:

Code Snippet
  1. myWordDocument.SaveAs(ref fileName, ref missingValue,
  2.                  ref missingValue, ref missingValue,
  3.                  ref missingValue, ref missingValue,
  4.                  ref missingValue, ref missingValue,
  5.                  ref missingValue, ref missingValue,
  6.                  ref missingValue, ref missingValue,
  7.                  ref missingValue, ref missingValue,
  8.                  ref missingValue, ref missingValue);

לבין זה ב-VB:

Code Snippet
  1. MyWordDocument.SaveAs(FileName: fileName)

שזה, אתם חייבים להודות, הבדל לא קטן.

אבל מצד שני, לא כל ממשק COM עובד ככה. למען האמת, בכל שנותי אני חושב שנתקלתי בסגנון כתיבה הזה רק בספריות של אופיס, ולא בשום רכיב אחר, בין אם של מיקרוסופט (כמו רכיבי DirectShow שאיתם עבדתי לאחרונה) או של חברות אחרות. ברוב המקרים, לעשות COM Interop ב-C# פשוט באותה מידה כמו ב-VB.

.NET 4.0 – גם C# זוכה ליחס

היתרון הקטן הזה של VB נהיה גם הוא פחות רלבנטי החל מ-C# 4.0 ו-Visual Studio 2010, משתי סיבות:

הראשונה היא מילת הקסם שהתווספה לה לשפה, dynamic. כשאנחנו מגדירים אובייקט כדינאמי, אנחנו מוותרים על הרבה מה-static type checking שהקומפיילר עושה בשבילנו, ונותנים למנגנון ה-late binding (שסופח ל-C# באמצעות ה-DLR, ה-Dynamic Language Runtime שנבנה לתמוך בשפות דינאמיות כמו Python או Ruby) לעשות בשבילנו את העבודה ב-runtime. מכיוון שיש late binding, אפשר להתעלם מהפרמטרים המיותרים ולתת ל-DLR להבין לבד מה לחבר לאיפה. אפשר למצוא הסבר כללי על שימוש ב-dynamic במאמר הזה, והסבר יותר ספציפי על השימוש בו ל-COM Interop במאמר הזה.

אבל אם אנחנו שקלנו לעבור ל-VB רק בשביל לעבוד מול רכיבי אופיס, אז C# 4.0 מאפשר לנו להקל על החיים גם בלי לצלול לעולם העכור של dynamic languages. אנחנו עדיין לא יכולים להתעלם מ-ref parameters במתודות של מחלקות, אבל אנחנו כן יכולים להתעלם מהפרמטרים הללו בקריאות למתודות על interfaces. אני לא יכול להגיד שאני מבין לגמרי למה זה ככה (טרם התעמקתי) אבל זה אומר שאם מה שיש לנו ביד זה לא אובייקט DocumentClass של אופיס, אלא interface מסוג Document, אז אפשר לקרוא למתודות שעליו כמו ב-VB.

לנוחותינו, ספריות ה-Interop של אופיס חושפות לנו את ה-interfaces האלה, דרך property בשם InnerObject שיש על רוב האובייקטים המעניינים. (כן, זה property בשם InnerObject שחושף את ה-Interface. אני יודע. זה מבלבל. זה מה יש). אז את הקוד שכתבנו למעלה  אפשר להחליף, ב-C# 4.0, בזה:

Code Snippet
  1. myWordDocument.InnerObject.SaveAs(FileName: fileName);

וזהו! אמנם לא כל אובייקט של אופיס זוכה ל-InnerObject הזה, אבל המרכזיים והגדולים (שהם גם אלה עם המתודות המפלצתיות) דווקא כן. ואם אין ברירה, נעבוד גם עם dynamic – או עם Missing.Value.

אז מתי בכל זאת VB?

כל המלל לעיל לא נועד לשכנע מישהו לבחור דווקא ב-C# או דווקא לא ב-VB. שתי השפות יכולות לעשות פחות או יותר כל אחת את מה שהשניה יכולה לעשות, והבחירה בה צריכה להיות של העדפה אישית לסגנון כתיבה, לא בגלל יכולת קריטיות לכאן או לכאן. זה מה שניסיתי להעביר כאן – כמו ש-VB יישרה קו עם C# לאורך השנים בכמה פיצ’רים שהיו חסרים, כך גם C# השלימה את החסר לה בנוגע לעבודה עם אופיס. הבחירה היא, שוב, סגנונית בלבד.

Surviving the Enterprise Application

Many of us have worked on this sort of application: “The Monster”, “The Big One”, “The Behemoth”, or simply “The Enterprise Application”. You know the kind I mean: it’s a mess of dozens of projects and solutions. It requires setting up three console applications and two databases just so you can debug something (god forbid you could just launch it from within Visual Studio), and after making changes to some code, getting the updates DLLs to the right place can be a headache. The sheer amount of time wasted on getting around the architecture can be daunting, and changing this behavior, especially when the app has years of crud and not enough tests, is not something you want to take on yourself.

This is where tools come in. Good tools make the difference when navigating such a codebase between “horrendous chore” and merely “momentous undertaking”. Some are commercial tools and add-ins, like Resharper, which are indispensable for making heads or tails of a giant project. But there are many tools that are built into Visual Studio or even Windows that can make our lives easier. Here are a few of them:

 

Macros Are Your Friends

I’m saddened, heartbroken, devastated that Microsoft has removed support for Macros in Visual Studio 2012. They’re a feature I’ve used for many years for automating common tasks, especially the ones that accompany a huge project. In many projects, one needs to launch the application manually, then attach via the Attach to Process command. That, in itself, is not an overly complex process. But repeat that fifty times a day, and you’re starting to lose serious minutes. And if you’ve got specific parameters involved – such as attaching to one process with .NET Debugging only, and to another with Managed Debugging as well, and you have endless frustration. That’s where macros come in. You can add a short macro (recorded automatically, then customized) to attach to a specific process, add that macro to your toolbar or map it to a keyboard shortcut, and watch productivity soar (well, jump a bit, maybe). Here’s an example of such a macro. Do not fear the Visual Basic. The Visual Basic is your friend.

Code Snippet
  1. Dim dbg2 As EnvDTE80.Debugger2 = DTE.Debugger
  2. Dim trans As EnvDTE80.Transport = dbg2.Transports.Item("Default")
  3. Dim dbgeng(1) As EnvDTE80.Engine
  4. dbgeng(0) = trans.Engines.Item("Managed (v4.0)")
  5. Dim proc2 As EnvDTE80.Process2 = dbg2.GetProcesses(trans, "MYCOMPUTERNAME").Item("MyExternalProcess.exe")
  6. proc2.Attach2(dbgeng)

That’s all there is to it, barring some error handling code if the process isn’t found – in which case you can even add code to launch it automatically. Assuming, of course, it’s a simple case of launching an EXE. If it isn’t, we have some more tricks up our sleeve:

Back To Basics With Batch Files

Yes, yes. It ain’t pretty, and It’s not as powerful as bash or PowerShell scripts. But many times you need a simple script without the learning curve, .BAT files can give you what you’re after. In my current project, launching the basic application environment involves launching a server app in a console window, waiting for it to load, then launching a second service, giving that one enough time, and only then launching the client. This can be easily implemented with a lesser-known utility called TIMEOUT.EXE, originally a part of the Windows Resource Kit, but starting with Vista, a standard part of every Windows installation. This lets us space our startup logic appropriately:

LaunchApp.bat
  1. pushd D:\EnterpriseApp
  2. start /min ConfigService.exe
  3. timeout 5
  4. start /min ServerService.exe
  5. timeout 15
  6. start ClientApplication.exe

(Yes, that’s fifteen seconds to wait every time. That’s one heck of an Enterprise Application).

But spacing out application launches isn’t always enough. Sometimes we need a batch file to kill all processes at once, so we can update data and restart them. Here we have, again, a built-in Windows utility named TASKKILL.EXE that can receive either a process ID or, better for us, an executable name and kills the process dead. However, in this case, I would recommend skipping the built-in tool and using PsKill, part of SysInternals indispensable PsTools package, which has a host of useful tools.

SendTo? Really?

I remember, when Windows 95 rolled into town, I was enchanted with the possibilities that Windows Explorer offered. One of these was the Send To menu, and specifically, how easy it was to add a new option to that menu. Now I could send any file to any app, easily! Of course, after 10 minutes of wonder, I discovered that I never use it for anything except attaching a file to an Outlook message, and (rarely) creating a desktop shortcut. But now, with the Enterprise Application, it came back to me!

In my current project, we have several solutions, each compiling outputs to a different build folder. Of course, if you’re building the Shared Enterprise Apps solution, you have to remember to copy the new files to the Enterprise Application Client folder, or you’ll be constantly surprised and frustrated by your old code running again. Of course, this entails keeping lots of Explorer windows open, closing them, reopening them, copying the new files, and so on, and so forth. This is where my teenage fascination with Send To came back to me – I can actually use it! For real!

In Windows 7, the Send To menu resides in the application data folder, under %AppData%\Microsoft\Windows\SendTo. All we have to do is add a shortcut to our Enterprise Client folder, and be able to quickly send any file to it from anywhere:

image

 

Seconds Count

All these tips I’ve collected here may seem a bit trivial. The problems they come to solve only cost us a handful of seconds each. But when we sit in front of our projects for hours every day, repeating endless cycles of code-test-fix, these seconds add up. They add up in productivity, and they add up in frustration. A smooth development and operations process keeps us focused on what counts – fixing the real bugs, solving the real problems, and going home. If you have your own productivity tips, I’d love to hear them.

Remote Debugging through fire, snow or fog

Remote Debugging in Visual Studio is a wonderful feature, especially during the later stages of testing and deployment, and even (if all else fails) when in production, but getting it to work is rarely smooth. Everything is fine if both the computer running VS and the one running the application are in the same domain, but when they aren’t, things start to break, fast. I

So for those stuck debugging remote applications in different domains, here’s a quick guide to east the pain.

  1. On the the remote machine, install the Visual Studio Remote Debugging package, which comes with the version of Visual Studio you’re using.
  2. On the remote machine, create a new local user account. Give it the exact same name and password that you use on your development machine.
    If you’re developing when logged into MYCORPDOMAIN\MyUserName, with password MYPASS123, you wil have to create a local user REMOTEMACHINE\MyUserName with the same password. It doesn’t matter that the domains are different and there’s no trust relationship and so forth. Just have them be the same username and password.
  3. Give REMOTEMACHINE\MyUserName permissions. Administrator permissions is the safest, though you should be able to get it to work with a more restricted group, but I haven’t checked it yet.
  4. Run the application you want to debug using the MyUserName credentials. In Windows 7, this means using Shift-Rightclick and choosing Run As Different User. For Vista, there’s a shell extension to enable it.
  5. Run the Remote Debugging program, again under MyUserName credentials, same as above. The Remote Debugger will start with a new session named MyUserName@REMOTEMACHINE.
  6. Copy the remote debugging session name from the remote machine (you can copy it via the Tools –> Options menu).
  7. In your development machine, open Visual Studio, and go to Debug –> Attach to Process.
  8. In the Attack to Process screen, paste the remote debugging session name into the Qualifier textbox (the second one from the top).
  9. Voila! You are now debugging remotely!

Remote Debugging through fire, snow or fog

Remote Debugging in Visual Studio is a wonderful feature, especially during the later stages of testing and deployment, and even (if all else fails) when in production, but getting it to work is rarely smooth. Everything is fine if both the computer running VS and the one running the application are in the same domain, but when they aren’t, things start to break, fast. I

So for those stuck debugging remote applications in different domains, here’s a quick guide to east the pain.

  1. On the the remote machine, install the Visual Studio Remote Debugging package, which comes with the version of Visual Studio you’re using.
  2. On the remote machine, create a new local user account. Give it the exact same name and password that you use on your development machine.
    If you’re developing when logged into MYCORPDOMAINMyUserName, with password MYPASS123, you wil have to create a local user REMOTEMACHINEMyUserName with the same password. It doesn’t matter that the domains are different and there’s no trust relationship and so forth. Just have them be the same username and password.
  3. Give REMOTEMACHINEMyUserName permissions. Administrator permissions is the safest, though you should be able to get it to work with a more restricted group, but I haven’t checked it yet.
  4. Run the application you want to debug using the MyUserName credentials. In Windows 7, this means using Shift-Rightclick and choosing Run As Different User. For Vista, there’s a shell extension to enable it.
  5. Run the Remote Debugging program, again under MyUserName credentials, same as above. The Remote Debugger will start with a new session named MyUserName@REMOTEMACHINE.
  6. Copy the remote debugging session name from the remote machine (you can copy it via the Tools –> Options menu).
  7. In your development machine, open Visual Studio, and go to Debug –> Attach to Process.
  8. In the Attack to Process screen, paste the remote debugging session name into the Qualifier textbox (the second one from the top).
  9. Voila! You are now debugging remotely!

ArcGIS–Getting the Legend Labels out

Working with ESRI’s ArcGIS package, especially the WPF API, can be confusing. There’s the REST API, the SOAP APIs, and the WPF classes themselves, which expose some web service calls and information, but not everything. With all that, it can be hard to find specific features between the different options. Some functionality is handed to you on a silver platter, while some is maddeningly hard to implement.

Today, for instance, I was working on adding a Legend control to my map-based WPF application, to explain the different symbols that can appear on the map.

This is how the legend looks on ESRI’s own map-editing tools:

 

but this is how it looks when I used the Legend control, supplied out of the box by ESRI:

 

Very pretty, but unfortunately missing the option to display the name of the fields that make up the symbology.

Luckily, the WPF controls have a lot of templating/extensibility points, to allow you to specify the layout of each field:

   1: <esri:Legend>

   2:     <esri:Legend.MapLayerTemplate>

   3:         <DataTemplate>

   4:              <TextBlock Text="{Binding Layer.ID}"/>

   5:         DataTemplate> 

   6:     esri:Legend.MapLayerTemplate>

   7: esri:Legend>

but that only replicates the same built in behavior. I could now add any additional fields I liked, but unfortunately, I couldn’t find them as part of the Layer, GraphicsLayer or FeatureLayer definitions. This is the part where ESRI’s lack of organization is noticeable, since I can see this data easily when accessing the ArcGis Server’s web-interface, but I had no idea how to find it as part of the built-in class. Is it a part of Layer? Of LayerInfo? Of the LayerDefinition class that exists only in the SOAP service?

As it turns out, neither. Since these fields are used by the symbol renderer to determine which symbol to draw, they’re actually a part of the layer’s Renderer. Since I already had a MyFeatureLayer class derived from FeatureLayer that added extra functionality, I could just add this property to it:

   1: public string LegendFields

   2: {

   3:     get

   4:     {

   5:         if (this.Renderer is UniqueValueRenderer)

   6:         {

   7:             return (this.Renderer as UniqueValueRenderer).Field;

   8:         }

   9:         else if (this.Renderer is UniqueValueMultipleFieldsRenderer)

  10:         {

  11:             var renderer = this.Renderer as UniqueValueMultipleFieldsRenderer;

  12:             return string.Join(renderer.FieldDelimiter, renderer.Fields);

  13:         }

  14:         else return null;

  15: }

For my scenario, all of my layers used symbology derived from a single field or, as in the examples above, from several of them. The renderer even kindly supplied me with the comma to separate the fields with. Now it was a simple matter to get the Legend control in line – assuming that it was bound to a collection of MyFeatureLayer:

   1: <esri:Legend>

   2:     <esri:Legend.MapLayerTemplate>

   3:         <DataTemplate>

   4:             <StackPanel>

   5:                 <TextBlock Text="{Binding Layer.ID}"/>

   6:                 <TextBlock Text="{Binding Layer.LegendFields}" Margin="10,0,0,0" TextStyle="Italic"/>

   7:             StackPanel>

   8:         DataTemplate>

   9:     esri:Legend.MapLayerTemplate>

  10: esri:Legend>

and get the look I wanted – the list of fields below the layer name, indented.

ArcGIS–Getting the Legend Labels out

Working with ESRI’s ArcGIS package, especially the WPF API, can be confusing. There’s the REST API, the SOAP APIs, and the WPF classes themselves, which expose some web service calls and information, but not everything. With all that, it can be hard to find specific features between the different options. Some functionality is handed to you on a silver platter, while some is maddeningly hard to implement.

Today, for instance, I was working on adding a Legend control to my map-based WPF application, to explain the different symbols that can appear on the map.

This is how the legend looks on ESRI’s own map-editing tools:

 

but this is how it looks when I used the Legend control, supplied out of the box by ESRI:

 

Very pretty, but unfortunately missing the option to display the name of the fields that make up the symbology.

Luckily, the WPF controls have a lot of templating/extensibility points, to allow you to specify the layout of each field:

   1: <esri:Legend>

   2:     <esri:Legend.MapLayerTemplate>

   3:         <DataTemplate>

   4:              <TextBlock Text="{Binding Layer.ID}"/>

   5:         </DataTemplate> 

   6:     </esri:Legend.MapLayerTemplate>

   7: </esri:Legend>

but that only replicates the same built in behavior. I could now add any additional fields I liked, but unfortunately, I couldn’t find them as part of the Layer, GraphicsLayer or FeatureLayer definitions. This is the part where ESRI’s lack of organization is noticeable, since I can see this data easily when accessing the ArcGis Server’s web-interface, but I had no idea how to find it as part of the built-in class. Is it a part of Layer? Of LayerInfo? Of the LayerDefinition class that exists only in the SOAP service?

As it turns out, neither. Since these fields are used by the symbol renderer to determine which symbol to draw, they’re actually a part of the layer’s Renderer. Since I already had a MyFeatureLayer class derived from FeatureLayer that added extra functionality, I could just add this property to it:

   1: public string LegendFields

   2: {

   3:     get

   4:     {

   5:         if (this.Renderer is UniqueValueRenderer)

   6:         {

   7:             return (this.Renderer as UniqueValueRenderer).Field;

   8:         }

   9:         else if (this.Renderer is UniqueValueMultipleFieldsRenderer)

  10:         {

  11:             var renderer = this.Renderer as UniqueValueMultipleFieldsRenderer;

  12:             return string.Join(renderer.FieldDelimiter, renderer.Fields);

  13:         }

  14:         else return null;

  15: }

For my scenario, all of my layers used symbology derived from a single field or, as in the examples above, from several of them. The renderer even kindly supplied me with the comma to separate the fields with. Now it was a simple matter to get the Legend control in line – assuming that it was bound to a collection of MyFeatureLayer:

   1: <esri:Legend>

   2:     <esri:Legend.MapLayerTemplate>

   3:         <DataTemplate>

   4:             <StackPanel>

   5:                 <TextBlock Text="{Binding Layer.ID}"/>

   6:                 <TextBlock Text="{Binding Layer.LegendFields}" Margin="10,0,0,0" TextStyle="Italic"/>

   7:             </StackPanel>

   8:         </DataTemplate>

   9:     </esri:Legend.MapLayerTemplate>

  10: </esri:Legend>

and get the look I wanted – the list of fields below the layer name, indented.

The Case of the Unexpected Expected Exception

“NUnit is being problematic again”, they told me when I came to visit the project. “When running unattended it’s not catching assertions properly and the test is coming up green, but when stepping through in the debugger, it works fine.”. It’s nice, when getting a passing test is acknowledged as a bad thing, at least when you don’t expect it to be. In this case, though, the fault wasn’t really with NUnit.

  1: [Test]
  2: [ExpectedException]
  3: public void DoTheTest()
  4: {
  5:     _myComponent.RunMethod();
  6:     Assert.IsFalse(_myComponent.EverythingIsFine);
  7: }

“It’s simple. Either the method throws an exception, or at the very least – the EverythingIsFine property won’t be set to “True”, so the assert will catch the problem. But in their case, no exception was thrown and Everything wasn’t Fine,  but the Assert call wasn’t raising a red flag – unless they stepped through, in which case it did. What’s going on?

The basic problem is that to many developers, NUnit is a kind of magic. You write a self-contained little bit of code, the [Test] method, but you don’t call it yourself, you don’t get a feel for the whole execution flow. The result – developers don’t exercise the same sort of judgement they do on their own application code.

The root of the problem here is that the [ExpectedException] attribute told NUnit to pass the test if an exception is thrown. NUnit’s Assertion utilities, however, use exceptions as the mechanism for failing tests – when an assertion is hit, it raises an exception – it can be an AssertionException. For various mock frameworks, it can be an ExpectationException. It doesn’t matter – it’s these exceptions that make the test fail, and not some behind-the-scenes magic. Because the test had an open-ended [ExpectedException] attribute, these exceptions were caught, fulfilling the condition, and NUnit was happy.

What can we do to avoid this?

  • Be explicit. Don’t try to catch ALL exceptions with [ExpectedException]. If you’re expecting an exception, you’re probably expecting a specific exception. Specify it.
  • Be aware of how your tools work. If NUnit works by throwing an exception, don’t wrap it with a try/catch. Your tests are C# code too, as is the plumbing to enable it. It plays by the same rules.

The Case of the Expected Unexpected Exception

“NUnit is being problematic again”, they told me when I came to visit the project. “When running unattended it’s not catching assertions properly and the test is coming up green, but when stepping through in the debugger, it works fine.”. It’s nice, when getting a passing test is acknowledged as a bad thing, at least when you don’t expect it to be. In this case, though, the fault wasn’t really with NUnit.

  1: [Test]
  2: [ExpectedException]
  3: public void DoTheTest()
  4: {
  5:     _myComponent.RunMethod();
  6:     Assert.IsTrue(_myComponent.EverythingIsFine);
  7: }

“It’s simple. Either the method throws an exception, or at the very least – the EverythingIsFine property won’t be set to “True”, so the assert will catch the problem. But in their case, no exception was thrown and Everything wasn’t Fine,  but the Assert call wasn’t raising a red flag – unless they stepped through, in which case it did. What’s going on?

The basic problem is that to many developers, NUnit is a kind of magic. You write a self-contained little bit of code, the [Test] method, but you don’t call it yourself, you don’t get a feel for the whole execution flow. The result – developers don’t exercise the same sort of judgement they do on their own application code.

The root of the problem here is that the [ExpectedException] attribute told NUnit to pass the test if an exception is thrown. NUnit’s Assertion utilities, however, use exceptions as the mechanism for failing tests – when an assertion is hit, it raises an exception – it can be an AssertionException. For various mock frameworks, it can be an ExpectationException. It doesn’t matter – it’s these exceptions that make the test fail, and not some behind-the-scenes magic. Because the test had an open-ended [ExpectedException] attribute, these exceptions were caught, fulfilling the condition, and NUnit was happy.

What can we do to avoid this?

  • Be explicit. Don’t try to catch ALL exceptions with [ExpectedException]. If you’re expecting an exception, you’re probably expecting a specific exception. Specify it.
  • Be aware of how your tools work. If NUnit works by throwing an exception, don’t wrap it with a try/catch. Your tests are C# code too, as is the plumbing to enable it. It plays by the same rules.

Open Source Vigilantes?

<Placeholder for StackOverflow Weekday counter>