EDIT: I have found the issue lies in the ListBoxItem
's style in resources. If I comment it, it works fine. But what is wrong with it? I want to override the default style of ListBoxItem
with the blue selection, etc.
I have a ListBox
with the item template containing a Border
. The border has triggers to make the delete button only appear on mouse hover. Now what happens in DeleteCommand
command is that I delete the data for which this particular item was appearing in the list box. Consequently, the border disappears but the MouseLeave
trigger still gets called and I get the following exception:
Exception type: System.InvalidOperationException
Exception message: 'controlBox' name cannot be found in the name scope of 'System.Windows.Controls.Border'.
Exception stack trace:
at System.Windows.Media.Animation.Storyboard.ResolveTargetName(String targetName, INameScope nameScope, DependencyObject element)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.ClockTreeWalkRecursive(Clock currentClock, DependencyObject containingObject, INameScope nameScope, DependencyObject parentObject, String parentObjectName, PropertyPath parentPropertyPath, HandoffBehavior handoffBehavior, HybridDictionary clockMappings, Int64 layer)
at System.Windows.Media.Animation.Storyboard.BeginCommon(DependencyObject containingObject, INameScope nameScope, HandoffBehavior handoffBehavior, Boolean isControllable, Int64 layer)
at System.Windows.Media.Animation.BeginStoryboard.Begin(DependencyObject targetObject, INameScope nameScope, Int64 layer)
at System.Windows.EventTrigger.EventTriggerSourceListener.Handler(Object sender, RoutedEventArgs e)
...
This is a sample XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:MyItems x:Key="myItems">
<local:MyItem Name="Item 1" />
<local:MyItem Name="Item 2" />
<local:MyItem Name="Item 3" />
</local:MyItems>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter Margin="0,0,0,4" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{StaticResource myItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" Padding="5" BorderBrush="Blue" BorderThickness="1">
<Border.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="controlBox" Storyboard.TargetProperty="Opacity" To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="controlBox" Storyboard.TargetProperty="Opacity" To="0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name}" />
<StackPanel Grid.Column="1" x:Name="controlBox" Opacity="0">
<Button Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MainWindow}}}"
CommandParameter="{Binding}">Delete</Button>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
This is the sample code-behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ICommand DeleteCommand { get; private set; }
public MainWindow()
{
InitializeComponent();
DeleteCommand = new MyCommand<MyItem>( Delete );
}
private void Delete( MyItem myItem )
{
MyItems myItems = Resources[ "myItems" ] as MyItems;
myItems.Remove( myItem );
}
}
public class MyItem : DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( string ), typeof( MyItem ) );
public string Name { get { return (string) GetValue( NameProperty ); } set { SetValue( NameProperty, value ); } }
}
public class MyItems : ObservableCollection<MyItem>
{
}
public class MyCommand<T> : ICommand
{
private readonly Action<T> executeMethod = null;
private readonly Predicate<T> canExecuteMethod = null;
public MyCommand( Action<T> execute )
: this( execute, null )
{
}
public MyCommand( Action<T> execute, Predicate<T> canExecute )
{
executeMethod = execute;
canExecuteMethod = canExecute;
}
public event EventHandler CanExecuteChanged;
public void NotifyCanExecuteChanged( object sender )
{
if( CanExecuteChanged != null )
CanExecuteChanged( sender, EventArgs.Empty );
}
public bool CanExecute( object parameter )
{
return canExecuteMethod != null && parameter is T ? canExecuteMethod( (T) parameter ) : true;
}
public void Execute( object parameter )
{
if( executeMethod != null && parameter is T )
executeMethod( (T) parameter );
}
}
}