WCF DateTime Serialization

May 11, 2010 07:24 by wjchristenson2

By default, if the programmer does not specify the DateTime kind, .NET WCF services will serialize and deserialize DateTime objects based on the local time zone.  What this means is that if you have a client entering information on the east coast (EST) and your server is in central time (CST), the DateTime values entered by the client will have their hour decremented by 1 when the data is deserialized at the server.

The reason why WCF behaves this way is because it thinks that the DateTime entered on the client is in local time.  As a result, when the value reaches the server, 11:00 AM client local time is converted to 10:00 AM because that is what the local time is on the server.

If this is not what is desired, specifying the kind of the DateTime value appears to alter the serialization/deserialization behavior.  Specifying what kind of date your value is will not alter the underlying value.

Unspecified - No conversion:  returnValue = DateTime.SpecifyKind(value, DateTimeKind.Unspecified)
Local - Time Zone conversions:  returnValue = DateTime.SpecifyKind(value, DateTimeKind.Local) 
UTC – No conversions:  returnValue = DateTime.SpecifyKind(value, DateTimeKind.Utc)


Silverlight 4 Final – Released 4/15/2010

April 18, 2010 10:59 by wjchristenson2

On Thursday 4/15/2010, Microsoft officially released the final version Silverlight 4.  Scott Guthrie gave a 60 minute keynote on Silverlight 4 a few days prior.  If you haven’t watched the keynote, I’d recommend doing so.  I was impressed with the new features and the demos provided.  Silverlight 4 includes a ton of new features. 

Here is a quick list of some of the new features that I found interesting:

  • Tooling – Multi-Targeting Silverlight 3 and 4.  You can also now design within VS 2010.
  • Printing API – You can now print from Silverlight.
  • Right-Click Event Handling – MouseRightButtonUp/Down events are now available.
  • Webcam & Microphone Access – SL can now use the client’s webcam(s) and microphone(s).
  • Mouse Wheel Support – The mouse wheel event was added in SL 3, but now TextBox, ComboBox, Calendar, DatePicker, and the ScrollViewer auto-scroll.  You no longer have to manually handle the event.
  • RichTextArea Control – Provides an area where users can create and edit text and specify bold/italic/underlined/etc text.
  • Google Chrome Support - M$ finally now supports Google Chrome.
  • Implicit Theming – Create a style for a targeted type and all of those types will implicitly use it.
  • Fluid UI Support in the ItemsControl – 3 new states: BeforeLoaded, Loaded, and Unloaded.
  • DataGrid Enhancements – Column relative width sizing refactored.
  • DataBinding Enhancements – Binding can now use StringFormat, TargetNullValue, FallbackValue (e.g. Dates no longer requires ValueConverters to format the date).
  • IDataErrorInfo – When this interface is implemented, it reports data validation errors that a UI can bind to.  Only one property is validated/reported on at a time.
  • INotifyDataErrorInfo – Allows developers to provide custom validation logic server-side via asynchronous routines.  Here is a video on the new data validation features.
  • Silverlight Drop Target – Drag and drop folders/files into your Silverlight application.
  • ViewBox – New control which is used to simplify the resizing of images.
  • Text Trimming – Auto adding of word ellipse (…)
  • Keyboard Access in Full Screen Mode – If SL is in full screen mode – all keyboard input is accepted in Silverlight as long as the application is trusted.
  • Network Authentication – You can now pass separate credentials when connecting with 3rd party service providers.
  • COM Interop – Silverlight can now create COM objects – however this only applies to trusted SL applications.
  • Notification (“Toast”) API – Notifications (like a new Outlook email as arrived) can now be used.
  • Local File Access – SL can now access the user’s “My” folders (e.g. My Documents, etc).  This requires trusted SL application.
  • Elevated Trust Applications – Many new features in SL4 require elevated trust.  This new feature will prompt the user to allow/fully trust the SL application when installed.
  • HTML Hosting with WebBrowser – You can now show and host HTML within your SL application.
  • Clipboard API – SL now has access to copy to and paste from the clipboard.

Fiddler - Inspecting WCF Binary Encoded Messages

November 25, 2009 08:57 by wjchristenson2

Fiddler is the tool of choice when it comes to inspecting WCF messages that utilize an HTTP transport.  In a previous post I mentioned that HTTP WCF messages should be in encoded in a binary format to decrease their size.  The problem you run into when encoding your WCF messages is that when Fiddler attempts to inspect an encoded message, it’s not in a readable format.  To resolve this limitation, you can download a free plug-in which will add an inspector to Fiddler to decode binary WCF message.  Using this new inspector, you can inspect binary encoded WCF messages freely from within Fiddler.

http://code.msdn.microsoft.com/wcfbinaryinspector



Silverlight 3 WCF Binary Message Encoding

October 14, 2009 13:29 by wjchristenson2

Silverlight 3 offers us some new features when it comes to WCF web services.  In Silverlight 2, BasicHttpBinding was the only supported binding.  This essentially encodes your serialized objects in clear text and sends them over an HTTP transport.  Because the objects were sent as clear text, the message size could get out-of-hand.  When sending data across HTTP/Internet, you obviously want to decrease the size as this will improve performance of your client application.  Silverlight 3 offers the ability to create custom bindings which support the ability to encode your WCF web service messages as a binary format.

Binary encoding offers some serious performance gains over text encoding.  Personally I’ve seen 30% – 40% reduction in message size between the server and client when binary encoding is enabled.  Keep in mind that binary encoding is a WCF-specific feature.  Therefore if you have heterogeneous technologies wanting to consume your service, you’ll need to stick with BasicHttpBinding.  Here is an example of how to enable binary encoding for your WCF service:


   1:  <bindings>
   2:     <customBinding>
   3:        <binding name="binaryHttpBinding">
   4:           <binaryMessageEncoding />
   5:           <httpTransport />
   6:        </binding>
   7:     </customBinding>
   8:  </bindings>
   9:   
  10:  <endpoint address="" binding="customBinding" bindingConfiguration="binaryHttpBinding" contract="MyService" />

Silverlight Custom Content Control with Events

July 6, 2009 13:40 by wjchristenson2

In an earlier post, I wrote about how to develop a Silverlight custom content control.  Since then I’ve received inquiries as to how to add events to it.  More specifically, how can we add interactivity to the control and have the control raise events which can be handled by the consumer of our control.

Step 1: Add Template UI Elements to Receive User Interaction

In the previous post, I described how to create a generic.xaml file to house our templated control UI (style).  In this example, I am going to create a close HyperlinkButton and raise a close event.  Here’s our style which is defined in the generic.xaml file.  Take not of our new hlbClose HyperlinkButton (line 34).


   1:  <ResourceDictionary 
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:local="clr-namespace:ContentControlExample.Controls">
   5:   
   6:      <!-- CustomContentControl -->
   7:      <Style TargetType="local:CustomContentControl">
   8:          <Setter Property="Background" Value="Transparent" />
   9:          <Setter Property="Foreground" Value="Black" />
  10:          <Setter Property="BorderBrush" Value="Transparent" />
  11:          <Setter Property="BorderThickness" Value="0" />
  12:          <Setter Property="HorizontalAlignment" Value="Stretch" />
  13:          <Setter Property="VerticalAlignment" Value="Stretch" />
  14:          <Setter Property="HorizontalContentAlignment" Value="Left" />
  15:          <Setter Property="VerticalContentAlignment" Value="Top" />
  16:          <Setter Property="Template">
  17:              <Setter.Value>
  18:                  <ControlTemplate TargetType="local:CustomContentControl">
  19:                      <Border Background="White" BorderBrush="#87AFDA" BorderThickness="1">
  20:                          <Grid>
  21:                              <Grid.RowDefinitions>
  22:                                  <RowDefinition Height="Auto" />
  23:                                  <RowDefinition Height="*" />
  24:                              </Grid.RowDefinitions>
  25:                              
  26:                              <Border Grid.Column="0" Grid.Row="0" Background="#D4E6FC" BorderThickness="0,0,0,1" BorderBrush="#87AFDA">
  27:                                  <Grid>
  28:                                      <Grid.ColumnDefinitions>
  29:                                          <ColumnDefinition />
  30:                                          <ColumnDefinition Width="20" />
  31:                                      </Grid.ColumnDefinitions>
  32:                                  
  33:                                      <ContentControl Grid.Column="0" Content="{TemplateBinding Header}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Foreground="#224499" FontWeight="Bold" FontFamily="Arial" FontSize="12" Margin="3,3,3,3" />
  34:                                      <HyperlinkButton x:Name="hlbClose" Grid.Column="1" Content="[X]" />
  35:                                  </Grid>
  36:                              </Border>
  37:                              <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}" />
  38:                          </Grid>
  39:                      </Border>
  40:                  </ControlTemplate>
  41:              </Setter.Value>
  42:          </Setter>
  43:      </Style>
  44:  </ResourceDictionary>

Step 2: Get a Handle to Template UI Elements

Once we have our control UI looking the way we want it, the next step is to get a handle to our HyperlinkButton and add a handler for its click event.  When the user clicks the HyperlinkButton, we want to raise our control’s Close event.  The trick is to override the OnApplyTemplate of our control and get our handle to the HyperlinkButton using the GetTemplateChild method.  The GetTemplateChild method accepts the id/name of the control we are looking for.  It traverses the visual tree and returns a DependencyObject if found.  Below I show how we can do this:


   1:  Public Class CustomContentControl
   2:      Inherits ContentControl
   3:   
   4:      Private _hlbClose As HyperlinkButton
   5:   
   6:      Public Shared ReadOnly HeaderProperty As DependencyProperty = DependencyProperty.Register("Header", GetType(UIElement), GetType(CustomContentControl), Nothing)
   7:      Public Event Close(ByVal sender As CustomContentControl)
   8:   
   9:      Public Property Header() As UIElement
  10:          Get
  11:              Return DirectCast(Me.GetValue(CustomContentControl.HeaderProperty), UIElement)
  12:          End Get
  13:          Set(ByVal value As UIElement)
  14:              Me.SetValue(CustomContentControl.HeaderProperty, value)
  15:          End Set
  16:      End Property
  17:   
  18:      Public Sub New()
  19:          MyBase.New()
  20:          Me.DefaultStyleKey = GetType(CustomContentControl)
  21:      End Sub
  22:   
  23:      Public Overrides Sub OnApplyTemplate()
  24:          MyBase.OnApplyTemplate()
  25:          _hlbClose = DirectCast(GetTemplateChild("hlbClose"), HyperlinkButton)
  26:          AddHandler _hlbClose.Click, AddressOf hlbClose_Click
  27:      End Sub
  28:   
  29:      Private Sub hlbClose_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
  30:          RaiseEvent Close(Me)
  31:      End Sub
  32:  End Class

Step 3: Raise Control Events

Now that we have acquired a handle to our HyperlinkButton and also have added a handler for its click event, all we have to do now is raise our control’s Close event when the HyperlinkButton is clicked.  The control consumers can handle the close event if they wish.


   1:      Private Sub hlbClose_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
   2:          RaiseEvent Close(Me)
   3:      End Sub

ContentControlExample_Soln.zip (627.32 kb)


Get Child, Parent, or Children Objects in Silverlight

June 17, 2009 18:10 by wjchristenson2

As a programmer, you may have situations where you need a handle to a control to set its property programmatically.  Pretty simple right?  Let’s add some complexity.  What if you are not guaranteed to know exactly where the control resides.  Is it in our Grid at child index 0 or is it in a StackPanel within the Grid?  What if we want to set the background of all Canvas objects on our page to Transparent?

The Silverlight API provides the VisualTreeHelper class to assist with the traversal of the visual object tree.  Silverlight has a logical and visual object tree.  The visual tree is a subset of the logical object tree and is only populated by objects that have rendering implications.  In a general sense, a collection class would not be included in the visual tree however the Grid you are using as your LayoutRoot of your page would be.  The VisualTreeHelper class has 3 methods to assist with the visual object tree: GetChild(), GetChildrenCount(), GetParent().  Their names are self-descriptive and what I’d like to do is create a few recursive helper methods/extensions to these methods that are a bit more powerful.


Helper Method #1: GetParentObject

This method returns the first parent of a given type and/or name of an object’s parent hierarchy.  For instance, let’s say you know that our StackPanel resides somewhere in the bowels of a Grid named LayoutRoot.  To get a handle to LayoutRoot we could do the following:


   1:  Dim g As Grid = GetParentObject(Of Grid)(myStackPanel, "LayoutRoot")

Helper Method #2: GetChildObject

This method returns the first child of a given type and/or name of an object’s child hierarchy.  So let’s take the inverse of the above.  We have a StackPanel named myStackPanel that resides somewhere beneath our Grid LayoutRoot.  The get the handle to myStackPanel we could do the following:


   1:  Dim sp As StackPanel = GetChildObject(Of StackPanel)(Me.LayoutRoot, "myStackPanel")


Helper Method #3: GetChildObjects

This method returns a list collection of all children of a given type and/or name of an object’s child hierarchy.  So let’s say we want all rectangles which reside somewhere on our page.  Again, our page has a Grid named LayoutRoot.  We could get a list collection of all rectangles on the page by making the following call:


   1:  Dim rectangles As List(Of Rectangle) = GetChildObjects(Of Rectangle)(Me.LayoutRoot)


Helper Methods

Below is the actual methods’ code.  I’m also attaching my zipped test solution.


   1:  Module Common
   2:      ''' <summary>
   3:      ''' Navigates up the object's parent hierarchy and returns the first parent match of the specified type and/or object name.
   4:      ''' </summary>
   5:      ''' <typeparam name="T"></typeparam>
   6:      ''' <param name="obj"></param>
   7:      ''' <param name="name"></param>
   8:      ''' <returns></returns>
   9:      ''' <remarks></remarks>
  10:      Public Function GetParentObject(Of T As FrameworkElement)(ByVal obj As DependencyObject, Optional ByVal name As String = "") As T
  11:          Dim parent As DependencyObject = VisualTreeHelper.GetParent(obj)
  12:   
  13:          While parent IsNot Nothing
  14:              If TypeOf parent Is T AndAlso (CType(parent, T).Name = name Or String.IsNullOrEmpty(name)) Then
  15:                  Return CType(parent, T)
  16:              End If
  17:   
  18:              parent = VisualTreeHelper.GetParent(parent)
  19:          End While
  20:   
  21:          Return Nothing
  22:      End Function
  23:   
  24:      ''' <summary>
  25:      ''' Recursively searches an object's child hierarchy and returns the first child match of the specified type and/or object name.
  26:      ''' </summary>
  27:      ''' <typeparam name="T"></typeparam>
  28:      ''' <param name="obj"></param>
  29:      ''' <param name="name"></param>
  30:      ''' <returns></returns>
  31:      ''' <remarks></remarks>
  32:      Public Function GetChildObject(Of T As FrameworkElement)(ByVal obj As DependencyObject, Optional ByVal name As String = "") As T
  33:          Dim child As DependencyObject = Nothing, grandChild As T = Nothing
  34:   
  35:          For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
  36:              child = VisualTreeHelper.GetChild(obj, i)
  37:   
  38:              If TypeOf child Is T AndAlso (CType(child, T).Name = name Or String.IsNullOrEmpty(name)) Then
  39:                  Return CType(child, T)
  40:              Else
  41:                  grandChild = GetChildObject(Of T)(child, name)
  42:                  If grandChild IsNot Nothing Then Return grandChild
  43:              End If
  44:          Next
  45:   
  46:          Return Nothing
  47:      End Function
  48:   
  49:      ''' <summary>
  50:      ''' Recursively searches an object's child hierarchy and returns the all children that are of the specified type and/or object name.
  51:      ''' </summary>
  52:      ''' <typeparam name="T"></typeparam>
  53:      ''' <param name="obj"></param>
  54:      ''' <param name="name"></param>
  55:      ''' <returns></returns>
  56:      ''' <remarks></remarks>
  57:      Public Function GetChildObjects(Of T As FrameworkElement)(ByVal obj As DependencyObject, Optional ByVal name As String = "") As List(Of T)
  58:          Dim child As DependencyObject = Nothing, childList As List(Of T) = New List(Of T)
  59:   
  60:          For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
  61:              child = VisualTreeHelper.GetChild(obj, i)
  62:   
  63:              If TypeOf child Is T AndAlso (CType(child, T).Name = name Or String.IsNullOrEmpty(name)) Then
  64:                  childList.Add(CType(child, T))
  65:              End If
  66:   
  67:              childList.AddRange(GetChildObjects(Of T)(child))
  68:          Next
  69:   
  70:          Return childList
  71:      End Function
  72:  End Module

VisualTreeHelper_Soln.zip (589.38 kb)


Set an Enum Dependency Property with XAML

April 23, 2009 09: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.


   1:  Imports System.ComponentModel
   2:   
   3:  Public Class VehicleTypeConverter
   4:      Inherits TypeConverter
   5:   
   6:      Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
   7:          If sourceType.Equals(GetType(String)) Then
   8:              Return True
   9:          Else
  10:              Return MyBase.CanConvertFrom(context, sourceType)
  11:          End If
  12:      End Function
  13:   
  14:      Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
  15:          If destinationType.Equals(GetType(String)) Then
  16:              Return True
  17:          Else
  18:              Return MyBase.CanConvertTo(context, destinationType)
  19:          End If
  20:      End Function
  21:   
  22:      Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
  23:          If TypeOf value Is String Then
  24:              Try
  25:                  Return CType([Enum].Parse(GetType(Vehicle.VehicleType), value, True), Vehicle.VehicleType)
  26:              Catch
  27:                  Throw New InvalidCastException(value)
  28:              End Try
  29:          Else
  30:              Return MyBase.ConvertFrom(context, culture, value)
  31:          End If
  32:      End Function
  33:   
  34:      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
  35:          If destinationType.Equals(GetType(String)) Then
  36:              Return value.ToString()
  37:          Else
  38:              Return MyBase.ConvertTo(context, culture, value, destinationType)
  39:          End If
  40:      End Function
  41:  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.


   1:  Imports System.ComponentModel
   2:   
   3:  Partial Public Class Vehicle
   4:      Inherits UserControl
   5:   
   6:      Public Enum VehicleType
   7:          NA = 0
   8:          Car = 1
   9:          Bus = 2
  10:          Truck = 3
  11:      End Enum
  12:   
  13:      Public Shared ReadOnly TypeProperty As DependencyProperty = DependencyProperty.Register("Type", GetType(VehicleType), GetType(Vehicle), New PropertyMetadata(VehicleType.NA, AddressOf TypeChangedHandler))
  14:   
  15:      <TypeConverter(GetType(VehicleTypeConverter))> _
  16:      Public Property Type() As VehicleType
  17:          Get
  18:              Return DirectCast(GetValue(Vehicle.TypeProperty), VehicleType)
  19:          End Get
  20:          Set(ByVal value As VehicleType)
  21:              SetValue(Vehicle.TypeProperty, value)
  22:          End Set
  23:      End Property
  24:   
  25:      Public Sub New()
  26:          InitializeComponent()
  27:      End Sub
  28:   
  29:      Private Shared Sub TypeChangedHandler(ByVal o As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
  30:          DirectCast(o, Vehicle).OnTypeChanged(DirectCast(args.NewValue, VehicleType))
  31:      End Sub
  32:   
  33:      Private Sub OnTypeChanged(ByVal newValue As VehicleType)
  34:          Me.tbkTest.Text = "The vehicle type is a " & newValue.ToString() & "."
  35:      End Sub
  36:   
  37:  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)


Silverlight Custom Content Control

March 30, 2009 07: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:


   1:  Public Class CustomContentControl
   2:      Inherits ContentControl
   3:   
   4:      Public Shared ReadOnly HeaderProperty As DependencyProperty = DependencyProperty.Register("Header", GetType(UIElement), GetType(CustomContentControl), Nothing)
   5:   
   6:      Public Property Header() As UIElement
   7:          Get
   8:              Return DirectCast(Me.GetValue(CustomContentControl.HeaderProperty), UIElement)
   9:          End Get
  10:          Set(ByVal value As UIElement)
  11:              Me.SetValue(CustomContentControl.HeaderProperty, value)
  12:          End Set
  13:      End Property
  14:   
  15:      Public Sub New()
  16:          MyBase.New()
  17:          Me.DefaultStyleKey = GetType(CustomContentControl)
  18:      End Sub
  19:   
  20:  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:


   1:  <ResourceDictionary 
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:local="clr-namespace:ContentControlExample.Controls">
   5:   
   6:      <!-- CustomContentControl -->
   7:      <Style TargetType="local:CustomContentControl">
   8:          <Setter Property="Background" Value="Transparent" />
   9:          <Setter Property="Foreground" Value="Black" />
  10:          <Setter Property="BorderBrush" Value="Transparent" />
  11:          <Setter Property="BorderThickness" Value="0" />
  12:          <Setter Property="HorizontalAlignment" Value="Stretch" />
  13:          <Setter Property="VerticalAlignment" Value="Stretch" />
  14:          <Setter Property="HorizontalContentAlignment" Value="Left" />
  15:          <Setter Property="VerticalContentAlignment" Value="Top" />
  16:          <Setter Property="Template">
  17:              <Setter.Value>
  18:                  <ControlTemplate TargetType="local:CustomContentControl">
  19:                      <Border Background="White" BorderBrush="#87AFDA" BorderThickness="1">
  20:                          <Grid>
  21:                              <Grid.RowDefinitions>
  22:                                  <RowDefinition Height="Auto" />
  23:                                  <RowDefinition Height="*" />
  24:                              </Grid.RowDefinitions>
  25:                              
  26:                              <Border Grid.Column="0" Grid.Row="0" Background="#D4E6FC" BorderThickness="0,0,0,1" BorderBrush="#87AFDA">
  27:                                  <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" />
  28:                              </Border>
  29:                              <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}" />
  30:                          </Grid>
  31:                      </Border>
  32:                  </ControlTemplate>
  33:              </Setter.Value>
  34:          </Setter>
  35:      </Style>
  36:  </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.


   1:  <UserControl x:Class="ContentControlExample.Page"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:      xmlns:ex="clr-namespace:ContentControlExample.Controls;assembly=ContentControlExample.Controls"
   5:      Width="300" Height="200">
   6:      <Grid x:Name="LayoutRoot" Background="White">
   7:          <ex:CustomContentControl x:Name="myCustomContentControl">
   8:              <ex:CustomContentControl.Header>
   9:                  <TextBlock Text="This is the Header" />
  10:              </ex:CustomContentControl.Header>
  11:              <ex:CustomContentControl.Content>
  12:                  <StackPanel Orientation="Vertical" Margin="5">
  13:                      <TextBlock Text="This is the body or content." Margin="0,0,0,5" />
  14:                      <Button Content="Click Me!" Width="100" Height="30" />
  15:                  </StackPanel>
  16:              </ex:CustomContentControl.Content>
  17:          </ex:CustomContentControl>
  18:      </Grid>
  19:  </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)


Silverlight 3 Beta Released

March 18, 2009 12: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).


Silverlight DataGrid ItemsSource Filter

February 19, 2009 13: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:


   1:  <UserControl x:Class="FilterCollection.Page"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:      xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" 
   5:      Width="400" Height="250">
   6:      <Grid x:Name="LayoutRoot" Background="White">
   7:          <Grid.RowDefinitions>
   8:              <RowDefinition Height="25" />
   9:              <RowDefinition />
  10:          </Grid.RowDefinitions>
  11:          <Grid.ColumnDefinitions>
  12:              <ColumnDefinition />
  13:              <ColumnDefinition Width="75" />
  14:              <ColumnDefinition Width="75" />
  15:          </Grid.ColumnDefinitions>
  16:          
  17:          <ComboBox x:Name="cbxAge" Grid.Column="0" Grid.Row="0" Height="25"  />
  18:          <Button x:Name="btnFilter" Content="Filter" Grid.Column="1" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
  19:          <Button x:Name="btnClear" Content="Clear" Grid.Column="2" Grid.Row="0" Height="25" Width="70" Margin="5,0,0,0" />
  20:          <data:DataGrid x:Name="dgResults" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" AutoGenerateColumns="True" Margin="0,5,0,0" />
  21:      </Grid>
  22:  </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:


   1:  Public Class Person
   2:      Private _Name As String
   3:      Private _Age As Integer
   4:   
   5:      Public Property Name() As String
   6:          Get
   7:              Return _Name
   8:          End Get
   9:          Set(ByVal value As String)
  10:              _Name = value
  11:          End Set
  12:      End Property
  13:   
  14:      Public Property Age() As Integer
  15:          Get
  16:              Return _Age
  17:          End Get
  18:          Set(ByVal value As Integer)
  19:              _Age = value
  20:          End Set
  21:      End Property
  22:   
  23:      Public Sub New(ByVal name As String, ByVal age As Integer)
  24:          _Name = name
  25:          _Age = age
  26:      End Sub
  27:   
  28:  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.


   1:          'initialize our collection of person(s)
   2:          _Persons = New List(Of Person)
   3:          _Persons.Add(New Person("Jake", 29))
   4:          _Persons.Add(New Person("Same", 28))
   5:          _Persons.Add(New Person("Beth", 29))
   6:          _Persons.Add(New Person("Sue", 13))
   7:          _Persons.Add(New Person("Tina", 12))
   8:          _Persons.Add(New Person("Pete", 13))
   9:          _Persons.Add(New Person("Andy", 10))
  10:          _Persons.Add(New Person("Ashley", 11))
  11:          _Persons.Add(New Person("Sydney", 10))
  12:   
  13:          'initialize our combobox of ages
  14:          Dim ages As List(Of Integer) = New List(Of Integer)
  15:          For i As Integer = 1 To 30 Step 1
  16:              ages.Add(i)
  17:          Next
  18:          Me.cbxAge.ItemsSource = ages
  19:          Me.cbxAge.SelectedIndex = 0
  20:   
  21:          'initialize our DataGrid to our collection of person(s)
  22:          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:


   1:          'acquire the age to filter on
   2:          Dim age As Integer = DirectCast(Me.cbxAge.SelectedItem, Integer)
   3:   
   4:          'convert our Persons collection to an IEnumerable(Of T) where T (type) = Person
   5:          Dim source As IEnumerable(Of Person) = DirectCast(_Persons, IEnumerable(Of Person))
   6:   
   7:          'filter the collection using the "Where" extension method
   8:          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.


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

FilterCollection_Soln.zip (1.02 mb)