Saturday, March 14, 2009

In Visual Basic .NET, there are several cases in which the statement completion list will present the user with a list of values rather than the standard completion set. Most often, this occurs when assigning to a variable of one of the common System.Drawing types, Color, Brush or Pen.

Value Completion List

At first glance, the screenshot above might seem as if the Visual Basic IDE has hard-coded a set of values into IntelliSense, but that’s not the case. In fact, this is caused by a seldom-used feature of XML Documentation that is supported by Visual Basic .NET, but isn’t currently supported by C#1. By cracking open the the XML Documentation file for System.Drawing.dll (located at C:\Windows\Microsoft.NET\Framework\v2.0.50727\en\System.Drawing.xml on my machine), we’ll see a curious XML tag on the System.Drawing.Color definition.

<member name="T:System.Drawing.Color">
  <
summary>Represents an ARGB (alpha, red, green, blue) color.</summary>
  <
filterpriority>1</filterpriority>
  <completionlist cref="T:System.Drawing.Color" />
</
member>

The highlighted completionlist tag above is used by Visual Basic to populate the completion list with the public shared2 fields and properties from the specified class or module. In this particular case, the XML documentation causes Visual Basic to populate the completion list with the public shared properties of System.Drawing.Color.

Don’t believe me? Just comment out the System.Drawing.Color completionlist tag above, save and restart Visual Studio to see how this influences the statement completion list.

Standard Completion List

Many of you are probably thinking, “so, is this just a nifty implementation detail, or is something I can actually use?” The answer is, yes, this something you can use today to customize Visual Basic’s statement completion. The code below demonstrates how the completionlist tag can be used.

''' <completionlist cref="CommonOperations"/>
Public Class Operation
     Private ReadOnly _execute As Func(Of Integer, Integer, Integer)

     Public Sub New(ByVal execute As Func(Of Integer, Integer, Integer))
         _execute = execute
     End Sub

     Public Function
Execute(ByVal arg1 As Integer, ByVal arg2 As Integer) As Integer
         Return
_execute(arg1, arg2)
     End Function
End Class

Public NotInheritable Class
CommonOperations
     Public Shared ReadOnly Add = New Operation(Function(x, y) x + y)
     Public Shared ReadOnly Subtract = New Operation(Function(x, y) x - y)
     Public Shared ReadOnly Multiply = New Operation(Function(x, y) x * y)
     Public Shared ReadOnly Divide = New Operation(Function(x, y) x / y)
End Class

Visual Basic will automatically pick up the completionlist tag in the code above and use it to populate the completion list like so.

Custom Value Completion List

While a bit limited, it’s pretty easy to customize the statement completion list experience for Visual Basic to make certain types of APIs more discoverable. It’s as simple as a single XML tag.

1Kevin Pilch-Bisson (C# IDE Developer Lead and swell guy) has a clever idea for supporting the completionlist tag in the C# statement completion list while staying true to the C# IntelliSense model.
2The VB "Shared" keyword = static in C#.

posted on Saturday, March 14, 2009 12:23:10 PM (Pacific Standard Time, UTC-08:00)  #    Comments [4]

kick it on DotNetKicks.com
Monday, March 16, 2009 10:00:56 AM (Pacific Standard Time, UTC-08:00)
I love the footnote on the "Shared" keyword. Someday I hope that C# devs become smart enough to be able to understand VB without these footnotes so they'll stop bugging me to convert DNN to C#. It's not as if we are asking them to learn F# or anything weird like that. ;)
Monday, March 16, 2009 5:07:53 PM (Pacific Standard Time, UTC-08:00)
Any link to Kevin Pilch-Bisson's implementation for C#? I'm interested to know more.
Tuesday, March 17, 2009 1:04:57 AM (Pacific Standard Time, UTC-08:00)
Ikhwan:

There isn't an implementation to link to. It was just a conversation that we had awhile back about a potential design. However, we haven't done anything for Visual Studio 2010 in C# IntelliSense to support this feature.
Saturday, March 21, 2009 4:01:33 PM (Pacific Standard Time, UTC-08:00)
I owe you a beer for this one. I've been looking for the secret that drives this for YEARS. I reflected over the .NET assemblies looking for attributes, scanned System.Component model a dozen times and found Nothing (or null if you're so inclined). I'm surprised I never noticed this was missing from C# - it surely would have started a flame war at work.

Thanks for posting this man - make that two beers.
Anthony D. Green
Comments are closed.