【问题标题】:Pattern for deleting items from a Silverlight ItemsControl从 Silverlight ItemsControl 中删除项目的模式
【发布时间】:2011-10-25 21:33:13
【问题描述】:

我有一个包含 ItemsControl 的 Silverlight 页面。它看起来像这样

-- Name             Description          [Add]
-- Thing1           The first thing      [Edit] [Delete]
-- Thing2           The second thing     [Edit] [Delete]

其中[Edit][Delete][Add] 是按钮。

目前我将控件绑定到Thing 的集合并使用模板显示属性,并绑定到我的ViewModel 中的Edit 命令。

ThingViewModel 有一个 Delete 命令导致它自己删除是没有意义的(对我来说);

  • 看起来不干净
  • Thing 不知道它在集合中,因此无法将自己从集合中移除

那么连接[Delete] 按钮的最佳模式是什么?

【问题讨论】:

  • 在 WPF 中这很容易 - 我会使用 RelativeSource 将删除按钮绑定到父数据上下文中的删除命令。不幸的是,Silverlight 中的 RelativeSource 似乎残废且半无用...

标签: silverlight xaml mvvm observablecollection


【解决方案1】:

“删除”代码不会在集合中的单个项目的 ViewModel 上运行,而是会(以某种方式)冒泡到包含集合的 ViewModel,并在那里进行处理。

伪代码:

public class ItemContainerViewModel
{
    List<ItemClass> Items { get; set; }

    public void DeleteItem(ItemClass item)
    {
        this.Items.Remove(item);
        NotifyOfPropertyChange(() => this.Items); // Causes controls bound to this list to refresh their contents
    }
}

让事件冒泡的一种方法是让 ItemViewModel 知道它的父 ViewModel

public class ItemViewModel
{
    public ItemsCollectionViewModel ParentViewModel { get; private set; }
    public ItemViewModel(ItemsCollectionViewModel parentViewModel)
    {
        this.ParentView = parentViewModel;
    }

    public void Delete()
    {
        this.ParentViewModel.Delete(this);
    }
}

对于像 Caliburn.Micro 或 MVVM-Lite 这样的 MVVM 框架有更好的方法,但这至少可以让您开始考虑如何在 ViewModel 中考虑这些类型的操作。

本质上 - 您的 ViewModel 应该能够在不需要任何类型的 View 的情况下执行所有用户操作。 (您应该能够运行一些测试代码,并让您的虚拟机在没有绑定视图的情况下按预期工作)

【讨论】:

    【解决方案2】:

    这是我想出的 - 以非常简化的形式。当RelativeSource 不可用时,这是一个显而易见的选择。

    • 为绑定到父/集合的控件命名
    • 在绑定上为子项/项指定ElementName 属性

    这适用于ItemsControl,但我还不能让这种模式适用于DataGrid

    <ItemsControl Name="MyParentControl"
                  ItemsSource="{Binding Things}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Button Content="Delete" 
                        Command="{Binding ElementName=MyParentControl, 
                            Path=DataContext.DeleteCommand}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    【讨论】:

      【解决方案3】:

      如果您使用列表框以便用户可以选择一行,然后将您的命令放入包含该集合的 VM 中,您将获得更简洁的实现。

      这里是带有删除按钮的行的 xaml。诀窍是使用“RelativeSource”将按钮绑定到父项 DeleteCommand

          <ListBox ItemsSource="{Binding MyItems, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding MySelectedItem, UpdateSourceTrigger=PropertyChanged}" >
              <ListBox.ItemContainerStyle>
                  <Style TargetType="{x:Type ListBoxItem}">
                      <Setter Property="Template">
                          <Setter.Value>
                              <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                  <StackPanel Orientation="Horizontal"   >
                                      <Label Content="{Binding}" />
                                      <Button Content="Delete" Command="{Binding DataContext.MyDeleteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" />
                                  </StackPanel >
                              </ControlTemplate>
                          </Setter.Value>
                      </Setter>
                  </Style>
              </ListBox.ItemContainerStyle>
          </ListBox>
      

      以及 ViewModel 代码

      public DelegateCommand<object> MyDeleteCommand { get; set; }
      
      protected void DoDelete(object o)
      {
          MyItems.Remove(MySelectedItem);
      }
      
      protected bool CanDoDelete(object o)
      {
          return MySelectedItem != null;
      }
      
      string _mySelectedItem;
      public string MySelectedItem
      {
          get { return _mySelectedItem; }
          set
          {
              _mySelectedItem = value;
              OnPropertyChanged("MySelectedItem");
              MyDeleteCommand.RaiseCanExecuteChanged();
          }
      }
      
      ObservableCollection<string> _myItems;
      public ObservableCollection<string> MyItems
      {
          get { return _myItems; }
          set 
          { 
              _myItems = value;
              OnPropertyChanged("MyItems");
          }
      }
      

      哦,我刚刚看到上面关于 silverlight 和 relativeSource 的评论。我用 WPF 做了这个并测试了它,它有效,它可能不适用于 silverlight

      【讨论】:

      • 这就是我在 WPF 中的做法,但 Silverlight 中没有 FindAncestor
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2011-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多