【问题标题】:basic understanding: binding methods with arguments to controls基本理解:将带有参数的方法绑定到控件
【发布时间】:2016-09-28 14:16:28
【问题描述】:

我有一个带有ListBox 的窗口,它绑定到List<MyThing> 的一个实例。类MyThing 包含一个属性Name,它显示在ListBox 中。

我也有一个Button,我想在点击的时候运行下面的方法:

void RunMe(MyThing m) { ... }

我想使用ListBox 中的SelectedItem 作为此方法的参数。

我该怎么做?

这是我的 MyWindow 类的 XAML 文件:

<Window x:Class="MyProject.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <ListBox x:Name="lb" ItemsSource="{Binding MyThingList}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Content="Run command!" Command="{Binding RunCommand}"/>
    </StackPanel>
</Window>

MyWindowDataContext 设置为MyVM 的实例,定义如下。 此 XAML 的代码隐藏除 InitializeComponent 外为空。

这是我的 ViewModel 类MyVM

public class MyVM
{
    public IList<MyThing> MyThingList { get; private set; }

    public MyVM(IList<MyThing> aThingList)
    {
        MyThingList = aThingList;
        RunCommand = // somehow connect it to method "RunMe"
    }

    public void RunMe(MyThing m) { ... }

    public ICommand RunCommand { get; private set; }
}

正如各位经验丰富的人所见,我真的不知道自己在做什么。所以这里有一些具体的问题:

  1. 为什么我不能简单地将按钮绑定到任何在我单击时调用的 METHOD?为什么必须是命令,有什么好处? (我知道一个命令可以自动跟踪它的可执行性,当它无法执行时将按钮变灰,但现在我不关心这个。我也知道它可以绑定到一个事件,但似乎此事件必须在代码隐藏中定义,我想保留为空。如果我错了,请纠正我。)

  2. 如何将我的RunMe 方法连接到MyVM 的构造函数中的命令RunCommand? (我试过RunCommand = new RoutedUICommand("RunMe", "RunMe", typeof(MyVm));,但按钮是灰色的。我也试过typeOf(MyWindow)typeof(Window),但它仍然是灰色的。)

  3. 假设我得到了这个工作,因此单击按钮会导致调用方法RunMe。如何将SelectedItemListBox 交给RunMe 方法? (我知道我可以把按钮的DataContext改成DataContext="{Binding Path=SelectedItem, ElementName=lb}",但是这样就可能找不到命令了,我还是不知道怎么让按钮把这个选中的项交给方法。)

  4. 我的方法可能存在根本问题吗?我仍在尝试掌握任何东西(一般的 GUI 编程、事件/命令、WPF、MVVM 模式——这对我来说都是新的),所以如果你有更好的方法,请告诉我。

PS:请随意将标题更改为更具表现力的东西。我有很多问题,很难确定一个...

【问题讨论】:

  • "为什么我不能简单地将按钮绑定到任何我点击时调用的 METHOD?"因为ICommand 更加通用和强大。如果我是你,我会从某个地方窃取 DelegateCommand 实现并使用它。 MS Prism has one。 WPF 应该附带一个开箱即用的 DelegateCommand,恕我直言。这是他们真正的疏忽。
  • 您通过将CommandParameter 绑定到Button.CommandParameter 属性将其传递给命令。轻松愉快。
  • 这就是构建 MVVM 框架的原因。如果您已经在使用一个 - 检查它的文档,您可能会找到满足您需求的东西。如果不使用 Caliburn.Micro 操作:caliburnmicro.codeplex.com/wikipage?title=All%20About%20Actions - 它们可以满足您的所有需求。

标签: c# wpf data-binding listbox command


【解决方案1】:

ICommand 比简单的方法调用要灵活和强大得多。如果您确实想要一个简单的方法调用(嗯,有点简单...... ish),您可以处理 Button 的 Click 事件:

protected void RunCommandButton_Click(object sender, RoutedEventArgs args)
{
    var vm = (MyVM)DataContext;
    var selectedThing = lb.SelectedItem as MyThing;

    if (selectedThing != null)
    {
        vm.RunMe(selectedThing);
    }
}

这不是“正确的方法”,但有时它已经足够了。我们并不总是需要建造帕台农神庙。有时我们只需要一块防水布来挡住柴火上的雨水。

如果您正在使用命令(并且您应该学习使用命令),您应该使用DelegateCommand 来实现您的命令。 WPF 应该在开箱即用的情况下包含这样的东西,但不幸的是他们没有。他们提供的东西很难自己弄清楚。 MS Prism has one,虽然我没用过。下面我包含了一个非常简单的非通用版本。

private DelegateCommand _runCommand;
public ICommand RunCommand {
    get {
        if (_runCommand == null)
        {
            _runCommand = new DelegateCommand(RunCommandExecute, ExecuteCanExecute);
        }
        return _runCommand;
    }
}

protected void RunCommandExecute(Object parameter)
{
    //  Do stuff
}

protected bool RunCommandCanExecute(Object parameter)
{
    //  Return true if command can be executed
    return parameter != null;
}

像这样绑定:

<Button 
    Content="Run command!" 
    Command="{Binding RunCommand}"
    CommandParameter="{Binding SelectedItem, ElementName=lb}"
    />

另外,为您的列表项使用ObservableCollection&lt;T&gt; 而不是List&lt;T&gt;。这样,当您添加和删除列表项时,列表框将自动更新。

DelegateCommand.cs

using System;
using System.Windows.Input;

namespace HollowEarth.MVVM
{
    public class DelegateCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;

        public event EventHandler CanExecuteChanged;

        #region Constructors
        public DelegateCommand(Action<object> execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action execute)
            : this(o => execute(), null)
        {
        }

        public DelegateCommand(Action execute, Func<bool> canExecute)
        {
            _execute = o => execute();
            _canExecute = o => canExecute();
        }

        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion Constructors

        public virtual bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute(parameter);
        }

        public virtual void Execute(object parameter)
        {
            _execute(parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }
    }
}

【讨论】:

    【解决方案2】:
    1. 为什么我不能简单地将按钮绑定到在我单击时调用的任何 METHOD?为什么必须是命令,有什么好处?

    Command 模式主要用于 MVVM,因为正如您所说,它允许视图和视图模型共享信息,例如 CanExecute 类型的方法;您可以使用其Click 事件将按钮“绑定”到任何方法,而无需使用此模式。

    此外,命令是视图和视图模型之间的抽象;如果您可以使用任何方法,那么您的视图可以...好吧,使用您的视图模型公开的任何方法。

    话虽如此,这只是一种模式,您没有可以使用它。查看this question 中有关 Caliburn.Micro 的示例,它似乎不使用命令。

    1. 如何将我的 RunMe 方法连接到 MyVM 构造函数中的命令 RunCommand?

    我们需要您的 ICommand 实现来回答这个问题。

    1. 假设我得到了这个工作,因此单击按钮会导致调用方法 RunMe。如何将 ListBox 中的 SelectedItem 交给 RunMe 方法?

    在 MVVM 中,考虑数据,而不是视图。
    您的视图模型中应该有一个 List(或 ObservableCollection,如果它需要实时修改),其中包含您的 ListBox 的 ItemsSource 应该在 XAML 中绑定到的项目。 同样,您的视图模型中需要一个 CurrentItem 属性,ListBox 的 SelectedItem 将绑定到该属性。

    &lt;ListBox ItemsSource={Binding MyList} SelectedItem={Binding CurrentItem}/&gt;

    1. 我的方法是否存在根本性的问题?我仍在尝试掌握任何东西

    本身并没有错,WPF 和 MVVM 是一个陡峭的学习。您需要一些时间才能适应它们。

    【讨论】:

    • 只是关于“Caliburn.Micro”示例的一点:框架仍然使用命令模型,它只是根据命名约定动态创建命令和绑定。
    猜你喜欢
    • 1970-01-01
    • 2016-05-06
    • 2018-05-27
    • 1970-01-01
    • 2016-06-27
    • 2012-02-12
    • 2010-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多