2016-10-02 4 views
0

なぜリストボックスのボタンがバインドされたコマンドを実行できないのですか? 私は単純なボタン(テンプレートではない)のためにこのコマンドをバインドし、それは完全に動作しています。 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

2つのButtonのうちDataContextが異なります。ビュー内のいくつかの要素について、DataContextを見てみましょう。

  • WindowさんDataContextはあなたViewModelクラスです。
  • ListBoxDataContextは、Windowのものと同じです。あなたが設定したItemsSourceバインディングにRelativeSourceを使用する必要はありません。
  • ButtonDataTemplateの外にもWindowと同じDataContextがあります。そのため、このCommandバインディングはうまく動作します。
  • DataTemplateの中のButtonには、ViewModelクラスの中で作成したMyDataコレクションから表す特定のアイテムのDataContextがあります。重要なのは、ではなく、ViewModelクラスそのものです。

ここで、となります。RelativeSourceです。

<Button Height="20" Content="click" Command="{Binding DataContext.TestCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" CommandParameter="hello"/> 

これがうまく行かない場合は、教えてください。

+0

ありがとう!完璧に動作します! – Alexey

1

私は、TestCommandはViewModelのプロパティであり、MyDataコレクションの要素(文字列)ではないからです。

あなたは、クラスを作成する必要があります:あなたはその後、結合のTextBlockをDataTemplateの中のテキストに変更する必要があり、そのタイプのアイテムであなたの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)); 
    } 
    } 

ご参考まで:リストボックスのitemssourceバインディングに親戚の参照を持つ必要はありません。 WindowからDataContextを継承するので、{Binding MyData}を使用するだけで十分です。

+0

これは、wpfデータテンプレートをテストするためのシンプルなテストアプリです。 MyDataを埋め込むために、私はこのコードを使用します: Environment.GetEnvironmentVariables()。Values.Cast ().ToList(); MyData = new System.Collections.ObjectModel。ObservableCollection (GetCollection()); And: TestCommand = new RelayCommand(()=> MessageBox.Show( "test command")、(obj)=> true); – Alexey

+0

しかし、各要素のボタンが別のオブジェクトのコマンドをアクティブにするので、ビューモデルの考え方に違反します。これは可能ですが、私の意見ではかなり奇妙です:-) –

関連する問題