As of version 5.25, LINQPad has a new object diff’ing method that’s great for comparing objects and tracking down problems in code and this, of course, also applies to Visio.
If you’ve not come across LINQPad before you might be interested in this post where I started to look at using it with Visio.
The latest beta (5.28.8 at the time of writing) has a few improvements so I’ll use this version for the purposes of this post, but I imagine / hope that it will appear in the full release soon.
First of all, to download the beta you can head over the download page and simply download to a local folder. The beta behaves very nicely with your current install, if you have one, and automatically picks up all of your query, snippet and extension folders making it very easy to run both beta and current release side-by-side.
So let’s have a look at a quick example. If you have the following code:
1: void Main()
2: {
3: var firstCity = new CityRegion(){
4: Name="Greater London",
5: Population=8204000,
6: Country="UK"
7: };
8: var secondCity = new CityRegion(){
9: Name="Greater Manchester",
10: Population=2685000,
11: Country="UK"
12: };
13:
14: Util.Dif(firstCity, secondCity).Dump();
15: }
16:
17: public class CityRegion
18: {
19: public string Name { get; set; }
20: public int Population { get; set; }
21: public string Country { get; set; }
22: }
[* populations from ons.gov.uk ]
…you’ll see this output:
As you can see, the non-matching properties are automatically highlighted and, in fact, the method also includes an optional argument (‘hideMatchingMembers’) which, if set to true in the example above, would remove the Country property from the output.
One other optional argument is ‘depth’ (a nullable int) that can be set to prevent endless child objects being spurted out. This is particularly useful in Visio’s case as you probably don’t want to see all of the general context properties such as hosting Application and Document etc. and so setting this argument to 1 allows you to view the top level object only.
The Dif method returns a DifResult object, which you either dump directly (as above) or inspect its IsSame property to determine if the two compared objects differ at all.
Putting Dif to work in Visio
So where might this be useful in Visio? Well, while you can point the method at any object, what I often want to do is to reason against ShapeSheet cells. The problem here is that cells aren’t surfaced as a collection and so you have to iterate by index. To support this I’ve written a quick method to loop through the Shape Data section and report the values as strings. That way you can then compare two shapes using the Dif method and instantly see where the differences lie:
1: void Main()
2: {
3: var vApp = MyExtensions.GetRunningVisio();
4: var vSel = vApp.ActiveWindow.Selection;
5: Util.Dif(GetShapeData(vSel[1]), GetShapeData(vSel[2])).Dump();
6: }
7:
8:
9: public static object GetShapeData(Visio.Shape shp)
10: {
11: if (shp == null)
12: return null;
13:
14: IDictionary<string, object> shpDataInfo = null;
15: var propSectIdx = (short)Visio.VisSectionIndices.visSectionProp;
16:
17: if (shp.SectionExists[propSectIdx, 0] != 0)
18: {
19: shpDataInfo = new ExpandoObject();
20: for (short i = 0; i < shp.Section[propSectIdx].Count; i++)
21: {
22: var valueCell = shp.CellsSRC[propSectIdx, i, (short)Visio.VisCellIndices.visCustPropsValue];
23: shpDataInfo.Add($"Prop.{valueCell.RowNameU}", valueCell.ResultStrU[(short)Visio.VisUnitCodes.visUnitsString]);
24: }
25: }
26: return new { NameID = shp.NameID, ShapeData = shpDataInfo};
27: }
So, if you run the above code against the following flowchart:
…you’ll get the following output:
From the above, you can see that Sheet.5 has a different EndDate value from the one found in Sheet.6, and that it has an additional row (Prop.AnotherRow) that does not exist in Sheet.6.
The code is brief starting point, but you’re free to add other shape properties to the output anonymous object (line 26) or modify what get’s output in terms of Shape Data (or other) cells.
If you’re not familiar with the ExpandoObject then there’s a couple of good posts from Filip Ekberg and Rick Strahl respectively: