Unable to focus ListView

407 Views Asked by At

Situation: In MVVM pattern, I have some inputbindings on a listview which work only when the listview is focused. However, whenever user clicks, the listview goes out of focus and user is unable to execute the inputbindings.

Problem: I want to bring the focus on the listview (on button click) in a way that the inputbindings work.

What I tried: I tried using attached property IsFocused (where I focus using UIElement.Focus() and/or Keyboard.Focus()) and binding it to a bool variable in the ViewModel which I would set using an ICommand.

I also tried a separate example where I can use the System.Windows.Input.Keyboard.Focus(item) method in the code behind (I mean the .xaml.cs file with the same name) to focus the listview and it works! But, I don't know how to implement the similar thing in a ViewModel which is connected using a d:DesignInstance attribute.

I believe that the mouseclick event is bubbled up and handled somewhere else which causes the list to unfocus as soon as I click it. Like, if I find a way to set the event as handled that will help, but again I don't know how to do that in a viewmodel. Here is my attached property :

FocusExtension.cs

public static class FocusExtension {
    public static bool GetIsFocused(DependencyObject obj) {
        return (bool)obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value) {
        obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
        DependencyProperty.RegisterAttached(
            "IsFocused", typeof(bool), typeof(FocusExtension),
            new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e) {
        var uie = (UIElement)d;
        if ((bool)e.NewValue) {
            uie.Focus();
        }
    }
}

XAML File:

    <ListView
        x:Name="lv"
        Grid.Column="2" Margin="2" MinWidth="250" Height="400" ToolTip="the List"
        HorizontalContentAlignment="Stretch"
        ItemsSource="{Binding ListBindingInVM}"
        ScrollViewer.HorizontalScrollBarVisibility="Auto"
        ScrollViewer.CanContentScroll="False"
        dd:DragDrop.IsDragSource="True"
        dd:DragDrop.IsDropTarget="True"
        dd:DragDrop.DropHandler="{Binding }"
        behaviour:ListViewAutoScroll.AutoScrollToEnd="True"
        ScrollViewer.VerticalScrollBarVisibility="Visible"
        >

        <ListView.Style>
            <Style TargetType="ListView" >
                <Setter Property="ViewModels:FocusExtension.IsFocused" Value="{Binding ListFocused, Mode=TwoWay}"></Setter>
              <!--The one below is not clean, but worked. However, list goes out of focus on click. -->
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="ViewModels:FocusExtension.IsFocused" Value="True"></Setter>
                    </Trigger>
                </Style.Triggers>


            </Style>
        </ListView.Style>

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDown">
            <!--This command sets the ListFocused to true-->
                <i:InvokeCommandAction Command="{Binding BringListToFocus }"></i:InvokeCommandAction>
            </i:EventTrigger>
        </i:Interaction.Triggers>


        <ListView.InputBindings>
            <!-- Bindings that don't work when list is not focused-->
            <KeyBinding Modifiers="Control" Key="C" Command="{Binding CopyCommand}"/>
            <KeyBinding Modifiers="Control" Key="V" Command="{Binding PasteCommand}"/>
        </ListView.InputBindings>

        <ListView.ContextMenu>
            <ContextMenu>
                <MenuItem Header="Copy" Command= "{Binding CopyCommand}"></MenuItem>
                <MenuItem Header="Paste" Command= "{Binding PasteCommand}"></MenuItem>
            </ContextMenu>
        </ListView.ContextMenu>
1

There are 1 best solutions below

0
Tim Oehler On

The focus behavior that you describe is easily implemented from the codebehind, and doing so does not violate the MVVM pattern. Consider Josh Smith's post, below:

https://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090097

The use of a ViewModel here makes it much easier to create a view that can display a Customer object and allow for things like an "unselected" state of a Boolean property. It also provides the ability to easily tell the customer to save its state. If the view were bound directly to a Customer object, the view would require a lot of code to make this work properly. In a well-designed MVVM architecture, the codebehind for most Views should be empty, or, at most, only contain code that manipulates the controls and resources contained within that view. Sometimes it is also necessary to write code in a View's codebehind that interacts with a ViewModel object, such as hooking an event or calling a method that would otherwise be very difficult to invoke from the ViewModel itself.