C# 为什么listbox模板中的bind命令可以';我跑不了?

C# 为什么listbox模板中的bind命令可以';我跑不了?,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,为什么listbox中的按钮不能运行bind命令? 我为简单按钮(不是模板)绑定了这个命令,它工作得非常好。 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" xmln

为什么listbox中的按钮不能运行bind命令? 我为简单按钮(不是模板)绑定了这个命令,它工作得非常好。 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();
            }
        }
    }
}
公共类视图模型:INotifyPropertyChanged
{
公共视图模型(){}
公共可观测收集MyData{
得到
{
返回我的数据;
}
设置
{
如果(!\u MyData.SequenceEqual(值))
{
_MyData=值;
}
OnPropertyChanged();
}
}
私有ObservableCollection_MyData=新ObservableCollection();
公共事件属性更改事件处理程序属性更改;
受保护的虚拟void OnPropertyChanged([CallerMemberName]string caller=”“)
{
PropertyChanged?.Invoke(这是新的PropertyChangedEventArgs(调用者));
}
私有ICommandu testCommand;
公共ICommand TestCommand
{
得到
{
返回_testCommand;
}
设置
{
如果(_testCommand!=值)
{
_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();
    }
}
公共类RelayCommand:ICommand
{
公共关系命令(行动,职能执行指令)
{
if(action==null | | canExecutePredicate==null)
抛出新ArgumentNullException(“不能为null”);
这个._action=action;
这._canExecutePredicate=canExecutePredicate;
}
公共RelayCommand(Action-Action):这个(Action,(obj)=>true){
行动(行动);;
Func_canExecutePredicate;
公共事件处理程序CanExecuteChanged;
受保护的虚拟void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(此为EventArgs.Empty);
}
公共布尔CanExecute(对象参数)
{
返回_canExecutePredicate(参数);
}
public void Execute(对象参数)
{
_action?.Invoke();
}
}

您能说这个解决方案的有效xaml示例吗?

我认为这是因为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中的文本


顺便说一句:在列表框的itemssource绑定中不必有relativesource引用。它从窗口继承DataContext,因此仅使用{Binding MyData}就足够了

我认为这是因为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中的文本


顺便说一句:在列表框的itemssource绑定中不必有relativesource引用。它从窗口继承DataContext,因此仅使用{Binding MyData}就足够了

两个
按钮的
DataContext
不同。让我们看一下你的视图中的一些元素的<代码> DATACONTRONT/<代码>。< /P>
  • 窗口的
    DataContext
    是您的
    ViewModel
  • 列表框
    数据上下文
    窗口
    相同。在您设置的
    项目资源
    绑定中,应该不需要使用
    相对资源
  • 数据模板
    外部的
    按钮
    也与
    窗口
    具有相同的
    数据上下文
    。这就是为什么这个
    命令
    绑定可以正常工作的原因
  • DataTemplate
    中的
    按钮
    具有一个特定项的
    DataContext
    ,该项表示您在
    ViewModel
    类中创建的
    MyData
    集合。重要的是,它不是
    ViewModel
    类本身
这里我将使用
相对资源

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


如果这对您不起作用,请告诉我。

两个
按钮的
DataContext
不同。让我们看一下你的视图中的一些元素的<代码> DATACONTRONT/<代码>。< /P>
  • 窗口的
    DataContext
    是您的
    ViewModel
  • 列表框
    数据上下文
    窗口
    相同。在您设置的
    项目资源
    绑定中,应该不需要使用
    相对资源
  • 数据模板
    外部的
    按钮
    也与
    窗口
    具有相同的
    数据上下文
    。这就是为什么这个
    命令
    绑定可以正常工作的原因
  • DataTemplate
    中的
    按钮
    具有一个特定项的
    DataContext
    ,该项表示您在
    ViewModel
    类中创建的
    MyData
    集合。重要的是,它不是
    ViewModel
    类本身
这里我将使用
相对资源

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


如果这对您不起作用,请告诉我。

这是一个简单的测试应用程序,用于测试wpf数据模板。为了填充MyData,我使用以下代码:Environment.GetEnvironmentVariables().Values.Cast().ToList();MyData=new System.Collections.ObjectModel.ObservableCollection(GetCollection());和:TestCommand=newrelaycommand(()=>MessageBox.Show(“TestCommand”),(obj)=>true);好的,但是你违反了视图模型的概念,因为你在每个元素上的按钮会激活另一个对象上的命令,而