#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
.