c# - 基本理解: binding methods with arguments to controls

标签 c# wpf data-binding listbox command

我有一个带有 ListBox 的窗口绑定(bind)到 List<MyThing> 的实例.类(class)MyThing包含属性 Name显示在 ListBox 中.

我还有一个 Button ,我想在单击时运行以下方法:

void RunMe(MyThing m) { ... }

作为此方法的参数,我想使用 SelectedItem来自 ListBox .

我该怎么做?

这是我的 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>

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

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

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

  3. 假设我让这个工作正常,点击按钮会导致调用方法 RunMe .我如何递交 SelectedItem来自 ListBoxRunMe方法? (我知道我可以将按钮的 DataContext 更改为 DataContext="{Binding Path=SelectedItem, ElementName=lb}" ,但是这样可能找不到命令,我仍然不知道如何让按钮将这个选中的项目交给方法。)

  4. 我的方法是否存在根本性错误?我仍在努力掌握任何东西(一般的 GUI 编程、事件/命令、WPF、MVVM 模式——同时对我来说都是新的),所以如果你有更好的方法,请告诉我。

PS:请随意将标题更改为更具表现力的内容。我有很多问题,很难将其归结为一个...

最佳答案

ICommand远比简单的方法调用灵活和强大。如果你确实想要一个简单的方法调用(好吧,有点简单......大概),你可以只处理 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);
    }
}

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

如果你正在使用命令(你应该学会使用命令),你应该使用 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;
}

像这样绑定(bind):

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

此外,使用 ObservableCollection<T>对于您的列表项而不是 List<T> .这样,当您添加和删除列表项时,列表框将自动更新。

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);
        }
    }
}

关于c# - 基本理解: binding methods with arguments to controls,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39750141/

相关文章:

c# - 静态关键字、状态/实例变量和线程安全

c# - BigInteger 不能表示无穷大

播放 YouTube 的 C# LibVLCSharp 问题

c# - 如何调整 NotifyIcon 的大小?

java - 使用 Spring MVC 的绑定(bind)问题

javascript - 如何在回发中显示面板?

c# - 在对话框的构造函数中调用 .Show() 或 .ShowDialog 是一种好习惯

c# - C#中如何显示当前时间和日期

data-binding - F#:使用 INotifyPropertyChanged 进行数据绑定(bind)

mysql - join、外键等关系型DB规划