基本理解:带控件参数的绑定方法

时间:2016-09-28 14:16:28

标签: c# wpf data-binding listbox command

我有一个ListBox窗口绑定到List<MyThing>的实例。课程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>

DataContext的{​​{1}}设置为MyWindow的实例,定义如下。 除MyVM之外,此XAML的代码隐藏为空。

这是我的ViewModel类InitializeComponent

MyVM

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

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

  2. 如何将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; } } 方法连接到RunMe构造函数内的命令RunCommand? (我已尝试MyVM,但按钮显示为灰色。我也尝试RunCommand = new RoutedUICommand("RunMe", "RunMe", typeof(MyVm));typeOf(MyWindow),但它仍然是灰色的。)

  3. 假设我的工作正常,点击按钮会调用方法typeof(Window)。如何将RunMeSelectedItem传递到ListBox方法? (我知道我可以将按钮的RunMe更改为DataContext,但之后可能找不到命令,我仍然不知道如何让按钮移交给此选中该方法的项目。)

  4. 我的方法可能存在根本性的错误吗?我仍然试图抓住任何东西(一般的GUI编程,事件/命令,WPF,MVVM模式 - 它对我来说都是新的),所以如果你有更好的方法,请告诉我。

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

2 个答案:

答案 0 :(得分:2)

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

那不是&#34;正确的方式&#34;,但有时它还不错。我们并不总是需要建造帕台农神庙。有时我们只需要一个防水布来防止木柴下雨。

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

答案 1 :(得分:1)

  
      
  1. 为什么我不能简单地将按钮绑定到任何单击时调用的METHOD?为什么它必须是一个命令,有什么好处?
  2.   

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

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

话虽这么说,这只是一种模式而你 没有使用它。请查看this question以获取有关Caliburn.Micro的示例,它似乎不使用命令。

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

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

  
      
  1. 假设我的工作正常,点击按钮会调用方法RunMe。如何将SelectedItem从ListBox传递给RunMe方法?
  2.   

在MVVM中,思考数据,而不是视图 如果需要实时修改List(或ObservableCollection,则应该在viewmodel中包含ListBox的ItemsSource应在XAML中绑定的项目。 同样,您需要在viewmodel中使用CurrentItem属性,ListBox的SelectedItem将绑定到该属性。

<ListBox ItemsSource={Binding MyList} SelectedItem={Binding CurrentItem}/>

  
      
  1. 我的方法可能存在根本性的错误吗?
  2. 我仍然想要抓住任何东西   

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