在使用MVVM一个WPF应用程序,我有一个列表视图项的用户控件。 在运行时,它会使用数据绑定与对象的集合,以填补列表视图。
什么是附加一个双击事件在列表视图中的项目正确的方法,这样,当在列表视图中的项目doubleclicked,在视图模型中的相应事件被触发,并点击该项目的引用?
如何能在一个干净的MVVM方式进行,即在查看后面没有代码?
在使用MVVM一个WPF应用程序,我有一个列表视图项的用户控件。 在运行时,它会使用数据绑定与对象的集合,以填补列表视图。
什么是附加一个双击事件在列表视图中的项目正确的方法,这样,当在列表视图中的项目doubleclicked,在视图模型中的相应事件被触发,并点击该项目的引用?
如何能在一个干净的MVVM方式进行,即在查看后面没有代码?
请后面的代码是不是一件坏事的。 不幸的是,相当多的人在WPF社区得到了这个错误。
MVVM不是消除后面的代码的模式。 它是从逻辑部分(工作流)的视图部分(外观,动画等)分离。 此外,您还能够单元测试的逻辑部分。
我知道足够的情况下,您必须后面写的代码,因为数据绑定是不是解决一切问题。 在你的情况我会处理的代码隐藏文件DoubleClick事件,并委托该调用视图模型。
使用代码,同时仍能满足MVVM分离样本应用程序可以在这里找到:
WPF应用程序框架(WAF) - http://waf.codeplex.com
我能够得到这个与.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>
我喜欢用有附加命令行为和命令。 马龙格列奇有一个很好的实现了附加命令行为。 通过这些,我们可以再指定一个样式的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工具包 。
我发现了一个非常简单和干净的方式与混合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>
我认识到,这种讨论一岁,但与.NET 4,是否有这个解决方案有什么想法? 我绝对同意MVVM点是不是要消除隐藏文件代码。 我也感觉很强烈,只是因为事情是复杂的,并不意味着它的更好。 以下是我放在后面的代码:
private void ButtonClick(object sender, RoutedEventArgs e)
{
dynamic viewModel = DataContext;
viewModel.ButtonClick(sender, e);
}
你可以使用卡利的动作功能的事件映射到您的视图模型的方法。 假设你有一个ItemActivated
你的方法ViewModel
,那么相应的XAML会是什么样子:
<ListView x:Name="list"
Message.Attach="[Event MouseDoubleClick] = [Action ItemActivated(list.SelectedItem)]" >
欲了解更多详情您可以检查卡利的文档和示例。
我发现在创建视图时,它简单的链接命令:
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();
}
虽然方法应该与任何方法工作,你必须开辟新的意见。
我看到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可以避开宣布,在刚刚一个位置。
我希望这可以帮助别人!