Having fun with "Group-Ease"
Posted
Sun, Jul 8 2007 5:06
by
bill
(note the title is phonetic <g>)
I thought I'd write a simple Group By query to list the controls on a form grouped by their type.
Dim query = From c In Me.Controls Group c By c.GetType.Name Into Members
Then to write out the code I wrote:
For Each group In query
Debug.Print("----- Control Type =" & group.Name & "-------")
For Each cntl As Control In group.Members
Debug.Print(cntl.Name)
Next
Next
That'll list the controls grouped by their type. Pretty simple, but we can improve the query a bit.
(1) First improvement is to make it more strongly typed. The Me.Controls returns a ControlsCollection which was designed before generics, so it's IEnumerable is As Object. This isn't obvious in the above query , and you avoid the issue of casting when you iterate by specifying For each cntl As Control In .... Currently you can't specify the cast to be made on the c variable in the From clause, but you can effectively cast the Controls collection via the Cast(Of TResult) extension. The query hence becomes :
Dim query = From c In Me.Controls.Cast(Of Control)() Group c By c.GetType.Name Into Members
Now the query is strongly typed through-out.
(2) the next thing you might want to do is sort the grouping. There's two levels of sorting you can do, one is inside the group, and the other is sorting the group itself. To sort the groups, we add a Order By Name to the end of the query.
Dim query = From c In Me.Controls.Cast(Of Control)() Group c By c.GetType.Name Into Members Order By Name
The "Name" variable is created from the c.GetType.Name part of the Group By clause, so you are really sorting by the calculated value of c.GetType.Name. You could give the grouping criteria a different name if you wanted, such as TypeName might be appropriate here :
Dim query = From c In Me.Controls.Cast(Of Control)() Group c By TypeName = c.GetType.Name Into Members Order By TypeName
If you want to also sort the controls inside each group, you can add an Order By clause before the grouping:
Dim query = From c In Me.Controls.Cast(Of Control)() Order By c.Name Group c By TypeName = c.GetType.Name Into Members Order By TypeName
(3) Now you probably want to make your code a little more readable and break that whopping one liner into a neater block :
Dim query = From c In Me.Controls.Cast(Of Control)() _
Order By c.Name _
Group c By TypeName = c.GetType.Name Into Members _
Order By TypeName
(4) Finally, you can make the query a little bit more explicit by including the Select clause. As it stands the query is implicitly selecting the TypeName and Members, hence the query is returning an IEnumerable(Of String, IEnumerable(Of Control)). That may not be obvious to people looking at the code, so adding the Select clause helps a bit in spelling that out.
Dim query = From c In Me.Controls.Cast(Of Control)() _
Order By c.Name _
Group c By TypeName = c.GetType.Name Into Members _
Order By TypeName _
Select TypeName, Members
Note: the Select clause is optional in this case. If you find it adds readability add it