Reputation: 28718
I have a Silverlight page that contains an ItemsControl. It looks something like this
-- Name Description [Add]
-- Thing1 The first thing [Edit] [Delete]
-- Thing2 The second thing [Edit] [Delete]
where [Edit]
, [Delete]
, and [Add]
are buttons.
Currently I'm binding the control to a collection of Thing
and using a template to display the properties, and bind to an Edit
Command in my ViewModel.
It doesn't make sense (to me) for the ThingViewModel
to have a Delete
Command which causes it to delete itself;
So what's the best pattern to wire up the [Delete]
button?
Upvotes: 0
Views: 1171
Reputation: 28718
Here's what I've come up with - in a very simplified form. It's an obvious choice when RelativeSource
is not available.
ElementName
property on the binding for the child / itemThis works for an ItemsControl
but I haven't been able to get this pattern to work for a DataGrid
yet.
<ItemsControl Name="MyParentControl"
ItemsSource="{Binding Things}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="Delete"
Command="{Binding ElementName=MyParentControl,
Path=DataContext.DeleteCommand}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Upvotes: 1
Reputation: 7719
If you use a listbox so that the user can select a row and then put your commands in the VM that contains the collection, you get a cleaner implementation.
here is the xaml for a row with a delete button. The trick is to bind the button to the parent items DeleteCommand by using "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>
And the ViewModel code
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");
}
}
oh, i just saw the above comment about silverlight and relativeSource. I did this with WPF and tested it out, which worked, it may not work for silverlight
Upvotes: 0
Reputation: 2698
The "Delete" code will not be run on the ViewModel for the individual item, in the collection, rather it would be bubbled up (somehow) to the ViewModel that contains the collection, and processed there.
Pseudocode:
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
}
}
One way to bubble up the event is to have the ItemViewModel aware of it's parent ViewModel
public class ItemViewModel
{
public ItemsCollectionViewModel ParentViewModel { get; private set; }
public ItemViewModel(ItemsCollectionViewModel parentViewModel)
{
this.ParentView = parentViewModel;
}
public void Delete()
{
this.ParentViewModel.Delete(this);
}
}
There are better ways with a MVVM framework like Caliburn.Micro or MVVM-Lite, but this at least gets you started on how to think about these kinds of operations in your ViewModel.
Essentialy - your ViewModel should be able to do all of your user operations without requiring a View of any kind. (You should be able to run some test code, and have your VM work as intended without a bound View)
Upvotes: 1