Archive

Archive for the ‘Uncategorized’ Category

Implementing the Sticky Command Design Pattern

February 13, 2010 1 comment

The Sticky Command design pattern is one of the patterns in WPFGlue which allow to add custom functionality to out-of-the-box WPF controls. Adding a Sticky Command to a FrameworkElement gives it a certain new behaviour, and makes this behaviour available to the element and all its child elements through a routed command, while the implementation of the element remains separate and independent of the implementation of the behaviour.

Let’s take an example. A very common need in data oriented applications is a submit command which sends the values of a group of controls to the underlying data store. This command should be disabled when the data is invalid, and it should be possible to connect it to a KeyGesture which allows to call the command by pressing a given key when entering data.

<GroupBox x:Name="CustomerGroupBox" Header="Customer"
          v:Submit.Command="{StaticResource SubmitCommand}"
          v:Submit.KeyGesture="Enter"> <!—- Content left out for clarity -->
</GroupBox>

The Sticky Command is stuck to the GroupBox by setting the Submit.Command Sticky Property. Optionally, one can add a KeyGesture which invokes the command, in this case pressing the Enter key. The SubmitCommand is just a standard RoutedCommand:

<Window.Resources>
    <RoutedCommand x:Key="SubmitCommand"/>
</Window.Resources>

By itself it doesn’t provide any functionality; it’s just kind of an identifier for the behaviour we want to add to the GroupBox.

Creating a Sticky Property

A Sticky Property is an attached property that, in addition to storing a value on the target element, does some work in its PropertyChanged handler. In our case, this work consists of creating a CommandBinding and setting it on the target element, or removing the created CommandBinding, respectively.

Public Class Submit
    Inherits DependencyObject

    Public Shared ReadOnly CommandProperty As DependencyProperty = _
            DependencyProperty.RegisterAttached("Command", _
            GetType(RoutedCommand), GetType(Submit), _
            New PropertyMetadata(AddressOf OnCommandChanged))
    Public Shared Function GetCommand(ByVal d As DependencyObject) _
            As RoutedCommand
        Return d.GetValue(CommandProperty)
    End Function
    Public Shared Sub SetCommand(ByVal d As DependencyObject, _
            ByVal value As RoutedCommand)
        d.SetValue(CommandProperty, value)
    End Sub
    Private Shared Sub OnCommandChanged(ByVal d As DependencyObject, _
        ByVal e As DependencyPropertyChangedEventArgs)
        Dim f As FrameworkElement = TryCast(d, FrameworkElement)
        If f IsNot Nothing Then
            If e.OldValue IsNot Nothing Then
                Detach(f, e.OldValue)
            End If
            If e.NewValue IsNot Nothing Then
                Attach(f, e.NewValue)
            End If
        End If
    End Sub
'...
End Class

The implementation of OnCommandChanged is quite simple: if the property already has a value, the command should be detached from the target, and if a new value is provided, this value should be attached to it. One might ask why it is necessary to provide a Detach procedure as well. Why not just leave the CommandBinding in place? If the command can be removed by changing setting the value of Submit.Command to Nothing, then the command can be attached and detached through triggers, which opens up a whole new line of uses…

Attaching and Detaching the CommandBinding

Private Shared Sub Attach(ByVal d As FrameworkElement, _
                          ByVal command As RoutedCommand)
    d.CommandBindings.Add(New SubmitCommandBinding(command))
    Dim gesture As KeyGesture = GetKeyGesture(d)
    AttachKeyGesture(d, command, gesture)
    If d.BindingGroup Is Nothing Then
        d.BindingGroup = New BindingGroup
    End If
End Sub
Private Shared Sub Detach(ByVal d As FrameworkElement, _
                          ByVal command As RoutedCommand)
    For Each b As SubmitCommandBinding In _
            d.CommandBindings.OfType(Of SubmitCommandBinding)()
        d.CommandBindings.Remove(b)
    Next
    DetachKeyGesture(d)
End Sub

The Attach procedure creates a new CommandBinding that connects the provided RoutedCommand to its implementation, and adds it to the targets CommandBindings collection.

The Detach procedure removes the CommandBinding from the collection. In order to be able to find the correct CommandBinding, Attach uses a class derived from CommandBinding, in order to use its type as marker. This way, one doesn’t have to store a reference to the created CommandBinding in order to be able to remove it later. The dummy class is a private nested class inside the Submit class:

Private Class SubmitCommandBinding
    Inherits CommandBinding
    Public Sub New(ByVal command As RoutedCommand)
        MyBase.New(command, AddressOf OnExecutedSubmit, _
                   AddressOf OnCanExecuteSubmit)
    End Sub
End Class

The Command’s Implementation

The procedures OnExecutedSubmit and OnCanExecuteSubmit handle the RoutedCommand.CanExecute and RoutedCommand.Executed events that are sent out by the CommandBinding. It is useful to separate the code specific to event handling from the code that does the actual work:

Private Shared Sub OnCanExecuteSubmit(ByVal sender As Object, _
        ByVal e As CanExecuteRoutedEventArgs)

    Dim result As Boolean = False
    Dim target As FrameworkElement = TryCast(sender, FrameworkElement)
    If target IsNot Nothing Then
        ' Separate event specific code from domain specific code...
        result = CanExecuteSubmit(target,e.Parameter)
    End If
    e.CanExecute = result
    e.Handled = True
End Sub

Public Shared Function CanExecuteSubmit(ByVal target As FrameworkElement, _
        ByVal parameter As Object) As Boolean
    Dim result As Boolean = False
    If target IsNot Nothing Then
        result = (target.BindingGroup IsNot Nothing AndAlso Not _
                  System.Windows.Controls.Validation.GetHasError(target))
    End If
    Return result
End Function

Private Shared Sub OnExecutedSubmit(ByVal sender As Object, _
                                    ByVal e As ExecutedRoutedEventArgs)
    Dim target As FrameworkElement = TryCast(sender, FrameworkElement)
    If target IsNot Nothing Then
        If CanExecuteSubmit(target, e.Parameter) Then
            ExecuteSubmit(target, e.Parameter)
        End If
        e.Handled = True
    End If
End Sub

Public Shared Sub ExecuteSubmit(ByVal target As FrameworkElement, _
        ByVal parameter As Object)
    target.BindingGroup.UpdateSources()
End Sub

Providing a KeyGesture

In order to be able to specify a KeyGesture for the command on the target, we need to create a second Sticky Property, which is able to add the KeyGesture to the target’s InputBindings collection. The method is similar to the one used with the CommandBinding itself. However, there is one point of note: We cannot be sure of the order in which the attached properties are set on the target. So, we have to make sure that CommandBinding and InputBinding are set up correctly no matter which is specified first. Since the InputBinding depends on the CommandBinding, one could say that Submit.Command is the main Sticky Property, while Submit.KeyGesture is a supporting Sticky property. The rule would then be that the Attach and Detach procedures for the main Sticky property would have to call the Attach procedures for all supporting properties in turn, while the Attach procedures of the supporting properties only attach something if the main Sticky Property has already been set.

The implementation of the KeyGesture functionality looks like this:

Public Shared ReadOnly KeyGestureProperty As DependencyProperty = _
        DependencyProperty.RegisterAttached("KeyGesture", _
                    GetType(KeyGesture), GetType(Submit), _
                    New PropertyMetadata(AddressOf OnKeyGestureChanged))
Public Shared Function GetKeyGesture(ByVal d As DependencyObject) _
        As KeyGesture
    Return d.GetValue(KeyGestureProperty)
End Function
Public Shared Sub SetKeyGesture(ByVal d As DependencyObject, _
                                ByVal value As KeyGesture)
    d.SetValue(KeyGestureProperty, value)
End Sub
Private Shared Sub OnKeyGestureChanged(ByVal d As DependencyObject, _
        ByVal e As DependencyPropertyChangedEventArgs)
    Dim f As FrameworkElement = TryCast(d, FrameworkElement)
    If f IsNot Nothing Then
        If e.OldValue IsNot Nothing Then
            DetachKeyGesture(f)
        End If
        If e.NewValue IsNot Nothing Then
            AttachKeyGesture(f, GetCommand(f), e.NewValue)
        End If
    End If
End Sub
Private Shared Sub AttachKeyGesture(ByVal d As FrameworkElement, _
                                    ByVal command As RoutedCommand, _
                                    ByVal gesture As KeyGesture)
    If command IsNot Nothing And gesture IsNot Nothing Then
        d.InputBindings.Add(New SubmitKeyBinding(command, gesture))
    End If
End Sub
Private Shared Sub DetachKeyGesture(ByVal d As FrameworkElement)
    For Each i As SubmitKeyBinding In _
            d.InputBindings.OfType(Of SubmitKeyBinding)()
        d.InputBindings.Remove(i)
    Next
End Sub

Private Class SubmitKeyBinding
    Inherits InputBinding
    Public Sub New(ByVal command As RoutedCommand, _
            ByVal gesture As KeyGesture)
        MyBase.New(command, gesture)
    End Sub
End Class

Reusability

Once we have this submit command, we can reuse it as it is, in any project, with any kind of control or data. But what about reusability of the pattern implementation itself?

The whole Submit class consists of static members, only. This has the advantage of not creating dependent object references with the command bindings, which could interfere with the garbage collection of the page on which the command is used. Unfortunately, shared members cannot be overridden, which means that it is not straight forward to create a common base class for Sticky Commands and derive new implementations from that class. However, the domain specific code and the pattern specific code are quite easy to separate into different procedures, so that it might be an option to use Item Templates or Code Snippets in order to reuse the pattern specific code.

Conclusion

The Sticky Command pattern offers a powerful and flexible way of modifying the behaviour of standard controls without changing their implementation. Because it is used through declarative syntax, it can leverage the full power of WPF style, trigger and template mechanisms. In addition to that, a new implementation of the pattern can be created from an existing one with minimal changes.

Downloads

The example solution on the Downloads page contains a project called SubmitCommandExample which has the complete code for this example.

Advertisements

No More Sticky Stars!

November 30, 2009 Leave a comment

Oh, bother… good idea shattered on the rocks of the big Language Barrier Reef… Seems that folks feel a little ambiguous about being awarded Sticky Stars. Probably “sticky” has some connotations I wasn’t aware of, since English isn’t my mother tongue. So, I’ll have to find another way to express my respect… maybe “Self-Adhesive Stars” or “Attached Stars”?

Anyway, no innuendos intended (at least I was able to look that up in a dictionary…) and “Honi soit qui mal y pense”.

Categories: Uncategorized

CSS Class Equivalent in WPF

November 30, 2009 1 comment

In the small hours of this morning, I had an idea about how to create a stylesheet for FlowDocuments that behaves similar to CSS. What’s the difference between CSS classes and WPF styles? Well, CSS classes are not necessarily specific to a certain element type, and they can be combined on the styled element, while WPF Styles are specific to the element type, and each element can have exactly one Style (in addition to the default style, which fills in wherever the element style doesn’t provide a value). Also, if you want to combine Styles in WPF, you have to do it in the Style definition using BasedOn, which I find quite inflexible, and which drives one crazy if one has to support a lot of independent permutations.

So, what was the idea? The first one was that CSS classes should not be implemented as Styles, but as Triggers inside styles, which are selected whenever a certain attached property has the fitting value. So, in order to support different classes of TextBlocks, one would do this:

<Style TargetType="TextBlock" >
    <Style.Triggers>
        <Trigger Property="t:MultiStyle.Selector" Value="italic">
            <Setter Property="FontStyle" Value="Italic"/>
        </Trigger>
        <Trigger Property="t:MultiStyle.Selector" Value="red">
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
        <Trigger Property="t:MultiStyle.Selector" Value="bold">
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="FontStyle" Value="Normal"/>
        </Trigger>
    </Style.Triggers>
</Style>

 

The second idea was to use a special type for the attached property. The StyleSelector class tokenizes its String value, and returns “equal” on a comparison with another string or StyleSelector if the other object contains one of the tokens that are defined:

Public Class MultiStyle
    Inherits DependencyObject

    Public Shared ReadOnly SelectorProperty As DependencyProperty = DependencyProperty.RegisterAttached("Selector", GetType(StyleSelector), GetType(MultiStyle))
    Public Shared Function GetSelector(ByVal d As DependencyObject) As StyleSelector
        Return d.GetValue(SelectorProperty)
    End Function
    Public Shared Sub SetSelector(ByVal d As DependencyObject, ByVal value As StyleSelector)
        d.SetValue(SelectorProperty, value)
    End Sub

End Class

<System.ComponentModel.TypeConverter(GetType(StyleSelectorConverter))> _
Public Class StyleSelector
    Implements IEquatable(Of StyleSelector), IEquatable(Of String)

    Public Sub New(ByVal value As String)
        _Value = value
    End Sub

    Private _Value As String
    Public Property Value() As String
        Get
            Return _Value
        End Get
        Set(ByVal value As String)
            _Value = value
        End Set
    End Property

    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        If TypeOf obj Is StyleSelector Then
            Return Equals1(obj)
        End If
        If TypeOf obj Is String Then
            Return Equals2(obj)
        End If
        Return MyBase.Equals(obj)
    End Function

    Public Function Equals1(ByVal other As StyleSelector) As Boolean Implements System.IEquatable(Of StyleSelector).Equals
        If other IsNot Nothing And _Value IsNot Nothing Then
            Dim myValue() As String = _Value.Split(" "c)
            Dim otherValue() As String = other.Value.Split(" "c)
            Dim result As Boolean = False
            Dim i As Integer = 0
            Do While i < otherValue.Length And Not result
                result = myValue.Contains(otherValue(i))
                i += 1
            Loop
            Return result
        Else
            Return MyBase.Equals(other)
        End If
    End Function

    Public Function Equals2(ByVal other As String) As Boolean Implements System.IEquatable(Of String).Equals
        If other IsNot Nothing And _Value IsNot Nothing Then
            Dim myValue() As String = _Value.Split(" "c)
            Return myValue.Contains(other)
        Else
            Return MyBase.Equals(other)
        End If
    End Function
End Class

Public Class StyleSelectorConverter
    Inherits System.ComponentModel.TypeConverter

    Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
        Return (sourceType Is GetType(String))
    End Function

    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        Return (destinationType Is GetType(String))
    End Function

    Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        Dim result As StyleSelector = Nothing
        If TypeOf value Is String Then
            result = New StyleSelector(value)
        End If
        Return result
    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
        Dim result As Object = Nothing
        If TypeOf value Is StyleSelector Then
            result = DirectCast(value, StyleSelector).Value
        End If
        Return result
    End Function
End Class

The only thing that remains to do is to implement the TypeConverter that allows to use StyleSelector as an attribute value in XAML, and to define the attached property.

Applications

Since I couldn’t go back to sleep, I tried it out immediately. You can download the VB solution here. (you will have to remove the .doc extension from the zip file; wordpress.com doesn’t allow zip files directly.)

However, when I was done, the idea didn’t seem so brilliant anymore as when I woke up. There are limitations of course. The trick works only with property setters. If you want to use triggers, they will be the same for all classes. What can you do with this what you can’t do with styles? The last thing that I came up with is that one could write a style sheet editor that creates styles with identical class definitions for different types of controls, so that you can use the same set of property values for different elements, as in CSS. However, you’d have to have this editor, otherwise it would be to much work to keep the class definitions equal to each other. And, of course, one can combine multiple classes on the same element, for what it’s worth…

PS: What happens if two classes provide different values for the same property and are both set on the same element? Because triggers are executed in the order they appear in the Style definition, the class that is defined last wins and overwrites the value of its sibling…

Categories: Uncategorized Tags: , , ,

New Sticky Star Award: Selectors and attached behaviours

November 14, 2009 Leave a comment

I just awarded 5 Sticky Stars to Zoodazzle, a user in the MSDN WPF forum who published a way of making one item only selected across a group of selector controls in a WPF form. He uses an attached (or sticky) behaviour for this and can have several groups of selectors on the same page… really elegant solution.

Look at this thread: Selectors and attached behaviours.5StickyStars

 

See the Sticky Star Awardees page for more links to great solutions out there.

Categories: Uncategorized Tags: