为什么列表框模板中的绑定命令无法运行?

时间:2016-10-02 17:32:39

标签: c# wpf xaml mvvm

为什么列表框中的按钮无法运行绑定命令? 我将此命令绑定为简单按钮(不是模板),并且它完美地工作。 Main.xaml

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication2"
    mc:Ignorable="d"
    Title="MainWindow" Height="800" Width="525">
<Window.DataContext>
    <local:ViewModel/>
</Window.DataContext>
<Window.Resources>
    <DataTemplate x:Key="lbTemp">
        <StackPanel>
            <TextBlock Height="50" Text="{Binding}"/>
            <Button Height="20" Content="click" Command="{Binding Path=TestCommand}" CommandParameter="hello"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <ListBox x:Name="listBox" Width="500" Height="300" ItemTemplate="{StaticResource lbTemp}" ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.MyData}"/>
    <Button Command="{Binding Path=TestCommand}" CommandParameter="hello" Width="200" Height="40"/>
</Grid>

ViewModel.cs

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel() { }

    public ObservableCollection<string> MyData {
        get
        {
            return _MyData;
        }
        set
        {
            if (!_MyData.SequenceEqual(value))
            {
                _MyData = value;
            }
            OnPropertyChanged();
        }
    }
    private ObservableCollection<string> _MyData = new ObservableCollection<string>();

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string caller="")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
    }

    private ICommand _testCommand;
    public ICommand TestCommand
    {
        get
        {
            return _testCommand;
        }
        set
        {
            if (_testCommand != value)
            {
                _testCommand = value;
                OnPropertyChanged();
            }
        }
    }
}

和Command.cs

public class RelayCommand : ICommand
{
    public RelayCommand(Action action, Func<object, bool> canExecutePredicate)
    {
        if (action == null || canExecutePredicate == null)
            throw new ArgumentNullException("Can't be null");
        this._action = action;
        this._canExecutePredicate = canExecutePredicate;
    }

    public RelayCommand(Action action) : this(action, (obj) => true) { }

    Action _action;
    Func<object, bool> _canExecutePredicate;

    public event EventHandler CanExecuteChanged;
    protected virtual void OnCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

    public bool CanExecute(object parameter)
    {
        return _canExecutePredicate(parameter);
    }

    public void Execute(object parameter)
    {
        _action?.Invoke();
    }
}

您能说出此解决方案的有效xaml示例吗?

2 个答案:

答案 0 :(得分:2)

两个DataContext的{​​{1}}不同。让我们看一下Button视图中的一些元素。

  • DataContext的{​​{1}}是您的Window课程。
  • DataContext的{​​{1}}与ViewModel相同。我们不需要在您设置的ListBox绑定中使用DataContext
  • Window之外的RelativeSource也与ItemsSource具有相同的Button。这就是为什么这个DataTemplate绑定工作正常。
  • DataContext中的Window与您在Command课程中创建的Button集合中代表的特定项目有DataTemplate。重要的是,{em>不 DataContext类本身。

这是我 使用MyData的地方。

ViewModel

如果这不适合你,请告诉我。

答案 1 :(得分:1)

我认为这是因为TestCommand是ViewModel上的属性 - 而不是MyData集合的元素(字符串)。

你必须上课:

class MyItemClass : INotifyPropertyChanged
{
  public string Text {get;set;}
   private ICommand _testCommand;
    public ICommand TestCommand
    {
        get
        {
            return _testCommand;
        }
        set
        {
            if (_testCommand != value)
            {
                _testCommand = value;
                OnPropertyChanged();
            }
        }
    }

   public event PropertyChangedEventHandler PropertyChanged;
   protected virtual void OnPropertyChanged([CallerMemberName]string caller="")
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
    }
  }

然后,您应该使用该类型的项填充MyData集合,其中TextBlocks绑定应更改为DataTemplate中的Text。

BTW:你不必在列表框的itemssource绑定中有一个relativesource引用。它从Window继承DataContext,因此仅使用{Binding MyData}就足够了。