Okay, I’m not sure this is the best way to do this, but I needed a way to remove an item from an ItemsControl within the UI context of the item itself. My problem was that the item itself, when in a datatemplate, doesn’t know its parent.  The RelativeSource property came to the rescue (tip o’ the hat to Jaime Rodriguez), although my use of the Tag property seems a little funky. Here’s my solution:

 

<DataTemplate x:Key="DataTemplate1">
    <StackPanel Orientation="Vertical" >
        <TextBox Text="{Binding Path=DisplayName}"/>
<TextBox Text="{Binding Path=NavigateURL}"/>
<Button Content="Remove" Click="Remove_Click" Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}}"/> </StackPanel> </DataTemplate>

Then, in the code behind, here’s what I do:

 

void Remove_Click(object sender, RoutedEventArgs e)
{
    Button button = (Button) sender;
    ListBox listBox = (ListBox)button.Tag;
    MenuItemCollection menuItemCollection = (MenuItemCollection)listBox.ItemsSource;
    menuItemCollection.Remove((MenuItem)button.DataContext);
}

Basically, I get the listbox out of the Tag element and then call into its ItemsSource property, remove the object which is bound to the data template itself.  (Note that MenuItem and MenuItemCollection are objects I created.)

Maybe a little hacky, but it works!

Posted on January 27, 2009 14:10
Actions: E-mail | Comments (4)

Comments


Dan Puzey

Dan Puzey

January 24, 2009 17:39

Hi,

If your ItemsControl is (or could be) bound to a source list, there's a cleaner way to do this with RoutedCommands.  The gist of it would be:

- the ItemsControl is bound to a collection implementing INotifyCollectionChanged
- create a RoutedCommand for your "Delete" functionality
- put a CommandBinding around the ItemsControl.  The Execute method for the command should take the CommandParameter and remove it from the source collection.
- your button then becomes <Button Content="Remove" Command="YourCommand" CommandParameter="{Binding}" />  (note that the "{Binding}" will pass the current list item as parameter to the command)

This removes the funky/hacky aspects, and separates the functionality from the UI (it'd be trivial to turn your button into a menu item or a keyboard shortcut or a mouse gesture).

In my experience, a general rule for WPF is that if you're using click event handlers (especially on buttons!), you're missing something Smile

I'd be happy to expand on this example if you'd like it fleshed out Smile

Regards,

Dan.
          


Karsten Januszewski

Karsten Januszewski

January 24, 2009 18:11

Thanks for the tip, Dan. Much more elegant. And I grimaced when I put that click handler in XAML, but I just wanted to get things working! At this point in the app, I've held back from commands because it added complexity without a huge gain in functionality, even though I know it is the "right" way to do things.  Commands are on the the task list, but at this point there are other items of higher priority.  But if I get to it, I'll definitely implement your solution.  Thanks again for posting and I'd be curious if others have thoughts on commands vs. handlers...
          


eric burke

eric burke

January 25, 2009 07:17

Another option is to change the Tag binding:

Tag="{Binding Path=ItemsSource,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}}"

Then in the codebehind:

Button button = (Button) sender;
MenuItemCollection listBox = (MenuItemCollection)button.Tag;
menuItemCollection.Remove((MenuItem)button.DataContext);



Another option that saves you having one click handler per item:

On the ListBox, put a handler for ButtonBase.Click:

<ListBox ButtonBase.Click="OnClick"...>...</ListBox>

void OnClick(object sender, RoutedEventArgs e)
{
    ListBox lb = (ListBox)sender;
    Button b = (Button)e.Source;
    if(b.Content == "Remove")
    {
        (lb.ItemsSource as MenuItemCollection).Remove(b.DataContext);
    }
}

          


Karsten Januszewski

Karsten Januszewski

January 25, 2009 16:09

Nice Eric! Thanks
          


Add comment

Enter your name, handle, alias, or email.

We'll incarnate your avatar from the services below.