在使用MVVM的WPF应用程序中,我有一个带有listview项的usercontrol.在运行时,它将使用数据绑定来用列表视图填充对象集合.
将双击事件附加到列表视图中的项目的正确方法是什么,以便在列表视图中的项目被双击时,视图模型中的相应事件被触发并且引用了单击的项目?
如何以干净的MVVM方式完成,即视图中没有代码?
jbe*_*jbe 77
请注意,代码背后并不是件坏事.不幸的是,WPF社区中有很多人弄错了.
MVVM不是消除背后代码的模式.它是将视图部分(外观,动画等)与逻辑部分(工作流)分开.此外,您还可以对逻辑部件进行单元测试.
我知道你必须编写代码的足够场景,因为数据绑定不是解决所有问题的方法.在您的方案中,我将在代码隐藏文件中处理DoubleClick事件,并将此调用委托给ViewModel.
可以在此处找到使用代码并仍然实现MVVM分离的示例应用程序:
WPF应用程序框架(WAF) - http://waf.codeplex.com
Rus*_*uan 71
我能够使用.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>
Run Code Online (Sandbox Code Playgroud)
rmo*_*ore 44
我喜欢使用附加命令行为和命令.Marlon Grech非常好地实现了附加命令行为.使用这些,我们可以为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>
Run Code Online (Sandbox Code Playgroud)
对于命令,您可以直接实现ICommand,也可以使用MVVM Toolkit中的一些帮助程序.
Gun*_*ter 13
我发现使用Blend SDK事件触发器执行此操作非常简单明了.清理MVVM,可重用,无代码隐藏.
你可能已经有这样的事情了:
<Style x:Key="MyListStyle" TargetType="{x:Type ListViewItem}">
Run Code Online (Sandbox Code Playgroud)
现在为ListViewItem包含一个ControlTemplate,如果你还没有使用它:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<GridViewRowPresenter Content="{TemplateBinding Content}"
Columns="{TemplateBinding GridView.ColumnCollection}" />
</ControlTemplate>
</Setter.Value>
</Setter>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
如果你有GridRowPresenter"以上"的视觉元素(probalby从网格开始),你也可以将Trigger放在那里.
不幸的是,MouseDoubleClick事件不是从每个可视元素生成的(它们来自Controls,但不是来自FrameworkElements).解决方法是从EventTrigger派生一个类,并查找ClickCount为2的MouseButtonEventArgs.这有效地过滤掉所有非MouseButtonEvents和所有带有ClickCount!= 2的MoseButtonEvents.
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以写这个('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>
Run Code Online (Sandbox Code Playgroud)
小智 6
我意识到这个讨论已经有一年了,但是对于.NET 4,对这个解决方案有什么想法吗?我绝对同意MVVM的观点并不是要消除文件背后的代码.我也非常强烈地感到,只是因为事情很复杂,并不意味着它更好.以下是我在后面的代码中添加的内容:
private void ButtonClick(object sender, RoutedEventArgs e)
{
dynamic viewModel = DataContext;
viewModel.ButtonClick(sender, e);
}
Run Code Online (Sandbox Code Playgroud)