Caliburn Micro 在 ListView 的 ContextMenu 中找不到 DataContext
Caliburn Micro not finding DataContext in ContextMenu within ListView
我已经用对象填充了一个 ListView,并且我已经将 ContextMenu 绑定到我的 ListView 中的那些项目。上下文菜单只能通过单击一个项目来打开。问题是 Caliburn Micro 抛出一个错误,它找不到 ShowProperties() 的目标方法。
我认为出现此问题是因为 Caliburn 没有可用的 ViewModel 的正确 DataContext。我在 Whosebug 上尝试了很多解决方案,以使 ViewModel 可用于 ContextMenu 项目,但无济于事,例如:
WPF: Binding a ContextMenu to an MVVM Command
“No target found for method” thrown by Caliburn Message.Attach()
WPF Context Menus in Caliburn Micro
这是我认为的XAML代码:
<Window x:Class="CueMaster.Views.AppView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dragDrop="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
xmlns:cal="http://www.caliburnproject.org"
Height="500" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView Grid.Column="1" Margin="5"
ItemsSource="{Binding Cues}"
dragDrop:DragDrop.IsDragSource="True"
dragDrop:DragDrop.IsDropTarget="True"
dragDrop:DragDrop.DropHandler="{Binding}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView >
<GridViewColumn Width="70" Header="Cue" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Width="100" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="100" Header="Description" DisplayMemberBinding="{Binding Description}" />
<GridViewColumn Width="70" Header="Duration" DisplayMemberBinding="{Binding Duration}" />
<GridViewColumn Width="70" Header="Elapsed" DisplayMemberBinding="{Binding Elapsed}" />
<GridViewColumn Width="70" Header="Remaining" DisplayMemberBinding="{Binding Remaining}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
我错过了什么?
您通过放置命令绑定来覆盖 CM 将执行的操作。由于可视化树不知道上下文菜单的存在,更不用说背后目的的数据上下文了。
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" >
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
虽然我了解您尝试对 ListView 中的资源执行的操作,但您是在用命令绑定搬起石头砸自己的脚。放下资源给 ItemContainerStyle 一个滚动,看看它是否有效。您以后可以随时将其分解为资源。出于测试目的,看看它是否有效,现在试试内部样式。
使用 mvermef 的回答,我让它工作了。他的代码中唯一需要更改的是绑定意味着 "Travel up the Visual Tree, find the first ContextMenu object above this one, and bind to the PlacementTarget.Tag property"。问题在于绑定是在 ContextMenu 本身上,因此没有父 ContextMenu 对象。使用 RelativeSource Self 解决了这个问题。
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" >
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
我已经用对象填充了一个 ListView,并且我已经将 ContextMenu 绑定到我的 ListView 中的那些项目。上下文菜单只能通过单击一个项目来打开。问题是 Caliburn Micro 抛出一个错误,它找不到 ShowProperties() 的目标方法。
我认为出现此问题是因为 Caliburn 没有可用的 ViewModel 的正确 DataContext。我在 Whosebug 上尝试了很多解决方案,以使 ViewModel 可用于 ContextMenu 项目,但无济于事,例如:
WPF: Binding a ContextMenu to an MVVM Command
“No target found for method” thrown by Caliburn Message.Attach()
WPF Context Menus in Caliburn Micro
这是我认为的XAML代码:
<Window x:Class="CueMaster.Views.AppView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dragDrop="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
xmlns:cal="http://www.caliburnproject.org"
Height="500" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView Grid.Column="1" Margin="5"
ItemsSource="{Binding Cues}"
dragDrop:DragDrop.IsDragSource="True"
dragDrop:DragDrop.IsDropTarget="True"
dragDrop:DragDrop.DropHandler="{Binding}">
<ListView.Resources>
<ContextMenu x:Key="ItemContextMenu">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView >
<GridViewColumn Width="70" Header="Cue" DisplayMemberBinding="{Binding Id}" />
<GridViewColumn Width="100" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="100" Header="Description" DisplayMemberBinding="{Binding Description}" />
<GridViewColumn Width="70" Header="Duration" DisplayMemberBinding="{Binding Duration}" />
<GridViewColumn Width="70" Header="Elapsed" DisplayMemberBinding="{Binding Elapsed}" />
<GridViewColumn Width="70" Header="Remaining" DisplayMemberBinding="{Binding Remaining}" />
</GridView>
</ListView.View>
</ListView>
</Grid>
我错过了什么?
您通过放置命令绑定来覆盖 CM 将执行的操作。由于可视化树不知道上下文菜单的存在,更不用说背后目的的数据上下文了。
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" >
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
虽然我了解您尝试对 ListView 中的资源执行的操作,但您是在用命令绑定搬起石头砸自己的脚。放下资源给 ItemContainerStyle 一个滚动,看看它是否有效。您以后可以随时将其分解为资源。出于测试目的,看看它是否有效,现在试试内部样式。
使用 mvermef 的回答,我让它工作了。他的代码中唯一需要更改的是绑定意味着 "Travel up the Visual Tree, find the first ContextMenu object above this one, and bind to the PlacementTarget.Tag property"。问题在于绑定是在 ContextMenu 本身上,因此没有父 ContextMenu 对象。使用 RelativeSource Self 解决了这个问题。
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Tag" Value="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu cal:Action.TargetWithoutContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Properties" cal:Message.Attach="ShowProperties($dataContext)" >
<MenuItem.Icon>
<Image Source="../PropertyIcon.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>