我在一个类中有一个ObservableCollection<T>
,我想将MenuItem
元素的IsEnabled
属性与ObservableCollection<T>
绑定
Items.Any()
XAML =>
<MenuItem x:Name="MyMenu"
IsEnabled="{Binding ????}"
Header="MENU">
</MenuItem>
C# =>
public class MyClass
{
public static MyClass Current = new MyClass();
public ObservableCollection<Object> Items { get; } = new ObservableCollection<Object>();
}
惯用解决方案:命令
执行此操作的惯用方法是将菜单项绑定到命令,并让命令的CanExecute
处理程序检查Items
集合是否为空。 如果CanExecute
返回false
,则该MenuItem
将自动禁用。
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<Object> Items { get; }
public DelegateCommand MenuItemCommand { get; }
public MyViewModel()
{
Items = new ObservableCollection<object>();
MenuItemCommand = new DelegateCommand(
() => { /* Do Something */ },
() => Items.Count > 0);
Items.CollectionChanged += (s, e) => MenuItemCommand.RaiseCanExecuteChanged();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
根据 MVVM 模式,您应该将屏幕的DataContext
(例如,UserControl
、Page
或Window
(设置为视图模型的实例。
若要将菜单项挂接到命令,请按如下所示修改 Xaml:
<MenuItem Command="{Binding MenuItemCommand}"
Header="MENU" />
有无数DelegateCommand
实现的例子(有时称为RelayCommand
(,但为了完整起见,我将在这里包含一个:
public class DelegateCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public DelegateCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public virtual bool CanExecute() => _canExecute?.Invoke() ?? true;
public virtual void Execute() => _execute();
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
public event EventHandler CanExecuteChanged;
protected virtual void OnCanExecuteChanged()
{
this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
return this.CanExecute();
}
void ICommand.Execute(object parameter)
{
this.Execute();
}
}
替代解决方案:绑定IsEnabled
如果您坚持不使用命令,则可以执行以下操作:
public class MyViewModel : INotifyPropertyChanged
{
public ObservableCollection<Object> Items { get; }
public bool HasItems => Items.Count > 0;
public MyViewModel()
{
Items = new ObservableCollection<object>();
Items.CollectionChanged += (s, e) => OnPropertyChanged(nameof(HasItems));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
并按如下方式更新您的 Xaml:
<MenuItem IsEnabled="{Binding HasItems}"
Header="MENU" />
Style
并直接绑定到 Count
属性:
<MenuItem x:Name="MyMenu" Header="MENU">
<MenuItem.Style>
<Style TargetType="MenuItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Items.Count}" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</MenuItem.Style>
</MenuItem>
每次Count
属性更改时,ObservableCollection<T>
都会引发更改通知。
您可以绑定到ObservableCollection<T>.Count
属性并创建一个值转换器,该转换器将 0 转换为 false,将非零转换为 true:
public class HasItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as int?) > 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
然后将转换器添加到资源并在绑定中使用:
<MenuItem x:Name="MyMenu"
IsEnabled="{Binding Items.Count, Converter={StaticResource HasItemsConverter}}"
Header="MENU">
</MenuItem>