MenuItem.IsEnabled связан с тем, есть ли что-то выбранное в списке или нет, но он не обновляется

У меня есть MenuItem, который должен быть включен, только если что-то выбрано в ListBox. Я написал конвертер из объекта в bool, который возвращает false, если этот объект == ноль, и true в противном случае. Я привязал его к ListBox.SelectedItem с моим конвертером, но он не работает. Размещение точки останова в конвертере показывает, что он никогда не работает. Элемент MenuItem всегда включен независимо от того, что.

Вот код xaml ListBox и MenuItem

<ListBox Name="TestsListBox" 
         HorizontalAlignment="Left" Height="93" VerticalAlignment="Top" Width="128"
         Margin="0,5,-1.723,0" ItemsSource="{Binding Path=Tests, Mode=OneWay}">
    <ListBox.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Remove" Click="removeTest"
                      IsEnabled="{Binding ElementName=TestsListBox, Mode=OneWay,
                                          Path=SelectedItem, Converter={StaticResource ObjectToBool}}"/>
        </ContextMenu>
    </ListBox.ContextMenu>
</ListBox>

Здесь я покажу, как конвертер объявлен как ресурс окна

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:System="clr-namespace:System;assembly=mscorlib"
        xmlns:ClassesLib="clr-namespace:Laba1;assembly=ClassesLib"
        xmlns:local="clr-namespace:WpfApplication"
        Title="MainWindow" Height="450" Width="525">
    <Window.Resources>
        <local:ObjectToBoolConverter x:Key="ObjectToBool"/>
    </Window.Resources>

А вот и конвертер класса

namespace WpfApplication
{
    class ObjectToBoolConverter: IValueConverter
    {
        // Converts value to boolean. If value is null, returns false.
        // Otherwise returns true
        public object Convert(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            if (null == value)
            {
                return false;
            }
            return true;
        }
        public object ConvertBack(object value, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException("This is oneway converter, so ConvertBack is not supported");
        }
    }
}

3 ответа

Решение

RelativeSource и Popup

Отсюда вы сможете выяснить, что причина, по которой привязка ElementName не работает, заключается в том, что ContextMenu не является частью визуального дерева, как другие элементы управления, и поэтому не может принимать участие в таких сценариях привязки. AFAIK, PopUps имеют свойство PlacementTarget, к которому вы можете привязать и выяснить, как его использовать.

Вот как я это решил:

ПОСМОТРЕТЬ:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:WpfApplication2"
          Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:ObjectToBoolConverter x:Key="ObjectToBool"/>
        <ContextMenu x:Key="contextMenu" DataContext="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource Self}}">
            <MenuItem Header="Remove" Click="removeTest"
                      IsEnabled="{Binding Path=., Converter={StaticResource ObjectToBool}}"/>

        </ContextMenu>
    </Window.Resources>

    <Grid>
        <ListBox Name="TestsListBox" 
         HorizontalAlignment="Left" Height="93" VerticalAlignment="Top" Width="128" 
         Margin="0,5,-1.723,0" ContextMenu="{StaticResource ResourceKey=contextMenu}">
        </ListBox>
    </Grid>
</Window>

Код позади

using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            List<string> teste = new List<string>();
            teste.Add("test1");
            teste.Add("test3");
            teste.Add("test2");

            TestsListBox.ItemsSource = teste;

        }

        private void removeTest(object sender, RoutedEventArgs e)
        {

        }
    }
}

Конвертер остался прежним.

С Уважением,

Похоже, свойство ElementName в Binding не делает то, что я думал. Также очень отстойно, что XAML просто игнорирует и ничего не делает с неверными параметрами Binding: вместо этого должно появиться сообщение об ошибке. Я добавил DataContext в свое ContextMenu, удалил ElementName, и теперь он работает. Вот как я изменил код:

<ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}" >
    <MenuItem Header="Add" Click="addTest"/>
    <MenuItem Header="Remove" Click="removeTest"
              IsEnabled="{Binding Mode=OneWay,
                                  Path=SelectedItem, Converter={StaticResource ObjectToBool}}"/>
</ContextMenu>

Комментарий Dtex о дубликате помог мне в этом, хотя я думал, что мог бы использовать ElementName вместо DataContext.

Другие вопросы по тегам