Search code examples
c#wpfmvvm

How to get InputBinding to fire a MouseBinding Command on a WPF Listview


This is a common question and I've seen many questions asked and answered here. Sadly, none of the answers seem to work for me. How do I get the DoubleClick Command to fire on a UserControl in a ListView. My xaml:

<ScrollViewer Grid.Column="1" Grid.Row="1" VerticalScrollBarVisibility="Auto">
    <ListView BorderThickness="0" x:Name="methodLV" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
              ItemsSource="{Binding Methods}"
              SelectedItem="{Binding SelectedMethod}">

        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Background="Transparent">
                    <Grid.InputBindings>
                        <MouseBinding Gesture="LeftDoubleClick" Command="{Binding EditMethodCommand}"/>
                    </Grid.InputBindings>
                    <control:MethodCardControl/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
        
    </ListView>
</ScrollViewer>

I've tried adding the command to the UserControl itself:

<UserControl Background="Transparent">
    <UserControl.InputBindings>
       <MouseBinding Gesture="LeftDoubleClick" Command="{Binding OpenControlPanelCommand}"/>
    </UserControl.InputBindings>
</UserControl>

I've tried adding the command directly to the ListView:

<ListView.InputBindings>
   <MouseBinding Gesture="LeftDoubleClick" Command="{Binding EditMethodCommand}"/>
</ListView.InputBindings>

And the command will fire, but only empty parts on the Listview (which makes sense - at least the command works).

I've also tried investigating DependencyProperties, but, I must admit, didn't get too far. Any help would appreciated.


Solution

  • Try:

        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Background="Transparent">
                    <Grid.InputBindings>
                        <MouseBinding Gesture="LeftDoubleClick"
                                        Command="{Binding DataContext.EditMethodCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"/>
                    </Grid.InputBindings>
                    <control:MethodCardControl/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    

    Explanation.
    Judging by the fragments of your code, the property with the EditMethodCommand command is at the same level as the property with the Methods collection, which serves as the source for the ListView. The “source object” of this level is apparently some kind of ViewModel.
    But the data template specifies the visualization of a collection element and its Data Context has a collection element, not a ViewModel.
    Since the Data Template is included in the visual tree of the ListView, an ancestor can be found from it using the ascending binding "{Binding ..., RelativeSource={RelativeSource FindAncestor,...}". After finding the ancestor, we can get its Data Context Path=DataContext, and in it find the command-property "{Binding DataContext.EditMethodCommand, RelativeSource=...}".
    You can also try searching by element name instead of a pop-up anchor. This is easier to write into XAML, but does not always work from styles and templates.

    This is not the only way to solve such problems.

    1. In addition to it, RoutedComman is widely used. In this case, the Data Template will have a binding to the RoutedCommand. But it will only cause the command to bubble up. And at the level where the required command is in the Data Context, the pop-up command “CommandBinding” will be “catched” and redirected to calling the command from the Data Context property.

    2. Another common method is to transfer ViewModel through Resources. This is usually done using locators, which are also very widely used.