为ListViewItem附加行为以执行命令

8
我正在尝试使用一个附加行为,在用户双击列表项时执行ViewModel中的命令。
我已经查阅了许多相关文章,并尝试创建了一个简单的测试应用程序,但仍然存在问题。例如:Firing a double click event from a WPF ListView item using MVVM 我的简单测试ViewModel有两个集合,一个返回字符串列表,另一个返回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 Error: 39 : BindingExpression path error: 'Foo' property not found on 'object' ''String' (HashCode=785742638)'. BindingExpression:Path=Foo; DataItem='String' (HashCode=785742638); target element is 'ListViewItem' (Name=''); target property is 'DoubleClick' (type 'ICommand')

所以我的问题是:当您将ListView绑定到模型对象列表时,如何正确地将命令连接到每个ListViewItem?
谢谢。

这非常有用!为了增加可重用性,我将 ListViewItem element = target as ListViewItem; 改为 Control element = target as Control; - CatBusStop
1个回答

9
问题在于绑定的DataContext是字符串,而字符串类中没有Foo属性,因此会出现错误。这种情况在其他情况下不会发生,因为它们从父级继承了DataContext(对于数据项的自动生成容器不会发生这种情况-它们的DataContext是数据项)。
如果您更改绑定以使用父级ListView的DataContext,则应该可以正常工作:
Value="{Binding DataContext.Foo, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"

谢谢你的迅速回答,Abe。你真是个明星,我已经试图解决这个问题太久了。 - Paul Taylor
1
没问题,保罗。绑定错误确实很难懂,有时候只需要一双新鲜的眼睛才能看清楚发生了什么! - Abe Heidebrecht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接