【问题标题】:ICommand in MVVM WPFMVVM WPF 中的 ICommand
【发布时间】:2009-03-13 10:09:28
【问题描述】:

我正在查看这些 MVVM 内容,但遇到了一个问题。

情况很简单。

我的 index.xaml 页面中有以下代码

    <Grid>
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <view:MovieView ></view:MovieView>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

在我的 index.xaml.cs 中

...

初始化组件(); base.DataContext = new MovieViewModel(ent.Movies.ToList()); ....

这是我的 MoveViewModel

    public class MovieViewModel
{
    readonly List<Movies> _m;
    public ICommand TestCommand { get; set; }
    public MovieViewModel(List<Movies> m)
    {
        this.TestCommand = new TestCommand(this);
        _m = m;
    }
    public List<Movies> lm
    {
        get
        {
            return _m;
        }
    }
}

终于

这是我的控件 xaml MovieView

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Label VerticalAlignment="Center" Grid.Row="0" Grid.Column="0">Title :</Label><TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" Text="{Binding Title}"></TextBlock>  
    <Label VerticalAlignment="Center" Grid.Row="1" Grid.Column="0">Director :</Label><TextBlock VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" Text="{Binding Director}"></TextBlock>
    <Button Grid.Row="2" Height="20" Command="{Binding Path=TestCommand}" Content="Edit" Margin="0,4,5,4" VerticalAlignment="Stretch" FontSize="10"/>
</Grid>

所以我遇到的问题是,如果我将 ItemsSource 设置为 Binding

它什么也做不了

如果我设置 ItemsSource="{Binding lm}"

它填充了我的 itemsControl,但命令 (Command="{Binding Path=TestCommand}" ) 不起作用。

当然不行,因为TestCommand不属于我的实体对象Movies。

所以最后我的问题是,

我需要将什么传递给 ItemsControl 以使其工作?

提前谢谢

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    一旦您的项目被渲染,每个项目都会被设置为它所代表的特定行的 DataContext,因此您不再能够引用您的第一个 DataContext。此外,由于您处于DataTemplate,当需要该模板时,您的绑定将开始工作。所以在这种情况下,您需要通过 RelativeSource 绑定查找您的父控件...

    希望能解释一些事情..

    【讨论】:

      【解决方案2】:

      尝试实现 INotifyPropertyChanged 接口:

      public class MovieViewModel : INotifyPropertyChanged
      {
          readonly List<Movies> _m;
          private ICommand _testCommand = null;
          public ICommand TestCommand { get { return _testCommand; } set { _testCommand = value; NotifyPropertyChanged("TestCommand"); } }
          public MovieViewModel(List<Movies> m)
          {
              this.TestCommand = new TestCommand(this);
              _m = m;        
          }
          public List<Movies> lm
          {
              get
              {
                  return _m;
              }
          }
      
      
          public event PropertyChangedEventHandler PropertyChanged;
      
          protected void NotifyPropertyChanged(string sProp)
          {
              if (PropertyChanged != null)
              {
                  PropertyChanged(this, new PropertyChangedEventArgs(sProp));
              }
          }    
      }
      

      发生的情况是 TestCommand 有一个值,并且 UI 没有收到有关更改发生的通知。在控件上,您使用 Dependency 属性解决此问题,在数据对象上,您可以使用 INotifyPropertyChanged 接口。.

      其次,Movie 对象没有对父对象的引用。

      您可以通过三种不同的方式解决此问题:

      1. 在 Movie 上有一个对模型的引用,并使 Bind 路径如下所示:即.. 如果您的属性名为 ParentMovieModel,那么您的 Binding 将如下所示:

        {绑定路径=ParentMovieModel.TestCommand}

      2. 像这样基于 ElementName 进行绑定:查找设置 DataContext 的父控件,并为其命名:即 Root。现在基于 ElementName 创建一个绑定,如下所示:

        {Binding ElementName=Root, Path=DataContext.TextCommand}

      3. 像这样基于RelativeSource进行绑定:按类型查找父控件,并使用与上述相同的路径...

        {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type yourparentwindowtype}}, Path=DataContext.TextCommand}

      【讨论】:

      • 在这种情况下实现 INotifyPropertyChanged 接口有什么帮助?您在构造函数中为 TestCommand 赋值。那个时候怎么能订阅PropertyChanged事件呢?
      【解决方案3】:

      搞定了

      事情是这样的

       <ItemsControl DataContext="{Binding}" ItemsSource="{Binding lm}">
      
       Command="{Binding Path=DataContext.TestCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" 
      

      所以RelativeSource 是我错过的东西。

      如果有人对此有很好的解释,我肯定会很高兴。

      【讨论】:

      • 查看@Arcturus 的回答:在项目模板中,您的 DataContext 更改为正在显示的特定项目。您要绑定的命令在父视图(您的视图模型)的数据上下文中,而不是项目的数据上下文(这是Movie 的单个实例)
      • 另外,DataContext="{Binding}" 是多余的:您将 DataContext 绑定到整个当前数据上下文,它已经是 :)
      【解决方案4】:
      //include namespace
      using Microsoft.Practices.Composite.Wpf.Commands;
      
      readonly List<Movies> _m;
          public ICommand TestCommand { get; set; }
          public MovieViewModel(List<Movies> m)
          {
              this.TestCommand = new DelegateCommand<object>(TestcommandHandler);
              _m = m;
          }
          public List<Movies> lm
          {
              get
              {
                  return _m;
              }
          }
      
      void TestcommandHandler(object obj)
      {
            // add your logic here
      }
      }
      

      【讨论】:

        【解决方案5】:

        &lt;ItemsControl ItemsSource="{Binding Path=lm}"&gt; 呢?

        【讨论】:

          【解决方案6】:

          在 ItemsSource="{Binding Path=lm}"> 案例中

          itemsControl 运行良好,但我完全绕过了我的 MovieViewModel

          我在输出窗口中得到了这个

          System.Windows.Data 错误:39:BindingExpression 路径错误:在“对象”“电影”(HashCode=1031007)上找不到“TestCommand”属性。绑定表达式:路径=测试命令; DataItem='电影' (HashCode=1031007);目标元素是 'Button' (Name='');目标属性是“命令”(输入“ICommand”)

          Movies 是我的实体对象,仅拥有 Title 和 Director 属性

          【讨论】:

            猜你喜欢
            • 2013-10-02
            • 1970-01-01
            • 1970-01-01
            • 2011-09-25
            • 2013-08-05
            • 1970-01-01
            • 2010-11-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多