Шаблон для удаления элементов из Silverlight ItemsControl

#silverlight #xaml #mvvm #observablecollection

#silverlight #xaml #mvvm #наблюдаемая коллекция

Вопрос:

У меня есть страница Silverlight, которая содержит ItemsControl. Это выглядит примерно так

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

где [Edit] , [Delete] , и [Add] являются кнопками.

В настоящее время я привязываю элемент управления к коллекции Thing и использую шаблон для отображения свойств и привязки к Edit команде в моей ViewModel.

Для меня не имеет смысла (для меня) ThingViewModel иметь Delete команду, которая заставляет ее удалять себя;

  • не кажется чистым
  • вещь не знает, что она находится в коллекции, поэтому не может удалить себя из коллекции

Итак, какой наилучший шаблон для подключения [Delete] кнопки?

Комментарии:

1. В WPF это было бы легко — я бы привязал кнопку удаления к команде удаления в datacontext родительского datacontext с помощью RelativeSource . К сожалению RelativeSource , Silverlight кажется поврежденным и наполовину бесполезным…

Ответ №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);
    }
}
  

Есть лучшие способы с помощью MVVM-фреймворка, такого как Caliburn.Micro или MVVM-Lite, но это, по крайней мере, поможет вам понять, как думать об этих операциях в вашей ViewModel.

По сути, ваша ViewModel должна иметь возможность выполнять все ваши пользовательские операции, не требуя представления любого вида. (Вы должны иметь возможность запускать некоторый тестовый код, и ваша виртуальная машина будет работать по назначению без привязанного представления)

Ответ №2:

Вот что я придумал — в очень упрощенной форме. Это очевидный выбор, когда RelativeSource он недоступен.

  • Назовите элемент управления, который привязывается к родительскому элементу / коллекции
  • Укажите ElementName свойство в привязке для дочернего элемента / элемента

Это работает для an ItemsControl , но я пока не смог заставить этот шаблон работать для a 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:

Если вы используете listbox, чтобы пользователь мог выбрать строку, а затем поместить ваши команды в виртуальную машину, содержащую коллекцию, вы получаете более чистую реализацию.

вот xaml для строки с кнопкой удаления. Хитрость заключается в том, чтобы привязать кнопку к родительским элементам DeleteCommand с помощью «RelativeSource»

     <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

Комментарии:

1. Вот как я бы сделал это в WPF, но в Silverlight этого нет FindAncestor .