WPF中如何从ListView的GridView内部绑定到ViewModel中的ICommand
你遇到的这个问题在WPF MVVM开发里挺常见的——ListView的每一行默认会把DataContext设置为集合里的单个Service对象,所以你直接绑定RemoveTimerClick时,WPF会去当前行的Service对象里找这个命令,而不是你想要的ViewModel里的命令。下面给你几种简单可行的解决方法:
方法一:通过RelativeSource访问父控件的DataContext
你之前已经尝试了找UserControl的RelativeSource,但漏掉了访问它的DataContext属性。只需要在绑定路径里加上DataContext.,就能定位到ViewModel里的命令了:
<Button Background="Transparent" BorderThickness="0" Command="{Binding DataContext.RemoveTimerClick, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" CommandParameter="{Binding}"> <Image Source="pack://application:,,,/Resources/Images/Delete.png" Width="20"/> </Button>
这里的逻辑很简单:先通过RelativeSource找到外层的UserControl,它的DataContext就是你的ViewModel,再从ViewModel里取出RemoveTimerClick命令。
方法二:使用ElementName绑定
给你的ListView(或者外层的UserControl)起个名字,然后通过ElementName来绑定它的DataContext里的命令。比如给ListView命名为ServicesListView:
<ListView x:Name="ServicesListView" ItemsSource="{Binding Services}"> <!-- 其他GridView代码保持不变 --> <GridViewColumn.CellTemplate> <DataTemplate> <Button Background="Transparent" BorderThickness="0" Command="{Binding DataContext.RemoveTimerClick, ElementName=ServicesListView}" CommandParameter="{Binding}"> <Image Source="pack://application:,,,/Resources/Images/Delete.png" Width="20"/> </Button> </DataTemplate> </GridViewColumn.CellTemplate> <!-- 其他代码保持不变 --> </ListView>
因为ListView的DataContext和外层UserControl一样都是ViewModel,所以这种方式也能准确找到目标命令。
方法三:使用x:Reference绑定
如果你的外层UserControl有名字(比如RootControl),还可以用x:Reference来直接引用它的DataContext:
<UserControl x:Name="RootControl" ...> <!-- 其他代码 --> <Button Background="Transparent" BorderThickness="0" Command="{Binding DataContext.RemoveTimerClick, Source={x:Reference RootControl}}" CommandParameter="{Binding}"> <Image Source="pack://application:,,,/Resources/Images/Delete.png" Width="20"/> </Button> </UserControl>
另外要提一句,你原来写的CommandParameter="{Binding}"是完全正确的,它会把当前行的Service对象作为参数传给命令,正好满足删除对应项的需求。
备注:内容来源于stack exchange,提问作者zynovij

