WPF:如何使用MVVM将命令绑定到ListBoxItem?

Bor*_*ris 26 c# wpf binding mvvm commandbinding

我刚开始学习MVVM.我通过遵循这个MVVM教程从头开始制作应用程序(我强烈推荐给所有MVVM初学者).基本上,我到目前为止创建的是一些文本框,其中用户添加他或她的数据,一个按钮来保存该数据,随后使用所有条目填充ListBox.

这就是我陷入困境的地方:我希望能够双击ListBoxItem并触发我创建并添加到ViewModel的命令.我不知道如何完成XAML方面,即我不知道如何将该命令绑定到ListBox(Item).

这是XAML:

...
<ListBox 
    Name="EntriesListBox" 
    Width="228" 
    Height="208" 
    Margin="138,12,0,0" 
    HorizontalAlignment="Left" 
    VerticalAlignment="Top" 
    ItemsSource="{Binding Entries}" />
...
Run Code Online (Sandbox Code Playgroud)

这是ViewModel:

public class MainWindowViewModel : DependencyObject
{
    ...
    public IEntriesProvider Entries
    {
        get { return entries; }
    }

    private IEntriesProvider entries;
    public OpenEntryCommand OpenEntryCmd { get; set; }

    public MainWindowViewModel(IEntriesProvider source)
    {
        this.entries = source;
        ...
        this.OpenEntryCmd = new OpenEntryCommand(this);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

最后,这是我想在用户双击EntriesListBox中的项目后执行的OpenEntryCommand:

public class OpenEntryCommand : ICommand
{
    private MainWindowViewModel viewModel;

    public OpenEntryCommand(MainWindowViewModel viewModel)
    {
        this.viewModel = viewModel;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter)
    {
        return parameter is Entry;
    }

    public void Execute(object parameter)
    {
        string messageFormat = "Subject: {0}\nStart: {1}\nEnd: {2}";
        Entry entry = parameter as Entry;
        string message = string.Format(messageFormat, 
                                       entry.Subject, 
                                       entry.StartDate.ToShortDateString(), 
                                       entry.EndDate.ToShortDateString());

        MessageBox.Show(message, "Appointment");
    }
}
Run Code Online (Sandbox Code Playgroud)

请帮忙,我很感激.

Abd*_*men 65

不幸的是,只有ButtonBase派生控件才有可能将ICommand对象绑定到它们的Command属性(对于Click事件).

但是,您可以使用混合到(在你的情况一样映射的事件提供了一个API MouseDoubleClickListBox)一个ICommand对象.

<ListBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseDoubleClick">
            <i:InvokeCommandAction Command="{Binding YourCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>
Run Code Online (Sandbox Code Playgroud)

你必须定义:xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"并引用System.Windows.Interactivity.dll.

- 编辑 - 这是WPF4的一部分,但如果您不使用WPF4,则可以使用Microsoft.Windows.Interactivity.这个dll来自Blend SDK,它不需要Blend,来自:http://www.microsoft.com/downloads/en/details.aspx? FamilyID = f1ae9a30-4928-411d-970b-e682ab179e17&displaylang = en

更新:我找到了一些可以帮到你的东西.检查MVVM Light Toolkit上的这个链接,其中包含有关如何执行此操作的演练,以及指向所需库的链接.MVVM Light Toolkit是一个非常有趣的框架,用于将MVVM应用于Silverlight,WPF和WP7.

希望这可以帮助 :)

  • -1:@AbdouMoumen:虽然这个答案基本上没问题,但它有一个棘手的问题:它将mouse2click绑定到ListBox,而不是ListBoxItem.这意味着如果ListBox显示其内部**滚动条**,则双击此类滚动条**将触发此命令**.通常,这是非常不受欢迎的 (9认同)

Pau*_*ell 7

由于DoubleClick事件,这很棘手.有几种方法可以做到这一点:

  1. 处理后面的代码中的双击事件,然后在ViewModel上手动调用命令/方法
  2. 使用附加行为将DoubleClick事件路由到您的Command
  3. 使用"混合行为" 将DoubleClick事件映射到您的命令

2和3可能更纯粹,但坦率地说,1更容易,更简单,而不是世界上最糟糕的事情.对于一次性案例,我可能会使用方法#1.

现在,如果您更改了要求使用每个项目的超链接,那么这将更容易.首先命名XAML中的根元素 - 例如,对于一个Window:

<Window .... Name="This">
Run Code Online (Sandbox Code Playgroud)

现在,在ListBox项的DataTemplate中,使用以下内容:

<ListBox ...>
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Hyperlink 
        Command="{Binding ElementName=This, Path=DataContext.OpenEntryCmd}"
        Text="{Binding Path=Name}" 
        />
Run Code Online (Sandbox Code Playgroud)

ElementName绑定允许您从ViewModel的上下文中解析OpenEntryCmd,而不是特定的数据项.

  • Abdou得分较高的答案有一个非常重要的问题.从那里复制注释没有意义,我只是说将命令绑定到LIST与将它绑定到每个ITEM完全不同.这种变化具有从"仅处理点击"的观点看不到的重要后果.datacontext更改和"双击滚动条"可能是最容易发现的.另一方面,Paul描述的所有三种方法都没有这些问题,因为它们将命令绑定到适当的目标.简而言之,他们是正确的,Abdou不是. (4认同)

Sha*_*han 5

编辑:我作为一个没有经验的 WPF 开发人员写了这篇文章,现在我要么使用提供事件到命令绑定的框架,要么简单地使用按钮并重新设计它的样式。当然,为了获得最大的灵活性,这可能更好。

我发现最好的方法是为我的内容创建一个简单的用户控件包装器,并使用命令和参数的依赖属性。

我这样做的原因是因为按钮没有将单击事件冒泡到我的列表框,这阻止了它选择列表框项。

CommandControl.xaml.cs:

public partial class CommandControl : UserControl
{
    public CommandControl()
    {
        MouseLeftButtonDown += OnMouseLeftButtonDown;
        InitializeComponent();
    }

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        if (Command != null)
        {
            if (Command.CanExecute(CommandParameter))
            {
                Command.Execute(CommandParameter);
            }
        }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand),
            typeof(CommandControl),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object),
            typeof(CommandControl),
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None));

    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }
}
Run Code Online (Sandbox Code Playgroud)

命令控制.xaml:

<UserControl x:Class="WpfApp.UserControls.CommandControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         Background="Transparent">
</UserControl>
Run Code Online (Sandbox Code Playgroud)

用法:

<ListBoxItem>
    <uc:CommandControl Command="{Binding LoadPageCommand}"
                       CommandParameter="{Binding HomePageViewModel}">
        <TextBlock Text="Home" Margin="0,0,0,5" VerticalAlignment="Center"
                   Foreground="White" FontSize="24" />
    </uc:CommandControl>
</ListBoxItem>
Run Code Online (Sandbox Code Playgroud)

内容可以是任何内容,当单击该控件时,它将执行该命令。

编辑:添加Background="Transparent"到 UserControl 以在控件的整个区域上启用单击事件。