Прикрепленное поведение для выполнения команды для ListViewItem
Я пытаюсь использовать прикрепленное поведение для выполнения команды в моей ViewModel, когда пользователь дважды щелкает по элементу списка.
Я просмотрел ряд статей на эту тему и попытался создать простое тестовое приложение, но у меня все еще есть проблемы, например. Запуск события двойного щелчка из элемента WPF ListView с использованием MVVM
Мой простой тест ViewModel имеет 2 коллекции: одна возвращает список строк, а другая возвращает список типов ListViewItem.
public class ViewModel
{
public ViewModel()
{
Stuff = new ObservableCollection<ListViewItem>
{
new ListViewItem { Content = "item 1" },
new ListViewItem { Content = "item 2" }
};
StringStuff = new ObservableCollection<string> { "item 1", "item 2" };
}
public ObservableCollection<ListViewItem> Stuff { get; set; }
public ObservableCollection<string> StringStuff { get; set; }
public ICommand Foo
{
get
{
return new DelegateCommand(this.DoSomeAction);
}
}
private void DoSomeAction()
{
MessageBox.Show("Command Triggered");
}
}
Вот прикрепленное свойство, подобное другим примерам, которые вы видите:
public class ClickBehavior
{
public static DependencyProperty DoubleClickCommandProperty = DependencyProperty.RegisterAttached("DoubleClick",
typeof(ICommand),
typeof(ClickBehavior),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(ClickBehavior.DoubleClickChanged)));
public static void SetDoubleClick(DependencyObject target, ICommand value)
{
target.SetValue(ClickBehavior.DoubleClickCommandProperty, value);
}
public static ICommand GetDoubleClick(DependencyObject target)
{
return (ICommand)target.GetValue(DoubleClickCommandProperty);
}
private static void DoubleClickChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
ListViewItem element = target as ListViewItem;
if (element != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
element.MouseDoubleClick += element_MouseDoubleClick;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
element.MouseDoubleClick -= element_MouseDoubleClick;
}
}
}
static void element_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
UIElement element = (UIElement)sender;
ICommand command = (ICommand)element.GetValue(ClickBehavior.DoubleClickCommandProperty);
command.Execute(null);
}
}
В моем главном окне я определил стиль, который устанавливает присоединенное поведение и привязывается к команде Foo
<Window.Resources>
<Style x:Key="listViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="local:ClickBehavior.DoubleClick" Value="{Binding Foo}"/>
</Style>
</Window.Resources>
Работает нормально, когда определены ListViewItems:
<!-- Works -->
<Label Grid.Row="2" Content="DoubleClick click behaviour:"/>
<ListView Grid.Row="2" Grid.Column="1" ItemContainerStyle="{StaticResource listViewItemStyle}">
<ListViewItem Content="Item 3" />
<ListViewItem Content="Item 4" />
</ListView>
Это также работает, когда привязан к списку типа ListViewItem:
<!-- Works when items bound are of type ListViewItem -->
<Label Grid.Row="3" Content="DoubleClick when bound to ListViewItem:"/>
<ListView Grid.Row="3" Grid.Column="1" ItemContainerStyle="{StaticResource listViewItemStyle}" ItemsSource="{Binding Stuff}">
</ListView>
Но это не так:
<!-- Does not work when items bound are not ListViewItem -->
<Label Grid.Row="4" Content="DoubleClick when bound to string list:"/>
<ListView Grid.Row="4" Grid.Column="1" ItemContainerStyle="{StaticResource listViewItemStyle}" ItemsSource="{Binding StringStuff}">
</ListView>
В окне вывода вы видите ошибку, но с трудом понимаете, что не так.
Ошибка System.Windows.Data: 39: Ошибка пути BindingExpression: свойство 'Foo' не найдено для объекта '' 'Строка' (HashCode = 785742638) '. BindingExpression: Path = Foo; DataItem = 'String' (HashCode = 785742638); целевым элементом является ListViewItem (Name=''); Свойство target - Double Click (тип ICommand)
Итак, мой вопрос: как правильно связать Команду с каждым ListViewItem, когда вы связываете свой ListView со списком объектов Model?
Благодарю.
1 ответ
Проблема в том, что DataContext
для Binding
это строка Так как нет Foo
свойство строкового класса, вы получаете ошибку. В других случаях этого не происходит, потому что они наследуют DataContext
от родителя (это не происходит для автоматически сгенерированных контейнеров для элементов данных - их DataContext
это элемент данных).
Если вы измените свою привязку, чтобы использовать родительский ListView
"s DataContext
, должно работать нормально:
Value="{Binding DataContext.Foo, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"