Xaml Namescope и шаблон вопроса о WPF ContextMenu - PullRequest
0 голосов
/ 06 июня 2010

Все в приведенном ниже коде работает, кроме привязки в ContextMenu. Это, очевидно, связано с тем, что ContextMenu находится внутри стиля, который помещает его в область имен, отличную от остальной части xaml. Я ищу решение, в котором мне не нужно будет создавать экземпляр ContextMenu в программном коде, поскольку приложение, в котором я должен применить решение, содержит очень большое ContextMenu с большим количеством привязок. Должен быть способ сделать это в xaml, иначе это может показаться серьезным упущением. Также обратите внимание, что я уже пробовал обходить дерево элементов, используя VisualTreeHelper и LogicalTreeHelper, но я не смог найти ContextMenu из корневого элемента Window (эти классы, очевидно, пропустили интересные элементы). Во всяком случае, весь код ниже. Это можно вставить в новое приложение WPF в Visual Studio, и ничего не пропало.

Вот код для 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();
        }
    }
}

Вот xaml для того, что изначально было Window1:

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

Любое понимание будет очень оценено. Большое спасибо!

Andrew

Ответы [ 2 ]

0 голосов
/ 07 июня 2010

Хорошо, это решение работает: я изменил файлы 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;
        }
    }
}

Andrew

0 голосов
/ 07 июня 2010

Хорошо - у меня возникли небольшие проблемы с тем, что вы пытаетесь выполнить, - но попробуйте следующее:

<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 Window, у которого нет свойства с именем MenuItem1 и т. Д. Свойство DataContext содержит данные, которые вас интересуют.

...