MVVM and focus ListView element with complex template

This is the scenario: I have a complex object with a lot of properties, I created a user control in MVVM style to edit those properties, then in the main View I have a list of those objects, and I simply used a ListView control that uses the aforementioned user control as a template.

   1: <DataTemplate x:Key="RilevazioneTemplate">

   2:     <Border BorderThickness="8" BorderBrush="{Binding BorderBrush, Mode=OneWay}" CornerRadius="8" Margin="0,2" Padding="3">

   3:         <Controls:RilevazioneForTreeEditor />

   4:     </Border>

   5: </DataTemplate>

   6:  

   7: ...

   8:  

   9: <ListView ItemTemplate="{DynamicResource RilevazioneTemplate}" 

  10: ...

Now the problem is the following one: when I use that ListView at runtime and I click on a texbox inside that user control, the SelectedItem of the ListView does not change, it changes only if I click on a empty part of the USerControl.

SNAGHTML262378

Figure 1: The problem: focusing a textbox of the usercontrol does not select the item of the ListView.

I could solve this problem with a little bit of Code Behind, but this is not MVVM style, and moreover is not maintainable because I want that logic to be declared only in one part of my project and reused in multiple views and I want a way to tell declaratively with XAML “Hey, when this textbox got focus, select the listviewItem that contains it”. I absolutely need to know selected element because I have logic on it to be done on the ViewModel and I want to keep at minimum the Code behind.

The solution is AttachedBehaviors; after a little browsing on the internet I find some hint to solve my problem. The solution is in this class.

   1: public class ListViewFocusBehaviour

   2: {

   3:     

   4:     public static readonly DependencyProperty SelectListViewItemOnFocusProperty =

   5:         DependencyProperty.RegisterAttached

   6:         (

   7:             "SelectListViewItemOnFocus",

   8:             typeof(bool),

   9:             typeof(SelectListViewItemOnFocusBehaviour),

  10:             new UIPropertyMetadata(false, OnSelectListViewItemOnFocusPropertyChanged)

  11:         );

  12:     public static bool GetSelectListViewItemOnFocus(DependencyObject obj)

  13:     {

  14:         return (bool)obj.GetValue(SelectListViewItemOnFocusProperty);

  15:     }

  16:     public static void SetSelectListViewItemOnFocus(DependencyObject obj, bool value)

  17:     {

  18:         obj.SetValue(SelectListViewItemOnFocusProperty, value);

  19:     }

  20:  

  21:    private static void OnSelectListViewItemOnFocusPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)

  22:     {

  23:         FrameworkElement element = dpo as FrameworkElement;

  24:         if (element != null)

  25:         {

  26:             if ((bool)args.NewValue)

  27:             {

  28:                 element.PreviewGotKeyboardFocus += OnControlPreviewGotKeyboardFocus;

  29:             }

  30:             else

  31:             {

  32:                 element.PreviewGotKeyboardFocus -= OnControlPreviewGotKeyboardFocus;

  33:             }

  34:         }

  35:     }

  36:  

  37:     private static void OnControlPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)

  38:     {

  39:         DependencyObject p = sender as DependencyObject;

  40:         while (p != null && !(p is ListBoxItem))

  41:         {

  42:             p = VisualTreeHelper.GetParent(p);

  43:         }

  44:  

  45:         if (p == null)

  46:             return;

  47:  

  48:         ((ListBoxItem) p).IsSelected = true;

  49:     }

  50:  

  51:   

  52:  

  53: }

This is a simple class that declares an AttachedProperty called SelectedListViewItemOnFocus, that simply (line 10) declare a callback function every time its value changes. The callback function simply try to cast the sender object to FrameworkElement, and if the cast succeeds it add an handler to PReivewGotKeyboardFocus, thus permitting to handle the event of focus change.

Finally the handler of focus change simply iterate to all parents  to find a parte that is of type ListBoxItem, and if it founds it, it simply select it. Now all the logic is in a simple class and you can use in XAML in this way.

   1:  

   2:     <TextBox Grid.ColumnSpan="4"

   3:              Behaviours:ListViewFocusBehaviour.SelectListViewItemOnFocus="True"

   4:              Height="Auto" HorizontalAlignment="Stretch" />

With this piece of XAML I’m telling to WFP engine to attach the SelectListViewItemOnFocus property to the textbox and give it a value of true. What happens at runtime? First of all the engine attach that property to the textbox and assign the value true, thus raising the OnSelectListViewItemOnFocusPropertyChanged property, that in turn add an handler to the PreviewGotKeyboardFocus even of the textbox. My final goal is achieved, I’m able to add logic to even of a Framework Element with only XAML declaration.

The final effect is that with this technique we can add behaviors to a control declaratively with XAML, keeping MVVM pattern intact and keeping all the logic only in one class, that can be reused for multiple views.

alk.

Published by

Ricci Gian Maria

.Net programmer, User group and community enthusiast, programmer - aspiring architect - and guitar player :). Visual Studio ALM MVP

Leave a Reply

Your email address will not be published.