I have an ObservableCollection<T>
in a class and i want to bind the IsEnabled
property of a MenuItem
Element with 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>();
}
The idiomatic way to do this would be to bind your menu item to a command and have the command's CanExecute
handler check whether your Items
collection is empty. The MenuItem
will automatically be disabled if CanExecute
returns false
.
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));
}
}
As per the MVVM pattern, you should set the DataContext
of your screen (eg, your UserControl
, Page
, or Window
) to an instance of the view model.
To hook up your menu item to the command, modify your Xaml as follows:
<MenuItem Command="{Binding MenuItemCommand}"
Header="MENU" />
There are countless examples of DelegateCommand
implementations out there (sometimes called RelayCommand
), but I'll include one here for completeness:
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
If you insist on not using commands, you could do the following:
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));
}
}
And update your Xaml as follows:
<MenuItem IsEnabled="{Binding HasItems}"
Header="MENU" />
You could use a Style
and and bind to the Count
property directly:
<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>
The ObservableCollection<T>
raises a change notification each time the Count
property changes.
You can bind to ObservableCollection<T>.Count
property and create a value converter which converts 0 to false and non-zero to 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();
}
}
Then add your converter to resources and use in binding:
<MenuItem x:Name="MyMenu"
IsEnabled="{Binding Items.Count, Converter={StaticResource HasItemsConverter}}"
Header="MENU">
</MenuItem>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.