ContextMenu 不会根据 Binding 改变 MenuItem.Visibility 属性
ContextMenu doesn't change MenuItem.Visibility property according to Binding
我用 MainWindow 和 Model 创建了一个简单的项目来说明问题。
我的目标是在某些 ObservableCollection 为空时隐藏 MenuItem。在 Converter 的帮助下,我设法为 "Visibility" 属性 创建了一个绑定。
但问题是第一次打开 ContextMenu 时的状态是锁定的,即使在更改绑定到 "Visibility" 的 "Items" 对象后,它也不会再更改其状态。
这意味着如果我在任何按钮之前按 RMB,则只会显示 "BBB" MenuItem。但是如果我之前按下 "Show AAA" 按钮,"AAA" 将显示在 ContextMenu 中。
我想从 ContextMenu 外部控制 appearing/disappearing of "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 又名模型是:
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();
}
}
}
如我的评论所述,您的绑定仅评估 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
}
这将在您 collection 内容更改时触发 PropertyChanged
事件。也许其他人可以想出更优雅的解决方案。
我用 MainWindow 和 Model 创建了一个简单的项目来说明问题。 我的目标是在某些 ObservableCollection 为空时隐藏 MenuItem。在 Converter 的帮助下,我设法为 "Visibility" 属性 创建了一个绑定。 但问题是第一次打开 ContextMenu 时的状态是锁定的,即使在更改绑定到 "Visibility" 的 "Items" 对象后,它也不会再更改其状态。 这意味着如果我在任何按钮之前按 RMB,则只会显示 "BBB" MenuItem。但是如果我之前按下 "Show AAA" 按钮,"AAA" 将显示在 ContextMenu 中。 我想从 ContextMenu 外部控制 appearing/disappearing of "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 又名模型是:
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();
}
}
}
如我的评论所述,您的绑定仅评估 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
}
这将在您 collection 内容更改时触发 PropertyChanged
事件。也许其他人可以想出更优雅的解决方案。