WPF:具有可观察集合项的绑定已启用菜单<T>项。Any()



我在一个类中有一个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(例如,UserControlPageWindow(设置为视图模型的实例。

若要将菜单项挂接到命令,请按如下所示修改 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>

最新更新