代码之家  ›  专栏  ›  技术社区  ›  Andrew

关于WPF上下文菜单的Xaml名称范围和模板问题

  •  0
  • Andrew  · 技术社区  · 16 年前

    除了ContextMenu上的绑定之外,下面代码中的所有内容都可以工作。这显然是由于ContextMenu位于样式内部的事实,这使得它与xaml的其余部分处于不同的名称范围中。我正在寻找一个解决方案,我将不必实例化一个ContextMenu在代码背后,因为我必须应用解决方案的应用程序包含一个非常大的ContextMenu与许多绑定。必须有一种方法在xaml中实现这一点,否则它看起来像是一个严重的疏忽。还要注意,我已经尝试使用VisualTreeHelper和LogicalTreeHelper遍历元素树,但是我无法从窗口的根元素中找到ContextMenu(这些类显然跳过了感兴趣的元素)。总之,所有的代码都在下面。这可以粘贴到visualstudio中的一个新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;
            }
        }
    }
    

    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));
            }
        }
    }
    

    安得烈

    2 回复  |  直到 16 年前
        1
  •  0
  •   Goblin    16 年前

    好吧-我在理解你想要完成的事情上有点困难-但请尝试以下方法:

    <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}"/>
         ...
    

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

        2
  •  0
  •   Andrew    16 年前

    好的,这个解决方案是有效的:我修改了WindowV.xaml和WindowV.xaml.cs文件如下。下面的更正修复了xaml中有关namescope的问题。

    <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;
            }
        }
    }
    

    安得烈

    推荐文章