【问题标题】:MenuItem.IsEnabled is bound to whether there is something selected in Listbox or not, but it doesn't updateMenuItem.IsEnabled 绑定到列表框中是否选择了某些内容,但它不会更新
【发布时间】:2014-03-26 04:58:24
【问题描述】:

我有一个 MenuItem,只有在 ListBox 中选择了某些内容时才应该启用它。我写了一个从对象到布尔的转换器,如果该对象== null,则返回false,否则返回true。我用我的转换器将它绑定到 ListBox.SelectedItem,但它不起作用。在转换器中放置一个断点表明它永远不会运行。无论如何,MenuItem 始终处于启用状态。

这是 ListBox 和 MenuItem 的 xaml 代码

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

【问题讨论】:

  • 你的数据上下文设置了吗?您是否也尝试过将菜单项绑定到命令并实现 canExecute 方法?
  • 我的 DataContext 在 Window 的构造函数中设置为整个窗口到我的 ViewModel,ListBox 从中获取它的项目,但它与是否启用该项目无关。你说的我没试过,第一次听说这种方法。我不确定 'Click="removeTest"' 与将其绑定到命令有何不同。
  • 当使用 mvvm 模式时,最佳实践是将域(业务逻辑)与代码隐藏文件隔离开来。后面的代码应该尽可能少的逻辑,以增加复用的灵活性。这些命令有多种好处,例如可以更轻松地实现撤消模式。命令的另一个好处是它使您能够预先检查将自动启用和禁用按钮的条件。这有助于将条件逻辑与业务逻辑分开(换句话说,我只能在满足所有条件时执行该方法)
  • 我认为启用/禁用菜单项与业务逻辑无关。
  • 如果你有“oneWay”,当它更新源时它不会更新视图。

标签: c# wpf xaml listbox ivalueconverter


【解决方案1】:

我就是这样解决的:

查看:

<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)
        {

        }
    }
}

转换器保持不变。

问候,

【讨论】:

    【解决方案2】:

    看起来 Binding 的 ElementName 属性并没有像我想象的那样做。此外,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。

    【讨论】:

      【解决方案3】:

      RelativeSource and Popup

      从这里您应该能够发现 ElementName 绑定不起作用的原因是因为 ContextMenu 不像其他控件那样属于可视化树,因此不能参与此类绑定场景。 AFAIK,PopUps 有一个 PlacementTarget 属性,您可以绑定到该属性并弄清楚如何使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-02
        • 1970-01-01
        • 2011-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-23
        相关资源
        最近更新 更多