Set an Enum Dependency Property with XAML

April 23, 2009 04:10 by wjchristenson2

Some new light has been shed on this topic in a Silverlight.net forum thread found here: http://silverlight.net/forums/t/91790.aspx

My explanation below assumes that custom logic is needed when converting from a string to an enumerated type.  The above thread shows how to setup an Enumurated Type dependency property and .NET will convert from string the the enumerated type just fine.

I'm going to leave the post below as there are going to be situations where you may want to use a TypeConverter.

***************************************************************************

There are situations where you want to set a property's value in XAML and .NET is unable to convert the string value to the property's type.  I’ve created a solution which provides an example of how to fix this using a TypeConverter.

The TypeConverter will basically tell .NET that we can convert a string to our type and furthermore, we will provide the logic on how to convert it to our custom type.

First step is to create our TypeConverter.  In my example, I have a vehicle control and we are going to set the type of vehicle.  Here is the code for our VehicleTypeConverter.

Imports System.ComponentModel

Public Class VehicleTypeConverter
    Inherits TypeConverter

    Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
        If sourceType.Equals(GetType(String)) Then
            Return True
        Else
            Return MyBase.CanConvertFrom(context, sourceType)
        End If
    End Function

    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        If destinationType.Equals(GetType(String)) Then
            Return True
        Else
            Return MyBase.CanConvertTo(context, destinationType)
        End If
    End Function

    Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        If TypeOf value Is String Then
            Try
                Return CType([Enum].Parse(GetType(Vehicle.VehicleType), value, True), Vehicle.VehicleType)
            Catch
                Throw New InvalidCastException(value)
            End Try
        Else
            Return MyBase.ConvertFrom(context, culture, value)
        End If
    End Function

    Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
        If destinationType.Equals(GetType(String)) Then
            Return value.ToString()
        Else
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End If
    End Function
End Class

As I noted earlier, this class tells .NET that strings are allowed to be converted to our type property and we also provide the logic on how to convert the string value to the type.  Now that we have this converter defined, we can use it in our control, page, or wherever our type property is being used.  I’m using it in the vehicle control.  Here’s how we use the TypeConverter.

Imports System.ComponentModel

Partial Public Class Vehicle
    Inherits UserControl

    Public Enum VehicleType
        NA = 0
        Car = 1
        Bus = 2
        Truck = 3
    End Enum

    Public Shared ReadOnly TypeProperty As DependencyProperty = DependencyProperty.Register("Type", GetType(VehicleType), GetType(Vehicle), New PropertyMetadata(VehicleType.NA, AddressOf TypeChangedHandler))

    <TypeConverter(GetType(VehicleTypeConverter))> _
    Public Property Type() As VehicleType
        Get
            Return DirectCast(GetValue(Vehicle.TypeProperty), VehicleType)
        End Get
        Set(ByVal value As VehicleType)
            SetValue(Vehicle.TypeProperty, value)
        End Set
    End Property

    Public Sub New()
        InitializeComponent()
    End Sub

    Private Shared Sub TypeChangedHandler(ByVal o As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
        DirectCast(o, Vehicle).OnTypeChanged(DirectCast(args.NewValue, VehicleType))
    End Sub

    Private Sub OnTypeChanged(ByVal newValue As VehicleType)
        Me.tbkTest.Text = "The vehicle type is a " & newValue.ToString() & "."
    End Sub

End Class

Everything is straight forward here except for how we wire in our VehicleTypeConverter.  We add attributes to our Type property to tell the property that we will handle the converting from and to the VehicleType.

Below you’ll find the entire solution if you wish to see it in action.

EnumDependencyProperty_Soln.zip (597.05 kb)

Bookmark and Share

Silverlight Custom Content Control

March 30, 2009 02:10 by wjchristenson2

As a Silverlight control developer, you may encounter a need to create a custom content control.  This type of control allows the consumers to place anything they want in certain areas of your control.  In Silverlight, you may have noticed that the Button control has a content property.  You can essentially put anything you want in a button.  I’m going to show you how you can create a control to do this.

We are going to create a control that has a header and body.  The final result will look like the image below:

 

Step 1 – Setup the Solution

The first step in creating our control is to ensure that our Visual Studio environment is setup correctly.  We’ll have our basic Silverlight Application project and Web Project to test it in.  However, we are going to add a Silverlight Class Library project and add a reference to it in our Silverlight Application project.

The next step is what gets a lot of developers.  Add a “themes” folder in our controls project (Silverlight Class Library project). Inside the “themes” folder, add a new text file and name it “generic.xaml”.  Make sure that the folder/file names are lower case!

 

Silverlight 2 does not support themes like WPF does.  However, inserting the “themes/generic.xaml” file inside our controls project does yield us the ability to define our CustomContentControl style/template.

 

Step 2 – Create the Custom Content Control

Now that our solution is ready, let’s add our CustomContentControl to our controls project.  We’ll be inheriting from the ContentControl object.  Therefore by default, we’ll already have a Content property.  However we want to create an additional Header property so that other developers can put whatever they want in the header.  Here’s our CustomContentControl code:

Public Class CustomContentControl
    Inherits ContentControl

    Public Shared ReadOnly HeaderProperty As DependencyProperty = DependencyProperty.Register("Header", GetType(UIElement), GetType(CustomContentControl), Nothing)

    Public Property Header() As UIElement
        Get
            Return DirectCast(Me.GetValue(CustomContentControl.HeaderProperty), UIElement)
        End Get
        Set(ByVal value As UIElement)
            Me.SetValue(CustomContentControl.HeaderProperty, value)
        End Set
    End Property

    Public Sub New()
        MyBase.New()
        Me.DefaultStyleKey = GetType(CustomContentControl)
    End Sub

End Class

Here we have our control which inherits from ContentControl and has a Header property defined.  There’s one last thing to mention about our control.  Check out line 17.  We set the DefaultStyleKey to a resource object which we are going to define in our generic.xaml file.  So let’s do that.

 

Step 3 – Create Control’s DefaultStyleKey (generic.xaml)

In step 2 we saw that after our CustomContentControl was instantiated we immediately wired up its DefaultStyleKey.  In the generic.xaml file we’ll define our style/template for our CustomContentControl.  The style will automatically be found and used for our control.  Here’s the style for our control:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ContentControlExample.Controls">
   
    <!-- CustomContentControl -->
    <Style TargetType="local:CustomContentControl">
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Stretch" />
        <Setter Property="HorizontalContentAlignment" Value="Left" />
        <Setter Property="VerticalContentAlignment" Value="Top" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:CustomContentControl">
                    <Border Background="White" BorderBrush="#87AFDA" BorderThickness="1">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                              
                            <Border Grid.Column="0" Grid.Row="0" Background="#D4E6FC" BorderThickness="0,0,0,1" BorderBrush="#87AFDA">
                                <ContentControl Content="{TemplateBinding Header}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Foreground="#224499" FontWeight="Bold" FontFamily="Arial" FontSize="12" Margin="3,3,3,3" />
                            </Border>
                            <ContentControl Grid.Column="0" Grid.Row="1" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" Cursor="{TemplateBinding Cursor}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" FontFamily="Arial" FontSize="{TemplateBinding FontSize}" FontStretch="{TemplateBinding FontStretch}" Foreground="{TemplateBinding Foreground}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

At a quick glance take note that we have a ContentControl for both the Header and Content properties of our CustomContentControl.  When the template is bound, the Header and Content values will be placed inside our ContentControls defined here.

 

Step 4 – Use the CustomContentControl

Now that we have the solution setup, our control coded, and our default style/template defined in the generic.xaml file, we can use our control.  Here’s the page XAML that uses the control.

<UserControl x:Class="ContentControlExample.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:ex="clr-namespace:ContentControlExample.Controls;assembly=ContentControlExample.Controls"
    Width="300" Height="200">
    <Grid x:Name="LayoutRoot" Background="White">
        <ex:CustomContentControl x:Name="myCustomContentControl">
            <ex:CustomContentControl.Header>
                <TextBlock Text="This is the Header" />
            </ex:CustomContentControl.Header>
            <ex:CustomContentControl.Content>
                <StackPanel Orientation="Vertical" Margin="5">
                    <TextBlock Text="This is the body or content." Margin="0,0,0,5" />
                    <Button Content="Click Me!" Width="100" Height="30" />
                </StackPanel>
            </ex:CustomContentControl.Content>
        </ex:CustomContentControl>
    </Grid>
</UserControl>

If you wish to add events to your custom content control, I've written another post which can be found here.

ContentControlExample_Soln.zip (619.31 kb)

Bookmark and Share

Silverlight 3 Beta Released

March 18, 2009 07:07 by wjchristenson2

I just found out that Silverlight 3 Beta is out.  Microsoft must be on an annual release cycle.

Here you will find the downloads, what’s new in Silverlight 3, as well as helpful videos.

Some of the new features that really caught my eye are:

1) Support for 3D graphics
2) Binary XML (communication with the server is compressed which will greatly increase data exchanges).
3) Out of Browser Capabilities -  Wow!  Detecting Internet connectivity, running your SL application from the desktop, auto-updating, etc.
4) More RIA built-in controls (new layout containers, treeview, chart/graphing).

Bookmark and Share

Silverlight DataGrid ItemsSource Filter

February 19, 2009 08:07 by wjchristenson2

In this post, I am going to show how to easily filter a collection of objects without creating a whole new collection.  We’ll be filtering collections which implement the IEnumerable interface. Specifically we’ll be casting our collection to an IEnumerable(Of T) which exposes the enumerater and is used to iterate over a collection of a specified type.

Personally I’ve used the IEnumerable(Of T) to filter a DataGrid in Silverlight.  When you want to bind data to the DataGrid, you set the ItemsSource property to your collection.  We’ll simply filter the DataGrid’s ItemsSource collection.  Here’s what our Silverlight filtering application looks like:

First, let’s setup our UI.  I’m going to create a collection of people.  Each person has a name and an age.  We want to filter on age.  Therefore the UI will have a ComboBox so we can select an age to filter on.  We’ll also have a filter Button and a clear filter Button.  We’ll finally have a DataGrid to display our filtered (or unfiltered) results.  Here’s the XAML to produce the UI:

<UserControl x:Class="FilterCollection.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
    Width="400" Height="250">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="25" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="75" />
            <ColumnDefinition Width="75" />
        </Grid.ColumnDefinitions>
          
        <ComboBox x:Name="cbxAge" Grid.Column="0" Grid.Row="0" Height="25"  />
        <Button x:Name="btnFilter" Content="Filter" Grid.Column="1" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
        <Button x:Name="btnClear" Content="Clear" Grid.Column="2" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
        <data:DataGrid x:Name="dgResults" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" AutoGenerateColumns="True" Margin="0,5,0,0" />
    </Grid>
</UserControl>

Before we can create our collection of people, we’ll first need to create a Person object/class.  Here’s the code to accomplish this:

Public Class Person
    Private _Name As String
    Private _Age As Integer

    Public Property Name() As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
        End Set
    End Property

    Public Property Age() As Integer
        Get
            Return _Age
        End Get
        Set(ByVal value As Integer)
            _Age = value
        End Set
    End Property

    Public Sub New(ByVal name As String, ByVal age As Integer)
        _Name = name
        _Age = age
    End Sub
End Class

 

Now that we have our UI and Person object ready, we can finally create our collection and initialize our UI.  I first create the Persons collection, then create a collection of Age(s) to filter by.  I then bind the ComboBox and the DataGrid to our collections.  Keep in mind that the DataGrid is not filtered at this point.

'initialize our collection of person(s)
_Persons = New List(Of Person)
_Persons.Add(New Person("Jake", 29))
_Persons.Add(New Person("Same", 28))
_Persons.Add(New Person("Beth", 29))
_Persons.Add(New Person("Sue", 13))
_Persons.Add(New Person("Tina", 12))
_Persons.Add(New Person("Pete", 13))
_Persons.Add(New Person("Andy", 10))
_Persons.Add(New Person("Ashley", 11))
_Persons.Add(New Person("Sydney", 10))

'initialize our combobox of ages
Dim ages As List(Of Integer) = New List(Of Integer)
For i As Integer = 1 To 30 Step 1
    ages.Add(i)
Next
Me.cbxAge.ItemsSource = ages
Me.cbxAge.SelectedIndex = 0

'initialize our DataGrid to our collection of person(s)
Me.dgResults.ItemsSource = _Persons

Now we are ready to make the magic happen.  When we select an Age and press our Filter Button, we want to filter the _Persons collection and rebind the DataGrid.  Here’s the code:

'acquire the age to filter on
Dim age As Integer = DirectCast(Me.cbxAge.SelectedItem, Integer)

'convert our Persons collection to an IEnumerable(Of T) where T (type) = Person
Dim source As IEnumerable(Of Person) = DirectCast(_Persons, IEnumerable(Of Person))

'filter the collection using the "Where" extension method
Me.dgResults.ItemsSource = source.Where(Function(p As Person) p.Age = age)

We first acquire the selected Age.  Then we convert our collection of Person(s) to an IEnumerable(Of Person).  This allows us to use the IEnumerable(Of T).Where method extension.  Notice I’ve passed in a function to the Where predicate to test each Person to see whether their Age is equal to the selected age.  Only people who’s age is equal to our filter will be bound to the DataGrid.  Here is a picture of our filtered DataGrid:

If we want to clear our filter, we simply set the ItemsSource of our DataGrid back to the original _Persons collection.

'reset the DataGrid's ItemsSource back to our original Persons collection
Me.dgResults.ItemsSource = _Persons

FilterCollection_Soln.zip (1.02 mb)

Bookmark and Share

Fixing WCF Circular Reference Serialization

February 3, 2009 09:03 by wjchristenson2

As a WCF developer, you may (or soon will) run into problems when an object is referenced multiple times within the business objects being serialized.  Let’s say we have a parent object which has a collection of children.  Therefore we have one-to-many children for a parent.  Let’s say we want a property for each child which references its parent.  When the DataContractSerializer attempts to serialize the parent and children, you’ll soon discover that the cyclic relationship between the parent and its children and back to the parent will cause a stack overflow.  Below is a basic diagram that shows a basic parent to child – child to parent object (cyclic) relationship where this can occur.

I’ve seen a lot of workarounds to this problem and most involve writing your own serializers.  In .NET 3.5 SP1, we can now specify an IsReference parameter in the DataContract attribute which will automatically retain our cyclic object relationships for us.  Specify this for objects which are referenced in other objects.  In our example, the parent would be a reference because the child object references it.  The child object would not be a reference because the children property is a collection of child.  Simple solution compared to what WCF developers had to do in the past.  Here’s a sample of a class definition that is of a reference type:

<DataContract(IsReference:=True)> _
Public Class Parent
Bookmark and Share