Localization Revisited
Among the first things I covered in this blog was Localization. After I had finished those posts, I felt like I had covered everything one would need to write fully globalized applications. However, meanwhile I found out how to do some things better, and I’d like to share that here.
Making Localization Work at Design Time
One of the drawbacks of the original WPFGlue localization components was that they didn’t work nicely at design time. The reason was that I hadn’t found out how to automatically locate an application’s resources when the application was hosted inside the VS XAML designer.
The key here is a property on the Application class named ResourceAssembly. When the VS designer hosts an assembly, it will put a reference to it into this property, so the MarkupExtensions can locate the assembly that contains the current project’s resources.
Also, I revised the detection for the resource basename. The basename contains the project’s root namespace. One can find this as the namespace of the project’s Application class, which will normally be the project’s only class derived of System.Windows.Application.
I encapsulated this all into a new base class for the localization markup extensions:
Public MustInherit Class ResourceExtension
Inherits System.Windows.Markup.MarkupExtension
Public Sub New()
MyBase.New()
End Sub
Public Sub New(ByVal key As String)
MyBase.New()
Me.Key = key
End Sub
Private _Key As String
Public Property Key() As String
Get
If String.IsNullOrEmpty(_Key) Then
Return "No Key"
Else
Return _Key
End If
End Get
Set(ByVal value As String)
_Key = value
End Set
End Property
Private Shared _BaseName As String
Public Shared Property BaseName() As String
Get
If _BaseName Is Nothing And Assembly IsNot Nothing Then
Dim applicationType As Type = (Aggregate t As Type In Assembly.GetTypes() Where t.IsSubclassOf(GetType(System.Windows.Application)) Into First())
If applicationType IsNot Nothing Then
_BaseName = applicationType.Namespace & ".Resources"
End If
End If
Return _BaseName
End Get
Set(ByVal value As String)
_BaseName = value
_ResourceManager = Nothing
End Set
End Property
Private Shared _Assembly As System.Reflection.Assembly
Public Shared Property Assembly() As System.Reflection.Assembly
Get
If _Assembly Is Nothing Then
_Assembly = System.Reflection.Assembly.GetEntryAssembly
End If
If _Assembly Is Nothing Then
_Assembly = Application.ResourceAssembly
End If
Return _Assembly
End Get
Set(ByVal value As System.Reflection.Assembly)
_Assembly = value
_ResourceManager = Nothing
End Set
End Property
Private Shared _ResourceManager As System.Resources.ResourceManager
Public Shared Property ResourceManager() As System.Resources.ResourceManager
Get
If _ResourceManager Is Nothing Then
If Assembly IsNot Nothing And BaseName IsNot Nothing Then
_ResourceManager = New System.Resources.ResourceManager(BaseName, Assembly)
End If
End If
Return _ResourceManager
End Get
Set(ByVal value As System.Resources.ResourceManager)
_ResourceManager = value
End Set
End Property
End Class
End Namespace
Notice the fallback detection for the resource assembly: the first location is the application domain’s entry assembly. If the assembly isn’t running it’s own domain (because it’s hosted in the designer), the next location is the ResourceAssembly. As before, one can override the resource assembly by setting the Localization.ResourceAssembly attached property.
By the way, in the post "The Last Word on Localized Resources" I suggested using a localized URL in order to use different localized ResourceDictionaries in ones XAML files. I don’t recommend this approach any longer. While it works at run time, the designer experience is bad: one will get squiggly lines telling that the resources can’t be found, and one cannot see the values of the resources in the designer panel. With the new markup extensions based on the above class, one will have instant feedback.
Funny enough, VS will use the set of resources that match its own language version. So, running US-English Microsoft Visual Basic 2010 Express on German Windows, I see the English localization in the designer and the German localization when I start the debugger.
How to Use Bitmap Resources
Another thing I learned is how to convert from System.Drawing.Bitmap, which is what one gets from the ResourceManager when one accesses an image resource, to System.Windows.Media.Imaging.BitmapSource, which is what the WPF Image control expects:
Dim result As BitmapSource = NoImage
If ResourceManager IsNot Nothing Then
Dim data As System.Drawing.Bitmap = TryCast(ResourceManager.GetObject(Key), System.Drawing.Bitmap)
If data IsNot Nothing Then
Dim sourceRect = New Int32Rect(0, 0, data.Width, data.Height)
result = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(data.GetHbitmap, Nothing, sourceRect, BitmapSizeOptions.FromEmptyOptions)
End If
End If
Return result
End Function
The conversion happens through a shared bitmap handle, so I assume it’s not too heavy on memory consumption. As a nice side effect, one can now use PNG images without them being unaccountably scaled to 133% their pixel size. Also, the images will now instantly be visible in the designer.
How to Create Alternative Resource Files
In my first post about localized resources, I described a kind of complicated procedure for creating resource files for alternative languages. A somewhat simplified procedure, which goes without closing the IDE, is as follows:
- Complete your application, creating resources in the My Project / Resources designer as you go.
- When done, activate the “Show All Files” icon in Solution Explorer.
- Locate the Resources.resx file in the My Project folder and create a copy of it. You can do this using the context menu in Solution Explorer. The copy can be located anywhere in your project directory. Probably you should keep it outside the My Project folder.
- Rename the copy to reflect the intended language and culture, e.g. to Resources.de.resx for general German, or Resources.ar-SA.resx for Arabic (Saudi Arabia). At that stage, you can deactivate the “Show All Files” switch again.
- Double click the file in order to open it in the resource designer.
- Make sure that the Access Modifier is set to “No Code Generation”.
- Replace the original resource strings and icons with the appropriate translations.
- Make sure that the Neutral Language in My Project / Application / Assembly Information is set to the language you used for the original version of the resources.
Conclusion
While support for .Net resources is completely missing from WPF, one can create an experience that feels equally nice at design time and at run time by using some simple custom markup extensions.
Commanding: Binding Controls to Methods
A Command in WPF encapsulates a method to do something, a check on whether it is possible to do it at the moment , and a notification of when that state changes. It does this in a way so that one can bind a control like a Button or MenuItem to a command, and the control will execute the command when clicked and enable and disable itself according to whether the command is available.
WPF supports this infrastructure through two APIs: CommandBindings, which can be defined in XAML, but can only call methods defined in the XAML’s code-behind file, and the ICommand-Interface, which can be implemented in ViewModel classes, and supports creating properties in the ViewModel which allow to bind a control to a command.
For the first approach, I don’t like that I have to write code in the code-behind file. Defining a command is a routine thing, so I’d like to do it in XAML.
For the second approach, I don’t like that I have to implement an interface, and create kind of standard boilerplate glue-code in order to be able to make the UI call a method in the model. If possible, I want to do all the gluing that is necessary in XAML only.
What would an ideal solution look like? I’d like to have a way to just take any normal method in the ViewModel, bind a command to it (that would be a RoutedCommand object, like with the CommandBinding approach), and assign the RoutedCommand to any Control that supports commands.
In the post about the Sticky Command Design Pattern, I have already demonstrated how one can set up CommandBindings through attached properties. Now, I want to generalize this approach into a commanding framework.
A Reusable Sticky Command
The first element is a reusable component which ties a RoutedCommand and its implementation together. Basically, it contains the same information as a CommandBinding, and in fact it is just a wrapper and adapter which is responsible for creating CommandBindings while overcoming their limitations.
So, on the one hand, the Command class defines the usual Attach and Detach methods for Sticky Components:
If SupportsCanExecute Or UseValidation Then
StickyComponentManager.Attach(sender, Command, AddressOf OnExecuted, AddressOf OnCanExecute, Preview)
Else
StickyComponentManager.Attach(sender, Command, AddressOf OnExecuted, Nothing, Preview)
End If
End Sub
Protected Overrides Sub Detach(ByVal sender As Object)
StickyComponentManager.Detach(sender, Command)
End Sub
Command here is a property which holds a RoutedCommand and which can be configured through XAML. UseValidation and Preview are also properties which can be configured through XAML.
On the other hand, the Command class defines standard implementations for the CommandBinding’s Executed and CanExecute delegates:
End Sub
Protected Overridable ReadOnly Property SupportsCanExecute() As Boolean
Get
Return False
End Get
End Property
Public Overridable Function CanExecute(ByVal sender As Object, ByVal parameter As Object) As Boolean
Return CanExecute(parameter)
End Function
Public Overridable Function CanExecute(ByVal parameter) As Boolean
Return True
End Function
Private Sub OnExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
Exception = Nothing
Status = WorkingMessage
Dim a As Func(Of Object, Object, Exception) = AddressOf Me.DoExecute
Dim op As System.Windows.Threading.DispatcherOperation = Me.Dispatcher.BeginInvoke(a, Windows.Threading.DispatcherPriority.Background, sender, e.Parameter)
op.Wait()
Dim success As Boolean = (op.Result Is Nothing)
If success Then
Status = ReadyMessage
Else
Dim ex As Exception = op.Result
Status = ex.Message
Exception = ex
End If
End Sub
Private Function DoExecute(ByVal sender As Object, ByVal parameter As Object) As Exception
Dim result As Exception = Nothing
Try
Execute(sender, parameter)
Catch ex As Exception
result = ex
End Try
Return result
End Function
Private Sub OnCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
Try
e.CanExecute = True
If SupportsCanExecute Then
e.CanExecute = CanExecute(sender, e.Parameter)
End If
If e.CanExecute And UseValidation Then
e.CanExecute = Not System.Windows.Controls.Validation.GetHasError(sender)
End If
e.Handled = Handled
Catch
e.Handled = False
End Try
End Sub
This code contains some details which I am going to cover in later posts. The important thing for now is: there are standard event handlers which can be attached to a CommandBinding, and their real work is done in the overridable Execute and CanExecute methods. These methods are supposed to be implemented by derived classes.
Binding to Methods
So far, we have an object which can create a CommandBinding by attaching itself to a Control. The next step is to bind a method of our ViewModel to this object.
For this purpose, I created a subclass of Command, ActionCommand, which defines a property of type Action, i.e. a Delegate for a method that doesn’t take parameters and doesn’t return a result.
Public Property ExecutedAction As Action
Get
Return GetValue(ExecutedActionProperty)
End Get
Set(ByVal value As Action)
SetValue(ExecutedActionProperty, value)
End Set
End Property
Public Overrides Sub Execute(ByVal sender As Object, ByVal parameter As Object)
If ExecutedAction IsNot Nothing Then
ExecutedAction.Invoke()
End If
End Sub
The class overrides the Execute method with an implementation that invokes the method the ExecutedAction delegate is bound to.
In order to be able to bind a value to the ExecutedAction delegate, I created the property as a DependencyProperty. Also, I derived the base class for my Command from Freezable. Freezables are special objects that are optimized for use as resources. What makes them extremely useful in our case is that they inherit the DataContext of the FrameworkElement in whose Resources section they are defined. The DataContext will be our ViewModel object. So, the only thing lacking is a way to create a Delegate for one of its methods through a Binding.
The solution here is a converter. The special converter we use in this case will accept the name of the method as ConverterParameter. Then it will inspect the type of the bound property and the type of the bound object in order to create a Delegate with a matching signature. This is its code:
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If value Is Nothing OrElse parameter Is Nothing OrElse Not TypeOf parameter Is String OrElse Not GetType([Delegate]).IsAssignableFrom(targetType) Then
Return Nothing
Else
Return WPFGlue.Framework.StickyComponentManager.CreateActionDelegate(value, parameter, targetType)
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New NotSupportedException()
End Function
End Class
And the method which creates the Delegate:
Dim result As [Delegate] = Nothing
If target IsNot Nothing AndAlso Not String.IsNullOrEmpty(methodName) AndAlso GetType([Delegate]).IsAssignableFrom(actionType) Then
Dim targetType As System.Type = target.GetType
Dim methodInfo As System.Reflection.MethodInfo
methodInfo = targetType.GetMethod(methodName, actionType.GetGenericArguments)
If methodInfo IsNot Nothing Then
result = [Delegate].CreateDelegate(actionType, target, methodInfo, False)
End If
End If
Return result
End Function
Gluing it Together in XAML
While this looks quite complex, the good news is that one needs to create this kind of class only once, and from then on can use it to bind to method in XAML only. This is how one would define a Command which binds to a method in the ViewModel:
In this case, the we use the predefined Open RoutedCommand, and bind it to a method called Open (as specified by the ConverterParameter in the Binding) on the current DataContext. Instead of the predefined Open command, we could use any RoutedCommand, even those defined in our own application.
We connect the command to a Control using a Sticky Property which the commanding framework defines:
<Button Command="Open">Open</Button>
As you can see, the Button which triggers the command is completely unaware of its implementation: it just knows that it should work with the Open predefined command, and the rest works through the WPF RoutedCommand infrastructure. This means that you can also take advantage of command routing, like having a MenuItem triggering the same command on different controls depending on which control has keyboard focus. This is more than one gets using the traditional MVVM RelayCommand approach.
Conclusion
Even though it is a bit of work, it is possible to create reusable components which allow it to bind a RoutedCommand to a method of a ViewModel using XAML only. With this technique, it is possible to take advantage of WPF command routing, while at the same time the amount of glue code in the ViewModel is reduced.
You can find the full code and an example project, CommandingExample, in the WPFGluePublished download through the Downloads page.
In posts to come I will cover more details of the WPFGlue Commanding framework, like executing commands on background threads, displaying status and exception messages, or defining CommandSets in order to attach several commands in one line of XAML.
Checking Property Types Automatically
It’s been a long time since I have written in this blog. The reason is: there have been many changes in my life during the past two years, and I simply haven’t found the time until now. Anyway, I’ve been checking out things and learning continuously, and I hope to be able to share these things more regularly in the future.
In the last post, Validating Objects with ValidationRules, I proposed a way to define ValidationRules for an object in one location and to set them on all Bindings that need them through a single attached property.
Today, I want to show how to set up basic type checks automatically. The example code is contained in the ValidationExample project in the source code distribution, which you can access through the downloads page.
How WPF handles Type
Normally, if the value entered in a TextBox cannot be converted into the type of the bound property, e.g. if you enter letters into a TextBox expecting an integer value, first of all there will be a runtime exception. By default, WPF will handle this exception, mark the TextBox as having invalid data, and assign a default “Conversion error” error message to it. Since the value cannot be converted, WPF will not update the bound property, which means that it will keep its value unchanged.
The first level of refinement here would be to allow the Binding to display exception messages by setting ValidatesOnExceptions to true. Now, if changing a property causes an exception, Validation.Errors will contain the exception’s message. Now the user will at least know whether the reason for the exception is a type mismatch or a numerical overflow.
Validation as a User Interface Responsibility
Why is this not enough?
Reusable software components like services or libraries need to check their input and throw exceptions if it is invalid. This is how one piece of software tells another piece of software that it has done something wrong.
User Interfaces, which basically translate between human users and the software they want to use, are responsible for making sure that those users only provide valid input values. The best way to do that is to offer only valid options to the user. Where that isn’t possible, validation steps in by providing explicit and meaningful validation messages which tell the users what they have to change in order to achieve the intended result.
As a rule of thumb, I’d say that one should protect one’s services and libraries by throwing exceptions, and that one should use validation in the UI to make sure that those exceptions never get thrown.
So, while exceptions are directed at technically aware developers debugging their software components, validation messages are directed at end users with possibly no technical background at all. For example, the error message you receive when you enter too many digits into the TextBox that validates with exceptions in the example project, reads: “Value was either too large or too small for an Int32”. This is obviously not what one would display to a user who has never heard of Int32.
Therefore, in the user interface, one would need two functionalities:
- a way to provide globalized validation messages independent of the system’s exception messages.
- a way to keep WPF controls from trying to update properties with values of non-matching type.
The TypeValidationRuleSetter Class
The TypeValidationRuleSetter class is a subclass of the ValidationRuleSetter class I mentioned in the last post. In addition to adding predefined ValidationRules to specified properties on a bound object, it will inspect the object’s type, find out the primitive CLR-type of a bound property, and will add default validation rules for each type which use XAML-configurable validation messages.
At the heart of the TypeValidationRuleSetter class is the following method:
Dim f As FrameworkElement = TryCast(base, FrameworkElement)
If f IsNot Nothing And ReferenceType IsNot Nothing Then
If f IsNot Nothing Then
Dim bg As BindingGroup = f.BindingGroup
If bg IsNot Nothing Then
For Each be As BindingExpression In bg.BindingExpressions.OfType(Of BindingExpression)()
Dim propertyName = be.ParentBinding.Path.Path
Dim propertyInfo As System.Reflection.PropertyInfo = ReferenceType.GetProperty(propertyName)
Select Case propertyInfo.PropertyType
Case GetType(DateTime)
be.ParentBinding.ValidationRules.Add(New DateValidationRule(Me.DateMessage))
Case GetType(Int16)
be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int16.MinValue, Int16.MaxValue, Me.IntegerMessage))
Case GetType(Int32)
be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int32.MinValue, Int32.MaxValue,Me.IntegerMessage))
Case GetType(Int64)
be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Int64.MinValue, Int64.MaxValue, Me.IntegerMessage))
Case GetType(Single)
be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Single.MinValue, Single.MaxValue,Me.NumericMessage))
Case GetType(Double)
be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Double.MinValue, Double.MaxValue, Me.NumericMessage))
Case GetType(Decimal)
be.ParentBinding.ValidationRules.Add(New NumericValidationRule(Decimal.MinValue, Decimal.MaxValue, Me.NumericMessage))
Case GetType(Integer)
be.ParentBinding.ValidationRules.Add(New IntegerValidationRule(Integer.MinValue, Integer.MaxValue, Me.IntegerMessage))
End Select
If Setters.Contains(propertyName) Then
For Each v As System.Windows.Controls.ValidationRule In Setters.Item(propertyName).ValidationRules
be.ParentBinding.ValidationRules.Add(v)
Next
End If
Next
End If
End If
End If
End Sub
This method will be called during the Initialized event, when Bindings have been established but still can be modified. It inspects the type of the property and adds one of a choice of default ValidationRules, which basically make a difference only between date, integer and other numerical values, providing different value ranges as feedback to the end user. So, the class translates from the technical, physical representation, which is determined by the CLR type of the property, to the logical limitations that type entails, which is what is relevant for the user.
So, although there are different checks for different allowed value ranges, there are only three configurable, i.e. localizable, validation messages:
Public Property IntegerMessage As String = "Must be an integer between {1} and {2}."
Public Property NumericMessage As String = "Must be a number between {1} and {2}."
The default ValidationRules use basic .Net functions in order to determine whether .Net can convert the value:
Dim result As Boolean = IsNumeric(value)
If result And MinValue.HasValue Then
result = CDbl(value) >= MinValue
End If
If result And MaxValue.HasValue Then
result = CDbl(value) <= MaxValue
End If
Return result
End Function
Finally, the allowed minimum and maximum values are formatted into the validation message, to give the users complete feedback on what they have to enter in order to make it a valid value:
Return String.Format(Me.Message, value, MinValue, MaxValue)
End Function
Conclusion
The TypeValidationRuleSetter class allows it to include meaningful and localizable validation messages for type checks with minimal effort. At the same time, it protects the application from exceptions which would otherwise be thrown because of type mismatches between what is entered into a TextBox and the type of the bound property.
Validating Objects with ValidationRules
In examples about writing WPF applications with the MVVM pattern, normally one doesn’t find any reference to ValidationRules. The normal way to do things there is to wrap the business object into the ViewModel and to implement IDataErrorInfo on the ViewModel. However, there are several points about this approach which I don’t like:
- I don’t like to wrap classes into other classes. The kind of code this produces is exactly the kind of useless boilerplate code I want to get rid of by using a flexible data binding system like WPF’s. If it can’t be helped, this kind of code should be generated, but that is not the topic of this blog.
- Validation on the ViewModel or Business level removes validation logic and error messages from the domain of the user interface definition to the domain of business logic. Now I like my business logic to be language independent, and it is absolutely valid for a user interface to implement different (more restrictive) checks on a property value than the business object it accesses. So, even if validation logic is implemented on the business level, I want to be able to choose how to use it and what error messages to display on the UI level, i.e. in XAML.
That said, ValidationRules have another significant advantage: they can check values before they are passed to the properties of the bound object. This means that I don’t have to implement rollback capabilities on the business object, but can make sure on the UI level that only valid values are set on the business object. This will be covered in a later post.
With all these advantages, there is a serious drawback to ValidationRules: defining them in XAML is rather unwieldy. Done the traditional way, they tend to bloat the XAML code, and sometimes it is difficult to get at all the reference data they need for their checks.
This is where WPFGlue comes into the picture: here, I want to introduce some ideas which allow to define and apply ValidationRules in a more concise manner, and to do some basic checks even without specifying ValidationRules explicitly.
The BindingGroup Class
The BindingGroup class has been introduced in WPF 3.5 SP1. A BindingGroup can be set on every FrameworkElement. If set, it can define ValidationRules which are able to access multiple properties of the DataContext in order to validate them against each other. In order to afford this possibility, the BindingGroup class provides methods which can access all Bindings in the subtree under the element which defines the BindingGroup, as long as these Bindings have the same DataContext as that element.
The idea here is to use this ability of the BindingGroup class in order to set up ValidationRules for all bindings which share a given DataContext.
So, in order to validate objects of a given class, first of all one would use a container like a StackPanel, Grid or GroupBox to combine a group of controls which share an instance of that class as their DataContext. Then, one would create a BindingGroup object and set it to the BindingGroup property of that container. Further, one would define a set of ValidationRules for a given class. Some of them would be at the property level, others would access multiple properties, as would be normal for ValidationRules on a BindingGroup object. Then, one would apply this set of ValidationRules to the container, instead of the separate controls which edit the object’s properties. One could reuse the set of ValidationRules wherever an instance of the DataContext class was used.
Using the BindingGroup’s ability for finding Bindings which have the same DataContext, one would add the ValidationRules to the appropriate Bindings inside the container.
How to Modify Bindings Which are Defined in XAML
The documentation says that Bindings can not be modified after they have been used. However, there is an event that comes right between the time when the Bindings have all been created, and when they are used for the first time: Initialized. If the DataContext is not null and a BindingGroup is present on a FrameworkElement, this BindingGroup can access and modify those Bindings during the FrameworkElement’s Initialized event.
So, one would need a couple of things:
- A way to define sets of ValidationRules.
- A Sticky Component which handles distributing the ValidationRules to the Bindings.
- A Sticky Property which attaches an instance of a set of ValidationRules to a container.
Defining Sets of ValidationRules
First of all, I said that I wanted to be able to define error messages in XAML. Using the techniques from the WPFGlue Localization namespace, I can handle multiple languages quite conveniently in XAML, and there might be other considerations like the intended audience of an application which might induce me to change the text of error messages. So, my first addition to a WPFGlue ValidationRule base class would be a Message property, which allows me to set the error message in XAML. In addition to that, I just added the functionality to format the property value into the error message.
Public MustInherit Class ValidationRule
Inherits System.Windows.Controls.ValidationRule
Private _Message As String = String.Empty
Public Property Message() As String
Get
Return _Message
End Get
Set(ByVal value As String)
_Message = value
End Set
End Property
Public Overrides Function Validate(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As System.Windows.Controls.ValidationResult
Dim result As ValidationResult = ValidationResult.ValidResult
If Not ValidateCore(value, cultureInfo) Then
Dim message As String = Me.FormatMessage(value)
result = New ValidationResult(False, message)
End If
Return result
End Function
Protected MustOverride Function ValidateCore(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As Boolean
Protected Overridable Function FormatMessage(ByVal value As Object) As String
Return String.Format(Me.Message, value)
End Function
End Class
End Namespace
The set of ValidationRules would be defined as a resource in XAML. In order to be able to do that, I create several classes which allow to define collections of ValidationRules grouped by property name:
<System.Windows.Markup.ContentProperty("Setters")>
Public Class ValidationRuleSetter
Inherits DependencyObject
Implements IStickyComponent
Private Shared _DummyDataContext As Object = New Object
Protected Overridable ReadOnly Property DummyDataContext As Object
Get
Return _DummyDataContext
End Get
End Property
Private _Setters As New PropertyValidationRuleSetterCollection
Public ReadOnly Property Setters As PropertyValidationRuleSetterCollection
Get
Return _Setters
End Get
End Property
The collection Setters contains collections of ValidationRules, grouped under different property names.
The ValidationRuleSetter class is a Sticky Component: in addition to defining the set of ValidationRules, it also has the ability to set the ValidationRules on the Bindings which belong to the container’s BindingGroup. The following method will be called during the container’s Initialized event:
Dim f As FrameworkElement = TryCast(base, FrameworkElement)
If f IsNot Nothing Then
Dim bg As BindingGroup = f.BindingGroup
If bg IsNot Nothing Then
For Each be As BindingExpression In bg.BindingExpressions.OfType(Of BindingExpression)()
If Setters.Contains(be.ParentBinding.Path.Path) Then
For Each v As System.Windows.Controls.ValidationRule In Setters.Item(be.ParentBinding.Path.Path).ValidationRules
be.ParentBinding.ValidationRules.Add(v)
Next
End If
If Setters.Contains(String.Empty) Then
For Each v As System.Windows.Controls.ValidationRule In Setters.Item(String.Empty).ValidationRules
bg.ValidationRules.Add(v)
Next
End If
Next
End If
End If
End Sub
For each Binding that is accessible to the BindingGroup, it looks up the name of the bound property and attaches all ValidationRules which are grouped under that name, if any. By convention, ValidationRules which have an empty String as property name are attached to the BindingGroup itself.
How to Use ValidationRuleSetter on a Container
In order to be able to attach the Sticky Component to a container, one defines a custom attached property like this:
Public Shared Function GetRuleSetter(ByVal d As DependencyObject) As ValidationRuleSetter
Return d.GetValue(RuleSetterProperty)
End Function
Public Shared Sub SetRuleSetter(ByVal d As DependencyObject, ByVal value As ValidationRuleSetter)
d.SetValue(RuleSetterProperty, value)
End Sub
This is actually the standard way of defining a sticky property in the WPFGlue framework.
One would then define the container in XAML like this:
<StackPanel.BindingGroup>
<BindingGroup/>
</StackPanel.BindingGroup>
<TextBox Text="{Binding StringProperty, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"/>
<TextBox Text="{Binding IntegerProperty, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"/>
<TextBox Text="{Binding DoubleProperty, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource ValidationTemplate}"/>
</StackPanel>
Side Effects of Using BindingGroups
The presence of a BindingGroup on a container has various effects:
- Any failed ValidationRule on any Binding inside the BindingGroup will make Validation.HasError return true on the container.
- The BindingGroup’s own ValidationRules will cause the Validation.ErrorTemplate to be displayed on the container. ValidationRules on the Bindings will show the ErrorTemplate on the respective control and also the container’s ErrorTemplate.
- The UpdateSourceTrigger on all Bindings inside the BindingGroup will be set to Explicit by default, which means that one would have to call BindingGroup.CommitEdit in order to have the Bindings update their sources. However, if the UpdateSourceTrigger on a Binding is set to another value in XAML, this value will be honoured.
Conclusion
Using this technique, it is possible to separate the definition of validation rules both from the model and from the view. This overcomes the main disadvantage of ValidationRules and opens up the way for further applications.
In posts to come, I intend to cover how to extend this technique in order to do type checks on property values before they are committed to the bound properties, and how to connect this with general data entry handling.
On the Downloads page, you can find the full code of the examples, plus some additions which will be covered in a later post.
WPFGlue goes WPF 4.0
When VS 2010 was released, I was quite eager to shift to the new version of the framework: the last two releases had brought a lot of real improvements, so I expected the new release to do likewise. Therefore, I upgraded the WPFGlue code to .Net 4.0 and tried it out. These were my experiences:
Upgrade Experience
Upgrading went real smooth: it’s possible to install VS 2010 parallel to VS 2008, so I did that, copied my projects to the new version’s project folder, and opened them. After completing an Upgrade Wizard, there I was… No problem at all, and the Upgrade Wizard didn’t have to change any code, it just set the projects to target the .Net 4.0 Framework instead of .Net 3.5 . (Cool feature: in VS 2010 Express you can select the targeted framework version in the compile options, and you can select older and (hopefully) even newer versions than the one that comes with the VS installation). I have been running VS 2010 and VS 2008 in parallel ever since, and so far haven’t experinced any problems with that.
However, when testing the examples again, there were a few glitches:
- The LocalizedList in the WPFGlue.Localization namespace caused an error message in the XAML designer.
- There was a problem with Bindings when navigating backwards through the navigation history in a NavigationWindow.
Error Message in the XAML Designer
The new XAML designer is supposed to be more robust and to be more consistent with the Blend XAML designer. However, one of the tricks I did with the LocalizedList class caused it to hiccup: On this class, I implemented IList so one could set the child elements directly. This was due to the fact that the VS 2008 XAML designer was not handling this very graceful, and I wrote the class before I found out what one has to do to have proper support for typed collections. At the same time, this class can be used as a converter in bindings, in order to translate Enum values into human readable localized strings.
The combination of IList and IValueConverter seemed to be too much for the designer: while the application was compiling and running alright, the designer kept showing squiggly lines about an unset object reference in the XAML pane.
Debugging custom mark-up extensions in VS 2010 Express Edition is a little bit painful. I would recommend to buy the full version if you consider this, and meanwhile I’d recommend to keep away from them… Anyway, I found out that the error message goes away if you don’t implement IList on the same class as IValueConverter, so I just followed the pattern described in the Coding for XAML post.
I posted this problem on the Connect website as product feedback, but they decided to not do anything about it, which I can understand.
TextBox Bindings not Restored when Navigating Backwards
I found this problem first in this thread in the MSDN WPF forum. When you have a page that has data-bound TextBoxes, navigate away from the page and then go back using the Back button of the NavigationWindow that contains the page, the TextBoxes come up blank, even though the DataContext of the page and TextBox is set correctly. It seems that the XAML of the Page is not read the same way when the page is re-displayed; I tried to solve the problem with a custom mark-up extension, and the extension was not instantiated when the page was revisited. So, I tried a DataTemplate instead, and that worked. Finally, I came up with wrapping the page contents into a UserControl. The UserControl seems to read its XAML definition every time it is displayed, and since it inherits the correct DataContext, the problem goes away.
Still, I think this is quite a serious glitch. I posted it to Connect as well, the answer is pending.
Conclusion
Having these strange kinds of errors was a little bit sobering about .Net 4.0. Even so, since I found workarounds, and since the new framework has some cool new features I hope to be using soon, I’m going to stick to it.
So, I updated the downloads page with projects targeting the new version. I’m still keeping copies of the WPF 3.5 version, as well, but I’m not going to include new code into these versions.
Coming Soon…
The next thing I’m planning to do is to research the object lifetime events in WPF 4.0. I was stopped in my tracks a little bit when I found out that the pattern of Loaded and Unloaded events is not quite what one expects (this was still under WPF 3.5), and I’d like to find out what happens there exactly.
Coding for XAML
XAML is basically a way of describing object structures, which will be instantiated by the XAML parser when a page, resource or template is used. Since one of the ideas in WPFGlue is to configure components in XAML, I had to wrestle a bit with the VS XAML designer and my classes in order to make them work well with each other. This post is about what patterns you can use in order to make classes nice to use in XAML, and how to implement them.
Basics
A XAML tag is basically nothing but a class name and some attributes. The class name stands for the component, the attributes, for its properties. What the XAML parser will do is to create an instance of the class using a parameterless constructor, and to set the properties which have the same names as the attributes to the provided attribute values. So, the basic rules for creating a class for use in XAML are:
- The class must be public.
- It must have a public, parameterless constructor.
- All properties that will be accessed in XAML must be public.
Two other best practices turned out to influence the XAML experience of my custom components, even though I didn’t quite find out why, and I expect this behaviour to change with version 4.0… Well, the problem was that the VS 2008 XAML editor didn’t recognize collection types properly, and that its Intellisense function would suggest every available element, not only the one which are allowed in the collection.
In order to fix that, I did the following:
- Move all components I want to use in XAML to a separate DLL (i.e. not the project they are used in)
- Declare an XML Namespace for this DLL. When I use the components, I don’t declare a XML prefix for their CLR namespace, but use this declared namespace instead.
<Assembly: System.Windows.Markup.XmlnsDefinition("http://wpfglue.wordpress.com/samples/codingforxaml", "CodingForXAML")>
<Assembly: System.Windows.Markup.XmlnsPrefix("http://wpfglue.wordpress.com/samples/codingforxaml", "cfx")>
Using the ContentProperty Attribute
XAML elements can have one or many content elements. A content element is something like the “natural” or most important child element of its parent. It is special because you can set it in XAML without mentioning the name of the property which references it. However, in order to create an object structure, the XAML parser needs to know the property name. Therefore, you can set the name of the property which holds the child element by adding the System.Windows.Markup.ContentProperty(“MyChild”) attribute to the class declaration, where “MyChild” would be the name of the property which holds the child.
If the property is of an collection type, the element can contain multiple child elements, and they will all be added to the collection which the property holds.
Collections for XAML
If you set up your project like I explained in the first section, all you have to do is to derive a collection class from List(Of T) or ObservableCollection(Of T), where T is your item type.
Inherits List(Of XamlCollectionItem)
End Class
Public Class XamlCollectionItem
Private _IntegerValue As Integer
Public Property IntegerValue() As Integer
Get
Return _IntegerValue
End Get
Set(ByVal value As Integer)
_IntegerValue = value
End Set
End Property
End Class
If you do it this way, the XAML will display and compile without problems, and Intellisense will offer you only the available classes of the correct type. Normally, since we are talking about configuring components through XAML, the collection is not expected to change during run time, so it would be enough to use List(Of T).
If you want to use a collection type as the content for an element, you’d have to do the following further steps:
- Create a read-only property which holds an instance of the collection type.
- Initialize it with an empty collection in the elements constructor.
- Set the ContentProperty attribute to the property’s name.
Public Class XamlCollectionParent
Private _Children As New XamlCollection
Public ReadOnly Property Children() As XamlCollection
Get
Return _Children
End Get
End Property
End Class
Referring to Objects by Name I: Keyed Collections
Sometimes you want to refer to be able to refer to specific children of an element. The best way to make sure that you get the correct child is to name the children and to refer to them by name. In XAML, you would do this in a binding’s property path, like this: {Binding Path=MyParentElement.Children[ChildName]}
In order to be able to do that, you’d derive the collection class from KeyedCollection(Of T). Then you’d have to override the GetKeyForItem function, which is supposed to create a unique string key from an item of type T.
Inherits System.Collections.ObjectModel.KeyedCollection(Of String, XamlDictionaryItem)
Protected Overrides Function GetKeyForItem(ByVal item As XamlDictionaryItem) As String
Return item.Key
End Function
End Class
Public Class XamlDictionaryItem
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 _IntegerValue As Integer
Public Property IntegerValue() As Integer
Get
Return _IntegerValue
End Get
Set(ByVal value As Integer)
_IntegerValue = value
End Set
End Property
End Class
You may ask why I don’t use Dictionary(Of TKey, TValue). Well, it seems that XAML’s support for dictionaries is very much geared towards the special needs of ResourceDictionaries, and I believe it is not intended to be used generally. However, KeyedCollection offers you the additional benefit that you can simply iterate over the items and don’t have to worry about mapping Key / Value pairs, since the keys are taken directly from the items.
Referring to Objects by Name II: NameScopes
A NameScope in WPF is what makes element names in a page unique and allows to reference elements by name using their x:Name attribute. If you have a tree structure of elements, but need to reference single elements by name also, a NameScope is the thing to use.
A Namescope is defined through a root element XAML. All descendants of this element which have an x:Name attribute will automatically be registered to the Namescope. In order for this to work, you have to do the following:
- The class which is the root element has to implement the System.Windows.Markup.INameScope interface. Internally, the implementation can be based on the NameScope class, which comes with the WPF framework.
- The classes which are used as descendants of the root element need to use the System.Windows.Markup.RuntimeNameProperty attribute in their declaration, so that the XAML parser knows which property of the class stores the name.
- In order to access the items by name, one needs an indexer. XAML is a little bit limited in its support for indexed properties: you have to have a class which declares the indexer as its default property. In order to provide indexed access to the named elements, this examples uses a nested class which does nothing but find the elements through its parent’s NameScope.
Public ReadOnly Property Node() As NamescopeIndexer
Get
Return _Indexer
End Get
End Property
Public Class NamescopeIndexer
Private _Root As XamlNamescopeRoot
Friend Sub New(ByVal root As XamlNamescopeRoot)
MyBase.New()
_Root = root
End Sub
Default Public ReadOnly Property Item(ByVal name As String) As NamescopeNode
Get
Return _Root.Namescope.FindName(name)
End Get
End Property
End Class
Markup Extensions
Markup extensions are helpful in situations where you have to do more complicated things than just creating an instance of a class and assign it some values. For example, you can use a Markup extension if you want to establish a binding, or if you want to access a backing store which itself is not accessible to XAML, like application settings, localized resources or a configuration file.
However, I am not very fond of markup extensions. They are basically a convenience: normally, you can achieve the same using just a more verbose syntax. And the convenience comes at a price:
Markup extensions have to execute well both at runtime and in the VS designer. Both environments are radically different, and it is not trivial to find out the difference from inside the markup extension. Also, if there is any problem in design mode, there is no debugging support, since the designer always uses the release compiled version of the markup extension. Moreover, I even couldn’t use structured error handling inside the class, since my catch blocks never were called, and sometimes VS crashes when compiling or editing markup extensions.
Anyway, for the sake of completeness, here what I learned:
- Derive a markup extension from System.Windows.Markup.MarkupExtension and override the ProvideValue method.
- Set the MarkupExtensionReturnType attribute to the correct type.
- If the type of the serviceProvider parameter is System.Windows.Markup.ProvideValueServiceProvider, you are in runtime mode, otherwise, you are in design mode.
- Use serviceProvider.GetService(GetType(IProvideValueTarget)) in order to retrieve information about the property which will receive the value. If the TargetProperty parameter is of type DependencyProperty, the property is a dependency property and you can do things like set up a binding, so that the value will be updated automatically if the underlying data source changes.
- Always return a valid value of the correct type, even if the value is some message like “I couldn’t find what you wanted”.
- Whatever you do, avoid exceptions in the ProvideValue method at all costs. In this case this doesn’t mean “catch”, but really “avoid”, since structured exception handling doesn’t seem to work when the extension is used in the designer.
Reporting Configuration Errors
As a rule, your component shouldn’t raise any exceptions if it doesn’t have all the information it needs to do its job, but just make do without it, using intelligent default behaviour or gracefully reducing the functionality. However, sometimes you just have to report something.
In this case, you can raise an ArgumentException with an appropriate message in the property setter which receives the invalid value. Exceptions that are raised in this way will show up in the VS Error List and keep the project from building.
Other Best Practices
- Don’t do complex things like e.g. accessing a database in a constructor. Constructors will run at design time as well as at run time. So, if they need information from the application they are running in, this information might not be accessible at design time. Therefore, one might see strange error messages at design time which are difficult to debug.
- Don’t rely on a certain order in which the properties of your component should be set. If you need several property values in order to do a job, either don’t access the values until you need to do the job, or check whether you have a complete set of values each time any of the properties changes, and start the job as soon as you have a valid set of values.
Conclusion
If you follow these guidelines, your components will behave similar to the components that come with the WPF framework, and they will integrate into the support given by the VS XAML editor.
There are some other techniques which I haven’t covered here, but which I would like to mention: using dependency properties and using type converters. You can find examples for dependency properties all over this blog, e.g. here: http://wpfglue.wordpress.com/2009/12/16/the-sticky-viewmodel-pattern/ . An example for using type converters is given in this post: http://wpfglue.wordpress.com/2009/11/30/css-class-equivalent-in-wpf/ . Otherwise, you will find the patterns which I discussed here everywhere in the WPFGlue code. A special example project for this post is available for download here:
(as always on wordpress.com, you will have to rename it to a .zip extension in order to extract it).
