从ItemsControl中绑定WPF - FindAncestor不起作用

时间:2011-03-08 17:43:03

标签: wpf data-binding findancestor

下午好,

我有一个看似非常普遍的问题。我有一个用户控件,它有一个View Model作为其数据上下文。然后,此用户控件中的元素将此ViewModel用于绑定目的等。

ViewModel

public class TicketDetailsViewModel : ViewModelBase
{
    public DelegateCommand<object> HideSelectedText { get; private set; }

    private Ticket _ticket;
    public Ticket Ticket
    {
        get { return _ticket; }
        set
        {
            _ticket = value;
            this.RaisePropertyChanged(p => p.Ticket);
        }
    }
}

我的ViewModel包含一个Ticket对象。此票证对象附有一组注释,并使用ItemsControl将其呈现给票证显示用户控件

 <ItemsControl ItemsSource="{Binding Path=Ticket.Comments}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border CornerRadius="15" Background="{Binding Path=CommentType, ConverterParameter=CommentType, Converter={StaticResource ResourceKey=commentColorConverter}}" Padding="10" Margin="40,10,40,0">
                                <TextBox x:Name="tbComment" Text="{Binding CommentText}" IsReadOnly="True">
                                <TextBox.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="Spam" Command="{Binding Path=DataContext.HideSelectedText,RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type UserControl} }}">

                                        </MenuItem>
                                    </ContextMenu>
                                </TextBox.ContextMenu>
                            </TextBox>
                        </Border>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>  
                            <StackPanel Orientation="Vertical"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>  

您会注意到此ItemsControl呈现的每个TextBox都附加了一个ContextMenu。我想要做的是将此ContextMenuItem的命令绑定到我的ViewModel中的DelegateCommand。当然,只需使用;

<MenuItem Header="Spam" Command="{Binding HideSelectedText}">

我们没有得到任何有用的东西,因为这个上下文中的'Binding'等于Ticket.Comment,因此不知道HideSelectedText究竟是什么。

似乎有许多类似的问题,并且所有答案似乎都转向了RelativeSource解决方案。正如你在我原来的XAML代码中看到的那样,我已经尝试了这个以及很多其他版本(使用和不使用AncestorLevel设置,使用AncestorType = {x:Type ItemsControl},AncestorType = {x :类型Window},AncestorType = {x:Type DataTemplate}等)和ALL产生类似于的输出错误;

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.UserControl', AncestorLevel='1''. BindingExpression:Path=DataContext.HideSelectedText; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

System.Windows.Data Error: 40 : BindingExpression path error: 'HideSelectedText' property not found on 'object' ''TicketComment' (HashCode=49290260)'. BindingExpression:Path=DataContext.HideSelectedText; DataItem='ContextMenu' (Name=''); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

那么,为什么这个解决方案似乎适用于这么多人,但对我来说,只需输入{Binding HideSelectedText}就没有什么区别?

3 个答案:

答案 0 :(得分:3)

ContextMenus实际上并不是WPF的VisualTree的一部分,因此Bindings不会像被驱逐的那样工作。作为替代方案,请尝试此绑定:

<MenuItem Header="Spam" Command="{Binding PlacementTarget.DataContext.HideSelectedText,
    RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" />

答案 1 :(得分:1)

最好的解决方案是通过资源完成。 如果将在资源中创建ContextMenu,它将自动从父级继承DataContext。 然后,只需要创建一个静态资源传递值的样式。

示例:

<DataGrid.Resources>
    <ContextMenu x:Key="test_ContextMenu" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
        <MenuItem Header="Test Header" Command="{Binding Path=TestCommand, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
    </ContextMenu>
</DataGrid.Resources>

<DataGrid.ItemContainerStyle>
    <Style TargetType="DataGridRow">
        <Setter Property="ContextMenu" Value="{StaticResource test_ContextMenu}"/>
    </Style>
</DataGrid.ItemContainerStyle>

此代码在.NET 4.5上进行了测试,但我认为它也适用于早期版本。

答案 2 :(得分:0)

由于Context Menu不是可视化树的一部分,因此默认情况下它不会继承DataContext。您必须将上下文菜单的DataContext设置为PlacementTarget,如下所示 -

<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext}">
   <ContextMenuItem Header="Item1" Command="{Binding YourCommand}" />
</ContextMenu>

希望这有帮助。

-VJ