Setting DateTime DependencyProperty in XAML

January 9, 2009 05:03 by wjchristenson2

Sometimes when setting properties with XAML you may get the AG_E_PARSER_BAD_PROPERTY_VALUE error.  This can mean a variety of things, but in this post we are going to visit a situation where .NET cannot convert the XAML string to the .NET object's type.

A good example of this is a Date property.  Let's say we have a control that has a MaxDate property.  If we set the property in XAML it may look like this: MaxDate="01-01-2009".  Unfortunately this does not work.  You'll get our fatal parser error because the parser is not smart enough to parse the string to a date type.

The workaround is to hold .NET parsers hand and tell it how to parse the string to our desired type (in this case a Date).  We'll do this by first creating our own TypeConverter.  I'll call it the DateTypeConverter.  We'll inherit from the TypeConverter base class and override key methods used to detect if we can convert from/to a type and handle the actual conversion process.  Here's our TypeConverter class for converting from and to a Date type.

Imports System.ComponentModel

Public Class DateTypeConverter
    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 Date.Parse(value.ToString())
            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

Now that we have our custom converter created, we need to tell our control's property to use this converter when being set via a property attribute.  In our case, when the parser attempts to set the property, it will invoke our DateTypeConverter and parse the string to a date properly.

Public Shared ReadOnly MaxDateProperty As DependencyProperty = DependencyProperty.Register("MaxDate", GetType(Date), GetType(SilverlightControl1), New PropertyMetadata(Date.MaxValue, AddressOf MaxDateChangedHandler))

<TypeConverter(GetType(DateTypeConverter))> _
      Public Property MaxDate() As Date
    Get
        Return DirectCast(GetValue(SilverlightControl1.MaxDateProperty), Date)
    End Get
    Set(ByVal value As Date)
        SetValue(SilverlightControl1.MaxDateProperty, value)
    End Set
End Property

Take note of line 3.  Adding the TypeConverter attribute tells the .NET XAML parser to use our DateTypeConverter to parse the String to Date.

SiverlightDateTime_Soln.zip (593.57 kb)

Bookmark and Share

Silverlight Global Properties

January 8, 2009 08:16 by wjchristenson2

It took me awhile to figure out the best way to create objects that can be used application-wide in Silverlight.  I'll quickly show you how easy it is to do so.

Let's say we want a global property that stores when the Silverlight application was first started.  We will acquire the date/time on application startup and store its value in our application object (App.xaml code behind) via a property.  Here's the code to do so in my App.xaml.vb file:

Private _startDateTime As Date

Public ReadOnly Property StartDateTime() As Date
    Get
        Return _startDateTime
    End Get
End Property

Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
    _startDateTime = Date.Now
    Me.RootVisual = New Page()
End Sub


When my RootVisual (page) loads, I'll get a handle on the current application object and set a TextBlock's text to the applications startup time which is stored in our application's property we defined.  Here's the code to do so:

Private Sub Page_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    Me.tbkDate.Text = DirectCast(Application.Current, App).StartDateTime.ToShortDateString() & " " & DirectCast(Application.Current, App).StartDateTime.ToShortTimeString()
End Sub

In line 2, we get a handle on the current application object by casting "Application.Current" to our "App" type.  Once we have this, we can access our newly created property and set our TextBlock's text property to it.

SilverlightGlobal_Soln.zip (583.88 kb)

Bookmark and Share

Silverlight Performance Tips

November 19, 2008 04:30 by wjchristenson2

1)  Do set IsWindowless=False.  There is a high performance price in rendering windowless controls.  However I've found that if you do this, you might run into some problems.  First, if you want your background of your SL application to be transparent, then you must set IsWindowless to True.  Also, if you have HTML overlays (modals) over your SL application, you'll also need to set IsWindowless = True.

2)  Do NOT set the Silverlight's HTML control background property to transparent or any variation thereof (make it opaque).   If the background property is set to such, each render call will go through a blending sequence which adds to a higher CPU cost.

3)  Do change the Silverlight's application default MaxFrameRate.  The default value is 60.  Most SL applications will look/run fine anywhere between 15 to 30.  You can change the MaxFrameRate programmatically or simply markup the Silverlight HTML control.

4)  Do NOT do text size animations.  When you animate the size of text in SL, it uses hinting to smooth each text glyph.  When animating text size, SL may drop frames due to this.  If you can, use a vector graphic to represent large text animations.

5)  Do use Visibility instead of Opacity whenever possible.  Even if an object's opacity is set to 0, SL will still account for it and its still technically rendered.  Setting the object's visibility to Collapsed will cause SL to ignore rendering the object.

6)  When using the MediaElement object, do not specify its Width and Height.  Let SL render the object at its natural size.

7)  Do not set the Width and Height on path objects.  Rely on the points defined for the path.

8)  When displaying a double's value, do use Double.ToString(CultureInfo.InvariantCulture) rather than Double.ToString().  This will alleviate the need for SL to acquire the culture setting before displaying the double and CurltureInfo.InvariantCulture is optimized for perormance.

9)  If your application is very large, consider loading pieces of it "on demand".

Bookmark and Share

Get ActualWidth and ActualHeight in Silverlight

October 17, 2008 07:27 by wjchristenson2

You may often encounter situations where you need to acquire the size of a Silverlight object and the Width, Height, ActualWidth, ActualHeight property values are not set.  This can occur if the Width & Height are set to Auto, if the object being referenced is in the process of being added dynamically, or it can occur in a myriad of other circumstances.

Let's assume that we have a Canvas object as our root element for our Silverlight application.  It's Width and Height are set to Auto.  As its parent is resized, the Canvas object will grow & shrink accordingly.  Now if we want to acquire the Canvas size to do some placement logic of Canvas child elements, we run into our problem.

My first approach was to investigate why ActualWidth and ActualHeight were not telling me (the developer) the ... uh... actual width and actual height.  You'd think the property names would say it all, but they don't.  They actually "get or set the rendered width/height of a FrameworkElement."  Ok, that's easy enough.  We'll just wait until the objects are rendered before we position them.  So, I figured I'd hook into my Silverlight's Loaded event.  At this point all objects should be "loaded" and I can acquire the actual size of my Canvas.  Wrong!  ActualWidth and ActualHeight are calculated based on the Width/Height property values and the layout system.  There is no guarantee as to when these values will be "calculated".  So I've found 2 ways to workaround this issue.

Dispatcher.BeginInvoke()
This approach executes a specified delegate asynchronously on the thread the Dispatcher is associated with.  Measurement and layout passes run in Silverlight asynchronously.  Therefore our ActualWidth and ActualHeight object properties can be set AFTER we actually need them.  Using BeginInvoke() ensures that our ActualWidth and ActualHeight calculations are done as we programmatically access them.  Here is a sample use of .BeginInvoke() and the delegate.

'ActualWidth and ActualHeight are calculated values and may not be set yet
'therefore, execute GetLayoutRootActualSize() asynchronously on the thread the Dispatcher is associated with
Me.Dispatcher.BeginInvoke(AddressOf GetLayoutRootActualSize)

Private Sub GetLayoutRootActualSize()
    Me.tbxInvoke.Text = Me.LayoutRoot.ActualWidth.ToString() & ", " & Me.LayoutRoot.ActualHeight.ToString()
End Sub

_SizeChanged Event
My preferred method of accessing ActualWidth and ActualHeight in these situations is by hooking into the FrameworkElement's SizeChanged event.  This event is fired when the ActualWidth and ActualHeight values change for a FrameworkElement.  So let's say we have an element which we need to position based on the size of it's parent.  We can hook into the parent's SizeChanged event and position the child element on the fly.  The ActualWidth and ActualHeight properties of the parent would be present at this time.  Here is a snipped of SizeChanged event handling.

Private Sub LayoutRoot_SizeChanged(ByVal sender As Object, ByVal e As System.Windows.SizeChangedEventArgs) Handles LayoutRoot.SizeChanged
    Me.tbxSizeChanged.Text = Me.LayoutRoot.ActualWidth.ToString() & ", " & Me.LayoutRoot.ActualHeight.ToString()
End Sub

I've uploaded an example using my 3 tests (page Loaded, BeginInvoke(), and _SizeChanged).  Hope it helps!


ActualWidthHeight_Soln.zip (586.38 kb)

Bookmark and Share

Microsoft Releases Silverlight 2

October 14, 2008 01:25 by wjchristenson2

Microsoft has released Silverlight 2 today.  Here are the downloads that I found useful:

Silverlight Tools for Visual Studio 2008 SP1
This allows you to develop Silverlight 2 applications within Visual Studio 2008 SP1.  Make sure you have SP1 installed first on Visual Studio before you attempt to install the tools.

Expression Blend 2 Service Pack 1
The service pack will allow you to develop Silverlight 2 with Expression Blend 2.

Microsoft® Silverlight™ 2 SDK
The Silverlight Tools for VS 2008 SP1 already includes the SL2 SDK.  However, if you are not installing that, you can install this SDK seperately.  The software development kit provides documentation, libraries and tools for developing Silverlight 2 applications.

Microsoft Releases Silverlight 2, Already Reaching One in Four Consumers Worldwide
This is the official press announcement.

I have not found any differences yet between SL2 and SL2RC0.  I'll post if I find any.

Bookmark and Share