ContextMenu不会根据Binding更改MenuItem.Visibility属性

时间:2015-03-30 15:03:42

标签: wpf data-binding contextmenu

我用MainWindow和Model创建了一个简单的项目来显示问题。 我的目的是在某些ObservableCollection为空时隐藏MenuItem。我设法在Converter的帮助下为“Visibility”属性创建了一个绑定。 但问题是第一次打开ContextMenu时的状态是锁定的,甚至在更改绑定到“可见性”的“Items”对象之后,它也不再改变其状态。 这意味着如果我在任何按钮之前按下RMB,则只显示“BBB”MenuItem。但如果我之前按“显示AAA”按钮,“AAA”将在ContextMenu中显示。 我希望从ContextMenu外部控制“AAA”MenuItem的出现/消失。

MainWindow.xaml:

<Window x:Class="TestWpfContextMenu.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:testWpfContextMenu="clr-namespace:TestWpfContextMenu">
    <Window.Resources>
        <testWpfContextMenu:C2VConverter x:Key="Converter"/>
    </Window.Resources>
    <Window.ContextMenu>
        <ContextMenu>
            <MenuItem Header="AAA" Visibility="{Binding Items, Converter={StaticResource Converter}, Mode=OneWay}"/>
            <Separator Visibility="{Binding Items, Converter={StaticResource Converter}, Mode=OneWay}"/>
            <MenuItem Header="BBB"/>
        </ContextMenu>
    </Window.ContextMenu>
    <Grid>
        <Button Content="Show AAA" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
        <Button Content="Hide AAA" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
    </Grid>
</Window>

使用代码隐藏:

using System;
using System.Collections;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace TestWpfContextMenu
{
    public partial class MainWindow : Window
    {
        public MainWindow(Model model)
        {
            DataContext = model;
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ((Model) DataContext).Show();
        }
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            ((Model)DataContext).Hide();
        }
    }
    public class C2VConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var elements = (IEnumerable )value;
            if (elements == null)
                return Visibility.Collapsed;

            return elements.Cast<object>().Any() ? Visibility.Visible : Visibility.Collapsed;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

和DataContext aka Model是:

using System.Collections.ObjectModel;
namespace TestWpfContextMenu
{
    public class Model
    {
        public Model()
        {
            Items = new ObservableCollection<object>();
        }
        public ObservableCollection<object> Items { get; set; }
        public void Show()
        {
            Items.Add(new object());
        }
        public void Hide()
        {
            Items.Clear();
        }
    }
}

1 个答案:

答案 0 :(得分:2)

正如我的评论中所述,您的绑定仅评估Items属性本身。 您可以在模型中执行以下操作:

public class Model : INotifyPropertyChanged
{
   public Model()
   {
     Items = new ObservableCollection<object>();
   }

  void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  {
     this.OnPropertyChanged("Items");
  }

  protected ObservableCollection<object> _Items;
  public ObservableCollection<object> Items
  {
     get
     {
        return this._Items;
     }
     set
     {
        if (this._Items == value)
        {
           return;
        }

        if(this._Items != null)
        {
           this._Items.CollectionChanged -= Items_CollectionChanged;
        }
        this._Items = value;
        this._Items.CollectionChanged += Items_CollectionChanged;
        this.OnPropertyChanged();
     }
  }

  public void Show()
  {
     Items.Add(new object());
  }
  public void Hide()
  {
     Items.Clear();
  }

  #region INPC
  public event PropertyChangedEventHandler PropertyChanged;

  protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = "")
  {
     PropertyChangedEventHandler tmp = this.PropertyChanged;
     if (tmp != null)
     {
        tmp(this, new PropertyChangedEventArgs(name));
     }
  }

  #endregion
}

当收集内容发生变化时,这将触发PropertyChanged事件。也许其他人可以提出更优雅的解决方案。