(注意 - 这是重新发布的帖子,因为我的第一个问题是在错误的标题下发布的:Here抱歉!)
我有一个标准的WPF树视图,并且绑定项目以查看模型类。
我现在希望在双击项目时处理行为(打开文档visual-studio-style)。
我可以在树视图(显示xaml)的控件中触发事件处理程序,但是如何绑定视图模型类上的特定行为 - 例如ProjectViewModel?
优先使用ICommand-implementationmenter,因为这在别处使用...
<TreeView ItemsSource="{Binding Projects}" MouseDoubleClick="TreeView_MouseDoubleClick">
<TreeView.ItemContainerStyle>
<!--
This Style binds a TreeViewItem to a TreeViewItemViewModel.
-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type Implementations:ProjectViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" />
<TextBlock Text="{Binding DisplayName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type Implementations:PumpViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Images\State.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type Implementations:PumpDesignViewModel}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="Images\City.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
答案 0 :(得分:53)
稍微更新我的回答。
我为此尝试了很多不同的方法,我仍然觉得附加行为是最好的解决方案。尽管在开始时它可能看起来很多开销但实际上并非如此。我将ICommands
的所有行为保留在同一个地方,每当我需要支持其他事件时,只需复制/粘贴并更改PropertyChangedCallback
中的事件即可。
我还添加了CommandParameter
的可选支持。
在设计师中,只需选择所需的事件
即可
您可以在TreeView
,TreeViewItem
或您喜欢的任何其他地方进行设置。
实施例。将其设置在TreeView
<TreeView commandBehaviors:MouseDoubleClick.Command="{Binding YourCommand}"
commandBehaviors:MouseDoubleClick.CommandParameter="{Binding}"
.../>
实施例。将其设置为TreeViewItem
<TreeView ItemsSource="{Binding Projects}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="commandBehaviors:MouseDoubleClick.Command"
Value="{Binding YourCommand}"/>
<Setter Property="commandBehaviors:MouseDoubleClick.CommandParameter"
Value="{Binding}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
这是附加行为 MouseDoubleClick
public class MouseDoubleClick
{
public static DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command",
typeof(ICommand),
typeof(MouseDoubleClick),
new UIPropertyMetadata(CommandChanged));
public static DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter",
typeof(object),
typeof(MouseDoubleClick),
new UIPropertyMetadata(null));
public static void SetCommand(DependencyObject target, ICommand value)
{
target.SetValue(CommandProperty, value);
}
public static void SetCommandParameter(DependencyObject target, object value)
{
target.SetValue(CommandParameterProperty, value);
}
public static object GetCommandParameter(DependencyObject target)
{
return target.GetValue(CommandParameterProperty);
}
private static void CommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Control control = target as Control;
if (control != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
control.MouseDoubleClick += OnMouseDoubleClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
control.MouseDoubleClick -= OnMouseDoubleClick;
}
}
}
private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
Control control = sender as Control;
ICommand command = (ICommand)control.GetValue(CommandProperty);
object commandParameter = control.GetValue(CommandParameterProperty);
command.Execute(commandParameter);
}
}
答案 1 :(得分:9)
我迟到了,但我只是使用了不同的解决方案。再一次,它可能不是最好的,但这就是我如何做到的。
首先,来自Meleak的前一个答案很酷,但我觉得被迫添加AttachedBehaviors只是为了像MouseDoubleClick这样基本的东西是非常沉重的。这会迫使我在我的应用程序中使用新模式,甚至会使一切变得更复杂。
我的目标是尽可能保持简单。因此我做了一些非常基本的事情(我的例子是针对DataGrid,但你可以在很多不同的控件上使用它):
<DataGrid MouseDoubleClick="DataGrid_MouseDoubleClick">
<!-- ... -->
</DataGrid>
在代码隐藏中:
private void DataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
//Execute the command related to the doubleclick, in my case Edit
(this.DataContext as VmHome).EditAppCommand.Execute(null);
}
为什么我觉得它不会破坏MVVM模式?因为在我看来,你应该在代码隐藏中放置的唯一东西是你的viewModel的桥梁,这是你的UI特有的东西。在这种情况下,它只是说,如果你双击,激活相关的命令。它几乎与Command =“{Binding EditAppCommand}”相同,我只是模拟了这种行为。
请随意向我发表您对此的看法,我很高兴听到一些批评者对这种思维方式的看法,但是现在我相信这是在不破坏MVVM的情况下实施它的最简单方法。
答案 2 :(得分:5)
Meleak和ígor的建议都很棒,但当双击事件处理程序绑定到TreeViewItem
时,将调用此事件处理程序。 item的父元素(不仅仅是单击的元素)。如果不需要,还有另外一个补充:
private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
Control control = sender as Control;
ICommand command = (ICommand)control.GetValue(CommandProperty);
object commandParameter = control.GetValue(CommandParameterProperty);
if (sender is TreeViewItem)
{
if (!((TreeViewItem)sender).IsSelected)
return;
}
if (command.CanExecute(commandParameter))
{
command.Execute(commandParameter);
}
}
答案 3 :(得分:3)
这很简单,这就是我在TreeView上双击的方法:
<Window x:Class="TreeViewWpfApplication.MainWindow"
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
...>
<TreeView ItemsSource="{Binding Departments}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<ei:CallMethodAction MethodName="SomeMethod" TargetObject="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
</Window>
System.Windows.Interactivity.dll 取自 C:\ Program Files(x86)\ Microsoft SDKs \ Expression \ Blend.NETFramework \ v4.0 \ Libraries \ System.Windows.Interactivity.dll 或者NuGet
我的观点模型:
public class TreeViewModel : INotifyPropertyChanged
{
private List<Department> departments;
public TreeViewModel()
{
Departments = new List<Department>()
{
new Department("Department1"),
new Department("Department2"),
new Department("Department3")
};
}
public List<Department> Departments
{
get
{
return departments;
}
set
{
departments = value;
OnPropertyChanged("Departments");
}
}
public void SomeMethod()
{
MessageBox.Show("*****");
}
}
答案 4 :(得分:2)
Meleak解决方案很棒!,但我添加了检查
private static void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
Control control = sender as Control;
ICommand command = (ICommand)control.GetValue(CommandProperty);
object commandParameter = control.GetValue(CommandParameterProperty);
//Check command can execute!!
if(command.CanExecute(commandParameter ))
command.Execute(commandParameter);
}
答案 5 :(得分:0)
我达到的最佳方法是在双向模式下将IsSelected
属性从TreeViewItem绑定到ViewModel,并在属性设置器中实现逻辑。然后,您可以定义在值为true或false时要执行的操作,因为只要用户单击某个项,此属性就会更改。
class MyVM
{
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == null)
return;
_isSelected = vale;
if (_isSelected)
{
// Your logic goes here.
}
else
{
// Your other logic goes here.
}
}
}
这避免了很多代码。
此外,此技术允许您仅在真正需要它的ViewModel中实现“onclick”行为。
答案 6 :(得分:0)
只是为了好奇:如果我接受Frederiks的一部分,但是直接将其作为行为来实现呢?
public class MouseDoubleClickBehavior : Behavior<Control>
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof (ICommand), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof (object), typeof (MouseDoubleClickBehavior), new PropertyMetadata(default(object)));
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseDoubleClick += OnMouseDoubleClick;
}
protected override void OnDetaching()
{
AssociatedObject.MouseDoubleClick -= OnMouseDoubleClick;
base.OnDetaching();
}
void OnMouseDoubleClick(object sender, RoutedEventArgs e)
{
if (Command == null) return;
Command.Execute(/*commandParameter*/null);
}
}
答案 7 :(得分:0)
TextBlock上的鼠标绑定
在View的TreeView.Resources中:
<HierarchicalDataTemplate
DataType="{x:Type treeview:DiscoveryUrlViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="../Images/ic_search.png" />
<TextBlock Text="{Binding DisplayText}" >
<TextBlock.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding DoubleClickCopyCommand}"
CommandParameter="{Binding }" />
</TextBlock.InputBindings>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
在该View的ViewModel中(DiscoveryUrlViewModel.cs):
private RelayCommand _doubleClickCommand;
public ICommand DoubleClickCopyCommand
{
get
{
if (_doubleClickCommand == null)
_doubleClickCommand = new RelayCommand(OnDoubleClick);
return _doubleClickCommand;
}
}
private void OnDoubleClick(object obj)
{
var clickedViewModel = (DiscoveryUrlViewModel)obj;
}