XAML ContextMenu.PlacementTarget未设置,不知道原因

vs3odd8k  于 2023-08-01  发布在  其他
关注(0)|答案(5)|浏览(126)
<DataTemplate x:Key="_ItemTemplateA">
  <Grid Tag="{Binding Path=DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <ContentControl Content="{Binding}" ContentTemplate="{StaticResource ContentTemplateB}" Grid.Row="0" />
    <ContentControl Name="uiContentPresenter" Content="{Binding ContentView}" Grid.Row="1" Height="0" />
    <ContentControl DataContext="{Binding IsContentDisplayed}" DataContextChanged="IsDisplayed_Changed" Visibility="Collapsed" />
    <Grid.ContextMenu>
      <ContextMenu>
        <MenuItem Header="Text" 
              Command="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
              CommandParameter="{Binding}" />
      </ContextMenu>
    </Grid.ContextMenu>
  </Grid>
</DataTemplate>

字符串
上述数据模板应用于ItemsControl。问题是,对于为Grid指定的ContextMenu,PlacementTarget属性从未实际设置为任何内容,因此我无法访问Grid的Tag属性,该属性对于将应在父UserControl上执行的Command传递到上下文菜单是必要的。我基于类似的例子来实现这种方法,比如:http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0244fbb0-fd5f-4a03-bd7b-978d7cbe1be3/
我找不到其他好的方法来传递这个命令。之所以这样设置,是因为我们使用的是MVVM方法,因此我们必须执行的命令位于应用此模板的用户控件的View Model中。我已经尝试过用几种不同的方法显式地设置PlacementTarget,但它仍然总是显示为未设置。

vi4fp9gy

vi4fp9gy1#

我意识到这是一个古老的问题,但似乎没有得到适当的回答。我看到了一个类似的帖子,并留下了完整的答案。您可能想看一看,因为您可以通过对代码进行一些调整来使其工作。
首先,将视图命名为UserControl ...为了简单起见,我通常将我的所有代码命名为This。然后记住我们的视图模型绑定到UserControlDataContext,我们可以使用{Binding DataContext, ElementName=This}绑定到视图模型。
现在我们可以绑定到视图模型,我们必须将其与ContextMenu.DataContext连接起来。我使用对象的Tag属性,并将ContextMenuPlacementTarget)作为该连接,在本例中为Grid

<DataTemplate x:Key="YourTemplate" DataType="{x:Type DataTypes:YourDataType}">
    <Grid ContextMenu="{StaticResource Menu}" Tag="{Binding DataContext, 
        ElementName=This}">
        ...
    </Grid>
</DataTemplate>

字符串
然后,我们可以通过将ContextMenu.DataContext属性绑定到PlacementTarget.Tag属性(在我们的示例中是Grid的属性)来访问ContextMenu中的视图模型属性和命令:

<ContextMenu x:Key="Menu" DataContext="{Binding PlacementTarget.Tag, RelativeSource=
    {RelativeSource Self}}">
    <MenuItem Header="Delete" Command="{Binding DeleteFile}" CommandParameter=
        "{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource 
        AncestorType=ContextMenu}}" CommandTarget="{Binding PlacementTarget, 
        RelativeSource={RelativeSource Self}}" />
</ContextMenu>


请注意MenuItem.CommandTarget属性上的绑定。设置此值可确保引发指定命令的目标元素是PlacementTarget,在本例中是Grid
还要注意CommandParameter绑定。它绑定到PlacementTargetDataContext,或者在本例中绑定到GridGridDataContext将从DataTemplate继承,因此如果您使用ICommand接口的某些实现,则您的数据项现在绑定到Command中的object参数:

public bool CanExecuteDeleteFileCommand(object parameter)
{
    return ((YourDataType)parameter).IsInvalid;
}

public void ExecuteDeleteFileCommand(object parameter)
{
    Delete((YourDataType)parameter);
}


或者,如果你直接在视图模型中使用某种RelayCommand委托:

public ICommand Remove
{
    get 
    {
        return new ActionCommand(execute => Delete((YourDataType)execute), 
            canExecute => return ((YourDataType)canExecute).IsInvalid); 
    }
}

rseugnpd

rseugnpd2#

我们有同样的问题,但它的工作随机。contextmenu在controltemplate内,样式为列表框。我们尝试将contextmenu移动到模板内的不同级别,但发生了相同的错误。
我们认为这可能与刷新我们的ICollectionView有关,ICollectionView是ListBox的itemssource。
看起来当视图刷新时,在设置PlacementTarget之前,上下文菜单中的相对源绑定被评估。
感觉像是collectionviewsource或WPF的ContextMenu中的bug...

xpszyzbs

xpszyzbs3#

下面是一个基于您的测试用例的独立XAML示例:ContextMenu,使用Tag从其PlacementTargetDataContext检索Command。您可以重新引入部分代码,直到它停止工作,以尝试找到问题所在:

<Grid>
    <Grid.Resources>
        <PointCollection x:Key="sampleData">
            <Point X="10" Y="20"/>
            <Point X="30" Y="40"/>
        </PointCollection>
        <DataTemplate x:Key="_ItemTemplateA">
            <Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}}">
                <TextBlock Text="{Binding X}"/>
                <Grid.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" CommandParameter="{Binding}"/>
                    </ContextMenu>
                </Grid.ContextMenu>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    <DockPanel DataContext="{x:Static ApplicationCommands.Open}">
        <ListBox ItemTemplate="{StaticResource _ItemTemplateA}" ItemsSource="{StaticResource sampleData}"/>
    </DockPanel>
</Grid>

字符串

k10s72fa

k10s72fa4#

在这个post中,
当您在按钮上单击鼠标右键时,ContextMenuService.PlacementTarget将填充。
它表示ContextMenu。当菜单显示时,PlacementTarget被填充。你可以通过snoop来检查。
编辑1这段代码运行良好。

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowBaseStyle}">
        <Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=DataContext}"/>
        <Setter Property="ContextMenu">
            <Setter.Value>
                <ContextMenu Style="{StaticResource ContextMenuStyle}" 
                             ItemContainerStyle="{StaticResource MenuItemStyle}">
                    <MenuItem Header="Enable" Command="{Binding Path=PlacementTarget.Tag.(viewModels:PrinterListPageViewModel.EnableCommand), RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
                </ContextMenu>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

字符串

yb3bgrhw

yb3bgrhw5#

我知道这是一个老问题,但这个bug的有效解决方案是创建一个继承自ContextMenuclass,然后添加一个新的DependencyProperty,类型为PlacementMode,名称为例如MenuPlacement,然后重写新菜单类的OnOpened void,最后将MenuPlacement属性的值分配给原始Placement属性。

相关问题