使用MVVM一个WPF ListView项射击双击事件使用MVVM一个WPF ListView项射击

2019-05-13 16:11发布

在使用MVVM一个WPF应用程序,我有一个列表视图项的用户控件。 在运行时,它会使用数据绑定与对象的集合,以填补列表视图。

什么是附加一个双击事件在列表视图中的项目正确的方法,这样,当在列表视图中的项目doubleclicked,在视图模型中的相应事件被触发,并点击该项目的引用?

如何能在一个干净的MVVM方式进行,即在查看后面没有代码?

Answer 1:

请后面的代码是不是一件坏事的。 不幸的是,相当多的人在WPF社区得到了这个错误。

MVVM不是消除后面的代码的模式。 它是从逻辑部分(工作流)的视图部分(外观,动画等)分离。 此外,您还能够单元测试的逻辑部分。

我知道足够的情况下,您必须后面写的代码,因为数据绑定是不是解决一切问题。 在你的情况我会处理的代码隐藏文件DoubleClick事件,并委托该调用视图模型。

使用代码,同时仍能满足MVVM分离样本应用程序可以在这里找到:

WPF应用程序框架(WAF) - http://waf.codeplex.com



Answer 2:

我能够得到这个与.NET 4.5的工作。 似乎直线前进,并没有第三方或后面的代码需要。

<ListView ItemsSource="{Binding Data}">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Margin="2">
                    <Grid.InputBindings>
                        <MouseBinding Gesture="LeftDoubleClick" Command="{Binding ShowDetailCommand}"/>
                    </Grid.InputBindings>
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Image Source="..\images\48.png" Width="48" Height="48"/>
                    <TextBlock Grid.Row="1" Text="{Binding Name}" />
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>


Answer 3:

我喜欢用有附加命令行为和命令。 马龙格列奇有一个很好的实现了附加命令行为。 通过这些,我们可以再指定一个样式的ListView的ItemContainerStyle属性将为每个ListViewItem的设置命令。

在这里,我们设置MouseDoubleClick事件被解雇的命令,和CommandParameter,将是我们点击数据对象。 在这里我旅行了可视树得到的是我使用的命令,但你可以很容易地创建应用程序广泛的命令。

<Style x:Key="Local_OpenEntityStyle"
       TargetType="{x:Type ListViewItem}">
    <Setter Property="acb:CommandBehavior.Event"
            Value="MouseDoubleClick" />
    <Setter Property="acb:CommandBehavior.Command"
            Value="{Binding ElementName=uiEntityListDisplay, Path=DataContext.OpenEntityCommand}" />
    <Setter Property="acb:CommandBehavior.CommandParameter"
            Value="{Binding}" />
</Style>

对于这些命令,你可以实现一个ICommand的直接,或使用一些像那些进来的佣工的MVVM工具包 。



Answer 4:

我发现了一个非常简单和干净的方式与混合SDK事件触发器来做到这一点。 干净的MVVM,可重用性和无代码隐藏。

你可能已经有这样的事情:

<Style x:Key="MyListStyle" TargetType="{x:Type ListViewItem}">

现在,包括像这样的,如果你还没有使用一个ListViewItem的一个控件模板:

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}" />
    </ControlTemplate>
  </Setter.Value>
 </Setter>

所述GridViewRowPresenter将是“内部”的所有元素组成的列表的行元素的视觉根。 现在,我们可以插入一个触发那里寻找MouseDoubleClick路由事件,并通过调用InvokeCommandAction的命令是这样的:

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}">
        <i:Interaction.Triggers>
          <i:EventTrigger EventName="MouseDoubleClick">
            <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" />
          </i:EventTrigger>
        </i:Interaction.Triggers>
      </GridViewRowPresenter>
    </ControlTemplate>
  </Setter.Value>
 </Setter>

如果你有视觉元素,“上面” GridRowPresenter(probalby的电力网开始),你也可以把触发那里。

不幸的是MouseDoubleClick事件不会从每一个视觉元素(它们是从控制,但不能从FrameworkElements例如)生成。 一种解决方法是从EventTrigger派生类,寻找MouseButtonEventArgs与这2的clickCount有效地过滤掉所有的非MouseButtonEvents和所有MoseButtonEvents与ClickCount的!= 2。

class DoubleClickEventTrigger : EventTrigger
{
    protected override void OnEvent(EventArgs eventArgs)
    {
        var e = eventArgs as MouseButtonEventArgs;
        if (e == null)
        {
            return;
        }
        if (e.ClickCount == 2)
        {
            base.OnEvent(eventArgs);
        }
    }
}

现在,我们可以写这个(“H”是辅助类的命名空间上图):

<Setter Property="Template">
  <Setter.Value>
    <ControlTemplate TargetType="{x:Type ListViewItem}">
      <GridViewRowPresenter Content="{TemplateBinding Content}"
                            Columns="{TemplateBinding GridView.ColumnCollection}">
        <i:Interaction.Triggers>
          <h:DoubleClickEventTrigger EventName="MouseDown">
            <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" />
          </h:DoubleClickEventTrigger>
        </i:Interaction.Triggers>
      </GridViewRowPresenter>
    </ControlTemplate>
  </Setter.Value>
 </Setter>


Answer 5:

我认识到,这种讨论一岁,但与.NET 4,是否有这个解决方案有什么想法? 我绝对同意MVVM点是不是要消除隐藏文件代码。 我也感觉很强烈,只是因为事情是复杂的,并不意味着它的更好。 以下是我放在后面的代码:

    private void ButtonClick(object sender, RoutedEventArgs e)
    {
        dynamic viewModel = DataContext;
        viewModel.ButtonClick(sender, e);
    }


Answer 6:

你可以使用卡利的动作功能的事件映射到您的视图模型的方法。 假设你有一个ItemActivated你的方法ViewModel ,那么相应的XAML会是什么样子:

<ListView x:Name="list" 
   Message.Attach="[Event MouseDoubleClick] = [Action ItemActivated(list.SelectedItem)]" >

欲了解更多详情您可以检查卡利的文档和示例。



Answer 7:

我发现在创建视图时,它简单的链接命令:

var r = new MyView();
r.MouseDoubleClick += (s, ev) => ViewModel.MyCommand.Execute(null);
BindAndShow(r, ViewModel);

在我的情况BindAndShow看起来像这样(updatecontrols + avalondock):

private void BindAndShow(DockableContent view, object viewModel)
{
    view.DataContext = ForView.Wrap(viewModel);
    view.ShowAsDocument(dockManager);
    view.Focus();
}

虽然方法应该与任何方法工作,你必须开辟新的意见。



Answer 8:

我看到rushui与InuptBindings的解决方案,但我仍然无法命中那里是没有文字的ListViewItem的区域-甚至是背景设置为透明之后,所以我用不同的模板解决它。

这个模板是当一个ListViewItem已选定并激活:

<ControlTemplate x:Key="SelectedActiveTemplate" TargetType="{x:Type ListViewItem}">
   <Border Background="LightBlue" HorizontalAlignment="Stretch">
   <!-- Bind the double click to a command in the parent view model -->
      <Border.InputBindings>
         <MouseBinding Gesture="LeftDoubleClick" 
                       Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ItemSelectedCommand}"
                       CommandParameter="{Binding}" />
      </Border.InputBindings>
      <TextBlock Text="{Binding TextToShow}" />
   </Border>
</ControlTemplate>

这个模板是当一个ListViewItem已被选定为与处于非活动状态:

<ControlTemplate x:Key="SelectedInactiveTemplate" TargetType="{x:Type ListViewItem}">
   <Border Background="Lavender" HorizontalAlignment="Stretch">
      <TextBlock Text="{Binding TextToShow}" />
   </Border>
</ControlTemplate>

这是用于ListViewItem的默认样式:

<Style TargetType="{x:Type ListViewItem}">
   <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate>
            <Border HorizontalAlignment="Stretch">
               <TextBlock Text="{Binding TextToShow}" />
            </Border>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
   <Style.Triggers>
      <MultiTrigger>
         <MultiTrigger.Conditions>
            <Condition Property="IsSelected" Value="True" />
            <Condition Property="Selector.IsSelectionActive" Value="True" />
         </MultiTrigger.Conditions>
         <Setter Property="Template" Value="{StaticResource SelectedActiveTemplate}" />
      </MultiTrigger>
      <MultiTrigger>
         <MultiTrigger.Conditions>
            <Condition Property="IsSelected" Value="True" />
            <Condition Property="Selector.IsSelectionActive" Value="False" />
         </MultiTrigger.Conditions>
         <Setter Property="Template" Value="{StaticResource SelectedInactiveTemplate}" />
      </MultiTrigger>
   </Style.Triggers>
</Style>

我不喜欢的是TextBlock的重复和文字结合,我不知道II可以避开宣布,在刚刚一个位置。

我希望这可以帮助别人!



文章来源: Firing a double click event from a WPF ListView item using MVVM
标签: wpf mvvm