Tuesday 1 January 2013

Setting Control Focus… The MVVM Way…

I needed to be able to set the Focus of a control, using MVVM. This means of course, that we can’t interact directly with the View, but instead must rely on Binding.

I found a very helpful post by Khalid Rafique which allows exactly this;

http://codenicely.blogspot.co.uk/2012/01/how-to-set-textbox-focus-in-silverlight.html

By making use of attached Interactivity Behaviours, we can Bind an IsFocused Property, allowing the focus of the selected control to be set..Very handy indeed.

The VB.net code is shown below;

Behaviour Class:

Imports System.Windows.Interactivity
 
Namespace Behaviours
 
    Public Class clsFocusBehavior
        Inherits Behavior(Of Control)
 
#Region "Properties"
 
        ''' <summary>
        ''' The IsFocused Property, which is a backing for the IsFocusedProperty Dependancy Property
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property IsFocused() As Boolean
            Get
                Return CBool(GetValue(IsFocusedProperty))
            End Get
            Set(value As Boolean)
                SetValue(IsFocusedProperty, value)
            End Set
        End Property
 
        ''' <summary>
        ''' The Has Initial Focus Property, which is a backing for the HasInitialFocusProperty Dependancy Property
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property HasInitialFocus() As Boolean
            Get
                Return CBool(GetValue(HasInitialFocusProperty))
            End Get
            Set(value As Boolean)
                SetValue(HasInitialFocusProperty, value)
            End Set
        End Property
 
#End Region
 
#Region "Dependancy Properties"
 
        ''' <summary>
        ''' Expose an IsFocused Dependancy Property to allow Binding
        ''' </summary>
        ''' <remarks></remarks>
        Public Shared ReadOnly IsFocusedProperty As DependencyProperty = DependencyProperty.Register("IsFocused", GetType(Boolean), GetType(clsFocusBehavior), New PropertyMetadata(False, Sub(d, e)
                                                                                                                                                                                               If CBool(e.NewValue) Then
                                                                                                                                                                                                   DirectCast(d, clsFocusBehavior).AssociatedObject.Focus()
                                                                                                                                                                                               End If
                                                                                                                                                                                           End Sub))
 
        ''' <summary>
        ''' Expose a HasInitialFocus Property, which allows Binaind dicating if the Control should be focuses Initially
        ''' </summary>
        ''' <remarks></remarks>
        Public Shared ReadOnly HasInitialFocusProperty As DependencyProperty = DependencyProperty.Register("HasInitialFocus", GetType(Boolean), GetType(clsFocusBehavior), New PropertyMetadata(False, Nothing))
 
#End Region
 
#Region "Helper Subs"
 
        ''' <summary>
        ''' Override the OnAttached Sub... Add Event Handlers
        ''' </summary>
        ''' <remarks></remarks>
        Protected Overrides Sub OnAttached()
 
            AddHandler AssociatedObject.GotFocus, Function(sender, args) InlineAssignHelper(IsFocused, True)
            AddHandler AssociatedObject.LostFocus, Function(sender, a) InlineAssignHelper(IsFocused, False)
            AddHandler AssociatedObject.Loaded, Sub(o, a)
                                                    If HasInitialFocus OrElse IsFocused Then
                                                        AssociatedObject.Focus()
                                                    End If
                                                End Sub
            MyBase.OnAttached()
        End Sub
 
        ''' <summary>
        ''' Used to simplify assigning the Target Vaues of Control Events
        ''' </summary>
        ''' <typeparam name="T"></typeparam>
        ''' <param name="target"></param>
        ''' <param name="value"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
            target = value
            Return value
        End Function
 
#End Region
 
    End Class
 
End Namespace

XAML to add to View Header Namepaces:



xmlns:Behaviors="clr-namespace:CriticalPath_WPF_MVVM_EF.Behaviours"
             

XAML to add to control:




<i:Interaction.Behaviors>
    <Behaviors:clsFocusBehavior HasInitialFocus="False" IsFocused="{Binding DataContext.OrderNumberFocus, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
</i:Interaction.Behaviors>

Code to add to ViewModel:



Private _OrderNumberFocus As Boolean
 
''' <summary>
''' The Order Number Focus
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public Property OrderNumberFocus As Boolean
    Get
        Return _OrderNumberFocus
    End Get
    Set(value As Boolean)
        _OrderNumberFocus = value
        OnPropertyChanged("OrderNumberFocus")
    End Set
End Property


Set the Focus using;



OrderNumberFocus = True

No comments:

Post a Comment