Wednesday, September 24, 2008
Period Key Closes Dialog Box in Visual Studio
Wednesday, August 27, 2008
LINQ Aggregates – Max() and Min() with no results from Where clause
Module Module1 Private Structure test Public Key As String Public Value As Integer End Structure Sub Main() Dim myList As New List(Of test) myList.Add(New test() With {.Key = "A", .Value = 1}) myList.Add(New test() With {.Key = "A", .Value = 2}) myList.Add(New test() With {.Key = "A", .Value = 3}) myList.Add(New test() With {.Key = "B", .Value = 1}) myList.Add(New test() With {.Key = "B", .Value = 2}) myList.Add(New test() With {.Key = "C", .Value = 1}) 'Get Max Value of the A values -- Res1 is set to 3 Dim Res1 = Aggregate t In myList Where t.Key = "A" Into Max(t.Value) 'Get Max Value of the D value, which doesn't exist in the data collection Dim Res2 = Aggregate t In myList Where t.Key = "D" Into Max(t.Value) 'This will fail with "Sequence contains no elements" error 'Yet the SUM aggregate function works without error and sets Res3 to 0 Dim Res3 = Aggregate t In myList Where t.Key = "D" Into Sum(t.Value) End Sub End Module
Module Module2 'Forget Max and just sort results and take first value Private Structure test Public Key As String Public Value As Integer End Structure Sub Main() Dim myList As New List(Of test) myList.Add(New test() With {.Key = "A", .Value = 1}) myList.Add(New test() With {.Key = "A", .Value = 2}) myList.Add(New test() With {.Key = "A", .Value = 3}) myList.Add(New test() With {.Key = "B", .Value = 1}) myList.Add(New test() With {.Key = "B", .Value = 2}) myList.Add(New test() With {.Key = "C", .Value = 1}) 'Create a blank list for testing Dim blankList As New List(Of test) 'Res1 will be 3 Dim Res1 = (From t In myList Where t.Key = "A" Order By t.Value Descending Select t.Value).FirstOrDefault() 'Res2 will be 0 because there is no "D" key Dim Res2 = (From t In myList Where t.Key = "D" Order By t.Value Descending Select t.Value).FirstOrDefault() 'Res3 will be 0 because the list is blank Dim Res3 = (From t In blankList Where t.Key = "A" Order By t.Value Descending Select t.Value).FirstOrDefault() 'Rewrite Res2 example using the Take clause. 'Res4 will be 0 because there is no "D" key Dim Res4 = (From t In myList Where t.Key = "D" Order By t.Value Descending Take 1 Select t.Value).SingleOrDefault() End Sub End Module
Module Module3 'Calling Max directly with Lambda Expressions Private Structure test Public Key As String Public Value As Integer End Structure Sub Main() Dim myList As New List(Of test) myList.Add(New test() With {.Key = "A", .Value = 1}) myList.Add(New test() With {.Key = "A", .Value = 2}) myList.Add(New test() With {.Key = "A", .Value = 3}) myList.Add(New test() With {.Key = "B", .Value = 1}) myList.Add(New test() With {.Key = "B", .Value = 2}) myList.Add(New test() With {.Key = "C", .Value = 1}) 'Create a blank list for testing Dim blankList As New List(Of test) 'Res1 will be 0 Dim Res1 = myList.Max(Function(p As test) If(p.Key = "D", p.Value, 0)) 'Res2 will error on empty list with "Sequence contains no elements" Dim Res2 = blankList.Max(Function(p As test) If(p.Key = "D", p.Value, 0)) 'Res3 will be nothing since list is blank and Lamda returns nullable integer type Dim Res3 = blankList.Max(Function(p As test) If(p.Key = "D", p.Value, New Integer?(0))) End Sub End Module
Module Module4 'Take 2 on Aggregate clause by forcing null values Private Structure test Public Key As String Public Value As Integer End Structure Sub Main() Dim myList As New List(Of test) myList.Add(New test() With {.Key = "A", .Value = 1}) myList.Add(New test() With {.Key = "A", .Value = 2}) myList.Add(New test() With {.Key = "A", .Value = 3}) myList.Add(New test() With {.Key = "B", .Value = 1}) myList.Add(New test() With {.Key = "B", .Value = 2}) myList.Add(New test() With {.Key = "C", .Value = 1}) 'Create a blank list for testing Dim blankList As New List(Of test) 'Res1 will be nothing since D does not exist and the overloaded ' Max function is called with a Nullable integer type. Dim Res1 = Aggregate t In myList Where t.Key = "D" Into Max(New Integer?(t.Value)) 'Same example but forcing the Integer Default value of 0 'Res2 will be 0 Dim Res2 = (Aggregate t In myList Where t.Key = "D" Into Max(New Integer?(t.Value))).GetValueOrDefault 'And calling on an empty list works too! Dim Res3 = (Aggregate t In blankList Where t.Key = "D" Into Max(New Integer?(t.Value))).GetValueOrDefault End Sub
Monday, August 11, 2008
A great use for extension methods
Extension Methods in a nutshell
<Extension()> _ Sub PrintLastName(ByRef contact As Contact) Console.WriteLine(contact.LastName) End Sub
Dim c As Contact = New Contact() With { _ .FirstName = "John", _ .LastName = "Smith", _ .Phone = "214-844-1212"} 'Call extension method c.PrintLastName()
PrintLastName(c)
My scenario and use of Extension Methods
Public Class Contact Private _contactId As Integer? Private _firstName As String Private _lastName As String Private _phone As String 'Plus all the properties to expose these members. End Class
Public Class ContactDAL 'CRUD methods Private Sub SelectContact(. . . Private Sub InsertContact(. . . Private Sub UpdateContact(. . . Private Sub DeleteContact(. . . End Class
Public Class ContactFacade Private Sub GetContact(. . . Private Sub SaveContact(. . . Private Sub CalculateSales(. . . End Class
Contact.Save()
ContactFacade.SaveContact(c)
Function SaveContact(ByVal contact As Contact)
<Extension()> _ Sub Save(ByVal contact As Contact)
Note on passing objects ByRef
<Extension()> _ Sub Delete(ByRef contact As Contact) 'Only try to delete from database if object has Database Id If contact.ContactId.HasValue Then contactDao.DeleteContact(contact) End If contact = Nothing End Sub
Consculsion
Friday, August 8, 2008
Custom TypeDescriptionProvider not being processed by Visual Studio Designer
While working with custom TypeDescriptionProvider's and setting TypeDescriptionProviderAttribute's on my classes, I found that my custom meta data was not being processed by the Visual Studio Datasources Window.
A single project had been created that contained both a Windows Form and my custom class files. This was just simply a test or "proof of concept" application so I just threw everything into one project. When I used the Data Sources window to add my class, Visual Studio was only showing the "real" properties of the class. This was in spite of the fact that I had decorated my class with the TypeDescriptionProviderAttribute pointing to my custom TypeDescriptionProvider that provided an extended list of properties. I knew the custom TypeDescriptionProvider was working correctly because I could create my data bindings in code.
I went to split up my single project into two differnt solutions so that I could debug the Visual Studio designer calls. I put the Window Form in one solution and my custom class and custom TypeDescriptionProvider in another solution. When I went to add my custom class to the Data Sources window of the Form solution, it came up correctly this time! Also, I tested the same with having one solution with two projects, and it still worked. So there must be a limitation or issue with having a TypeDescriptionProvider and attempting for the IDE to use it in the same project.
Conclusion, if you expect the Visual Studio IDE to process you Custom TypeDescriptionProvider, make sure those classes are in a separate project.
Thursday, August 7, 2008
AttributeCollection.Contains Method Can Return True on an Empty Collection
I found a weird side effect, or so I first thought, of the Contains method of the AttributeCollection class.
'desc references an instance of my custom type derived from the PropertyDescriptor class 'desc.Attriutes is an EMPTY collection dim x as boolean = desc.Attributes.Contains(New BrowsableAttribute(true))
Since desc.Attributes is an empty collection, calling the Contains method with any value couldn't possibly return anything but false, right?!?!? But as it turns out, the above statement will return True. This seemed absolutely crazy to me at first. But then I called:
'This time BrowsableAttribute is set false. dim x as boolean = desc.Attributes.Contains(New BrowsableAttribute(false))
In this case x was set to False.
So my conclusion is that there must be some extra logic in the AttributeCollection.Contains method. It is not just your typical contains method that is just simply looking for an object match. It is smarter than that!
Since the BrowsableAttribute default is True, asking if the Attributes collection contains a default attribute value is in fact really true even when the Attributes collection is empty, or for that matter contains other attributes.
Now that I understand this, I am confident that this was the intended behavior of the Contains method. And if you really want to see if an Attribute is specified in the collection, regardless of its default setting, use the AttributeCollection.Matches method instead.
Sunday, August 3, 2008
Custom Markup Extensions in WPF
Problem to be solved
'This example uses the Linq so be sure to reference and import System.Data.Linq Module ListOptions 'This example creates the data array in code. In a real app this would be a 'database query in place of this code. Private _options As ListOption() = { _ New ListOption With {.Key = "ShirtColor", .Display = "Red"}, _ New ListOption With {.Key = "ShirtColor", .Display = "Green"}, _ New ListOption With {.Key = "ShirtColor", .Display = "Blue"}, _ New ListOption With {.Key = "ShirtSize", .Display = "Small"}, _ New ListOption With {.Key = "ShirtSize", .Display = "Medium"}, _ New ListOption With {.Key = "ShirtSize", .Display = "Large"}, _ New ListOption With {.Key = "NamePrefix", .Display = "Mr."}, _ New ListOption With {.Key = "NamePrefix", .Display = "Mrs."}, _ New ListOption With {.Key = "NamePrefix", .Display = "Ms."}, _ New ListOption With {.Key = "NameSuffix", .Display = "Jr."}, _ New ListOption With {.Key = "NameSuffix", .Display = "Sr."} _ } Function GetOptions(ByVal key As String) As List(Of ListOption) 'Do a simple LINQ expression to return all ListOption objects that match 'the specified key Return (From lo In _options Where lo.Key = key Select lo).ToList End Function End Module
'Class to represent ListOption Table Row Public Class ListOption Private _key As String Public Property Key() As String Get Return _key End Get Set(ByVal value As String) _key = value End Set End Property Private _display As String Public Property Display() As String Get Return _display End Get Set(ByVal value As String) _display = value End Set End Property '... 'Also would need value member, display order, etc. End Class
Typical Solutions
<window class="Window1" title="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" width="300" local="clr-namespace:WpfApp1" height="300" x="http://schemas.microsoft.com/winfx/2006/xaml"> <window.resources> <objectdataprovider methodname="GetListOptions" objecttype="{x:Type local:ListOptions}" key="ColorOptions"> <objectdataprovider.methodparameters> NamePrefix </objectdataprovider.methodparameters> </objectdataprovider> </window.resources> <grid> <listbox name="MyListBox" height="80" displaymemberpath="Display" itemssource="{Binding Source={StaticResource ColorOptions}}"> </grid> </window>
Class Window1 Private Sub Window1_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded MyListBox.ItemsSource = GetListOptions("NamePrefix") End Sub End Class
Markup Extension Overview
Markup extensions are a XAML concept. In attribute syntax, curly braces ({ and }) indicate a markup extension usage. This usage directs the XAML processing to escape from the general treatment of attribute values as either a literal string or a directly string-convertible value.
{x:Static ...} {x:Type ...} {StaticResource ...}
Public Class HelloWorldExtension Inherits Markup.MarkupExtension 'This method is called by the XAML processor. Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object Return "Hello World!" End Function End Class
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApp1" Title="Window1" Height="300" Width="300" Name="Window1"> <Grid> <TextBox Text="{local:HelloWorld}" VerticalAlignment="Bottom" Height="20" /> </Grid> </Window>
{StaticResource MyKey} {StaticResource ResourceKey=MyKey}
Public Class HelloWorldExtension Inherits Markup.MarkupExtension Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property 'Default Constructor Public Sub New() End Sub 'Constructor to set name property Public Sub New(ByVal name As String) _name = name End Sub 'This method is called by the XAML processor. Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object Return "Hello World! From " & _name End Function End Class
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApp1" Title="Window1" Height="300" Width="300" Name="Window1"> <Grid> <TextBox Text="{local:HelloWorld John}" VerticalAlignment="Top" Height="20" /> <TextBox Text="{local:HelloWorld Name=John}" VerticalAlignment="Bottom" Height="20" /> </Grid> </Window>
The solution
<Markup.MarkupExtensionReturnType(GetType(List(Of ListOption)))> _ Public Class ListOptsExtension Inherits Markup.MarkupExtension 'Define our member and property for the Key Private _key As String Public Property Key() As String Get Return _key End Get Set(ByVal value As String) _key = value End Set End Property 'Default Constructor Public Sub New() End Sub 'Constructor with key specified as parameter Public Sub New(ByVal key As String) _key = key End Sub 'This method is called by the XAML processor. Public Overrides Function ProvideValue(ByVal serviceProvider As System.IServiceProvider) As Object Return GetListOptions(_key) End Function End Class
<window class="Window1" title="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" name="Window1" width="300" local="clr-namespace:WpfApp1" height="300" x="http://schemas.microsoft.com/winfx/2006/xaml"> <grid> <listbox name="MyListBox" height="80" displaymemberpath="Display" itemssource="{local:ListOpts NamePrefix}"> </grid> </window>