【问题标题】:Xaml Namescope and Template Question Concerning WPF ContextMenu关于 WPF ContextMenu 的 Xaml 名称范围和模板问题
【发布时间】:2010-06-06 19:14:37
【问题描述】:

除了 ContextMenu 上的绑定之外,以下代码中的所有内容都有效。这显然是因为 ContextMenu 位于 Style 内部,这将其置于与 xaml 的其余部分不同的名称范围内。我正在寻找一个解决方案,我不必在代码隐藏中实例化 ContextMenu,因为我必须应用该解决方案的应用程序包含一个非常大的 ContextMenu 和很多绑定。必须有一种方法可以在 xaml 中完成此操作,否则这似乎是一个严重的疏忽。另请注意,我已经尝试使用 VisualTreeHelper 和 LogicalTreeHelper 遍历元素树,但我无法从 Window 的根元素中找到 ContextMenu(这些类显然跳过了有趣的元素)。无论如何,所有代码都在下面。这可以粘贴到 Visual Studio 中的新 WPF 应用程序中,并且不会丢失任何内容。

这是 App.xaml.cs 的代码(xaml 保持不变):

using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            WindowV windowV = new WindowV();
            WindowVM windowVM = new WindowVM();

            windowV.DataContext = windowVM;

            windowV.Show();
        }
    }
}

这是原来 Window1 的 xaml:

<Window x:Class="WpfApplication1.WindowV"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:WpfApplication1"
        Name="MainWindow"
        Title="WindowV" Height="300" Width="300">
    <Window.Resources>
        <Style TargetType="{x:Type ItemsControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsLocked}" Value="true">
                    <Setter Property="ItemsSource" Value="{Binding LockedList}" />
                    <Setter Property="ItemTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsLocked}" Value="false">
                    <Setter Property="ItemsSource" Value="{Binding RegularList}" />
                    <Setter Property="ItemTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding}">
                                    <TextBlock.ContextMenu>
                                        <ContextMenu>
                                            <MenuItem Header="{Binding MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                                            <MenuItem Header="{Binding MenuItem2, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                                            <MenuItem Header="{Binding MenuItem3, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
                                        </ContextMenu>
                                    </TextBlock.ContextMenu>
                                </TextBlock>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="4*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <ItemsControl Grid.Row="0" />
        <Button Name="ToggleButton"
                Grid.Row="1"
                Content="Toggle Lock"
                Click="OnToggleLock" />
    </Grid>
</Window>

这是最初 Window1 的代码隐藏:

using System.Windows;
using System.Windows.Markup;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class WindowV : Window
    {
        public WindowV()
        {
            InitializeComponent();
        }

        private void OnToggleLock(object sender, RoutedEventArgs e)
        {
            if (((WindowVM)(DataContext)).IsLocked == true)
                ((WindowVM)(DataContext)).IsLocked = false;
            else
                ((WindowVM)(DataContext)).IsLocked = true;
        }
    }
}

在名为 WindowVM 的项目中添加了一个新类。这是它的代码:

using System.Collections.Generic;
using System.ComponentModel;

namespace WpfApplication1
{
    public class WindowVM : INotifyPropertyChanged
    {
        public string MenuItem1
        {
            get
            {
                string str = "Menu item 1";
                return str;
            }
        }
        public string MenuItem2
        {
            get
            {
                string str = "Menu item 2";
                return str;
            }
        }
        public string MenuItem3
        {
            get
            {
                string str = "Menu item 3";
                return str;
            }
        }

        public List<string> LockedList
        {
            get
            {
                List<string> list = new List<string>();
                list.Add("This items control is currently locked.");
                return list;
            }
        }
        public List<string> RegularList
        {
            get
            {
                List<string> list = new List<string>();
                list.Add("Item number 1.");
                list.Add("Item number 2.");
                list.Add("Item number 3.");
                return list;
            }
        }

        private bool _isLocked;
        public bool IsLocked
        {
            get { return _isLocked; }
            set
            {
                if (_isLocked != value)
                {
                    _isLocked = value;
                    OnPropertyChanged("IsLocked");
                }
            }
        }

        public WindowVM()
        {
            IsLocked = false;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }
}

任何见解将不胜感激。非常感谢!

安德鲁

【问题讨论】:

  • 编辑:好的,在这一点上,我什至在代码隐藏中遇到了困难。有什么想法吗?
  • 在上面的问题中,我刚刚意识到我确实对 App.xaml 文件进行了一次调整。具体来说,我删除了包含启动 url 的行。除此之外,App.xaml 文件保持不变。否则,这些文件可以在 Visual Studio 中粘贴到新的 WPF 应用程序中,并且当应用程序运行时,右键单击的问题会很明显。

标签: wpf xaml binding contextmenu


【解决方案1】:

好的 - 我在执行您要完成的任务时遇到了一些问题 - 但请尝试以下操作:

<ContextMenu>
     <MenuItem Header="{Binding DataContext.MenuItem1, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" />
     ...

为了让您的代码更简单,您可以尝试:

<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
     <MenuItem Header="{Binding MenuItem1}"/>
     ...

问题是您绑定到没有名为 MenuItem1 等属性的 UI 元素 Window。DataContext 属性包含您感兴趣的数据。

【讨论】:

  • 感谢您的回复,但这些解决方案似乎都不起作用。
  • 运行您提出的第二个建议会在 Visual Studio 的输出窗口中生成以下行:“System.Windows.Data 错误:4:找不到与引用‘RelativeSource FindAncestor, AncestorType= 进行绑定的源’ 'System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext; DataItem=null; 目标元素是 'ContextMenu' (Name='contextMenu'); 目标属性是 'DataContext' (类型 'Object' )”。我认为这是问题所在......
【解决方案2】:

好的,此解决方案有效:我更改了 WindowV.xaml 和 WindowV.xaml.cs 文件,如下所示。以下更正解决了有关 xaml 中名称范围的问题。

这是新的 WindowV.xaml 文件:

<Window x:Class="WpfApplication1.WindowV"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:src="clr-namespace:WpfApplication1"
        Name="RootElement"
        Title="WindowV" Height="300" Width="300">
    <Window.Resources>
        <ContextMenu x:Key="myContextMenu" DataContext="{Binding Path=DataContext, ElementName=RootElement}">
            <MenuItem Header="{Binding MenuItem1}" />
            <MenuItem Header="{Binding MenuItem2}" />
            <MenuItem Header="{Binding MenuItem3}" />
        </ContextMenu>
        <Style TargetType="{x:Type ItemsControl}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsLocked}" Value="true">
                    <Setter Property="ItemsSource" Value="{Binding LockedList}" />
                    <Setter Property="ItemTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding IsLocked}" Value="false">
                    <Setter Property="ItemsSource" Value="{Binding RegularList}" />
                    <Setter Property="ItemTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" ContextMenu="{StaticResource myContextMenu}" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="4*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <ItemsControl Grid.Row="0" />
        <Button Name="ToggleButton"
                Grid.Row="1"
                Content="Toggle Lock"
                Click="OnToggleLock" />
    </Grid>
</Window>

这是相应的代码隐藏:

using System.Windows;
using System.Windows.Markup;
using System;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class WindowV : Window
    {
        public WindowV()
        {
            InitializeComponent();

            System.Windows.Controls.ContextMenu contextMenu =
                FindResource("myContextMenu") as System.Windows.Controls.ContextMenu;

            NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this as DependencyObject));
            contextMenu.RegisterName("RootElement", this);
        }

        private void OnToggleLock(object sender, RoutedEventArgs e)
        {
            if (((WindowVM)(DataContext)).IsLocked == true)
                ((WindowVM)(DataContext)).IsLocked = false;
            else
                ((WindowVM)(DataContext)).IsLocked = true;
        }
    }
}

安德鲁

【讨论】:

    猜你喜欢
    • 2020-01-31
    • 2013-01-29
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2011-10-30
    • 2022-12-28
    • 2021-02-27
    • 1970-01-01
    相关资源
    最近更新 更多