Faced a problem. Available inside View

<ListBox.ItemTemplate> <DataTemplate> <local:iEventControl> <local:iEventControl.ContextMenu> <ContextMenu> <MenuItem Header="Отобразить список" Command="{Binding Path=DataContext.ShowCommand, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}" CommandParameter="{Binding}" /> </ContextMenu> </local:iEventControl.ContextMenu> </local:iEventControl> </DataTemplate> </ListBox.ItemTemplate> 

And inside the ViewModel is

 public RelayCommand ShowCommand { get; set; } void InitCommands() { ShowCommand = new RelayCommand( x => Show(x), (can) => { return can == null ? false : Directory.Exists(ApplicationPath.Groups + (can as Event).Persons + @"/") ? File.Exists(ApplicationPath.Groups + (can as Event).Persons + @"/" + "GroupList.dat") : false; }); } 

In (can) all the time (when you start the page) comes null. When I select the elements in ListBox and click the right button, the item menu with this command is not available.

Directory and file exist. Event - my class in which there is a field string Persons; The team sees the binding works.

I also attach the RelayCommand class - paste.org.ru/?wu3n62

    1 answer 1

    The problem is this.

    You have a implementation of CanExecute , but no implementation of CanExecuteChanged . Therefore, changes to CanExecute not picked up.

    There are several ways to solve this problem.

    First, your implementation of RelayCommand (this is a non-standard class, which means that you take it from some framework or have written it yourself) may provide an opportunity to run CanExecuteChanged . Then you yourself must implement the logic that determines these circumstances, and " CanExecuteChanged " CanExecuteChanged at the right time. This path can be complicated if your parameter changes over time.

    Secondly, you can, at the moment when, according to the logic of the program, a command can be activated or deactivated, you can manually call CommandManager.InvalidateRequerySuggested . After that, the value CanExecute will be re-read.


    InvalidateRequerySuggested really didn't work on RelayCommand . I changed it to RoutedUICommand , and everything took off.

    See it. First, we define the necessary command:

     public class GlobalCommands : RoutedUICommand { private GlobalCommands() { } // запрещаем создавать экземпляры снаружи public static GlobalCommands ShowStudents = new GlobalCommands() { Text = "Отобразить список студентов" }; // сюда можно добавлять ещё команды } 

    Then, in the main VM create the CommandBinding :

     public IEnumerable<CommandBinding> SupportedBindings { get { return supportedBindings; } } List<CommandBinding> supportedBindings; void InitCommands() { supportedBindings = new List<CommandBinding>() { new CommandBinding( GlobalCommands.ShowStudents, (sender, args) => ShowStudents(args.Parameter), (sender, args) => args.CanExecute = CanShowStudents(args.Parameter)) }; } // ... void ShowStudents(object iEvent) { if (iEvent is Event) { var _event = iEvent as Event; // загрузить конкретную группу } } bool CanShowStudents(object iEvent) { Debug.WriteLine("CanShowStudents called"); var ev = iEvent as Event; if (ev == null) return false; Debug.WriteLine($"CanShowStudents, persons = {ev.Persons}"); return Directory.Exists(AppPathGroups + @"\" + ev.Persons) && File.Exists(AppPathGroups + @"\" + ev.Persons + @"\" + "GroupList.dat"); } 

    This creates a team binding to its implementation.

    Then, bindings need to be registered in order for some UI object to process them. For example, a window. To do this, in App.xaml.cs we write:

     // вызывается на Application.Startup void ApplicationStartup(object sender, StartupEventArgs e) { var vm = new ViewModel(); foreach (var binding in vm.SupportedBindings) CommandManager.RegisterClassCommandBinding(typeof(Window), binding); var w = new Window1(); w.DataContext = vm; w.ShowDialog(); } 

    And finally, in the UI we write this:

     <ContextMenu> <MenuItem Header="Отобразить список студентов" Command="{x:Static mvvm:GlobalCommands.ShowStudents}" CommandParameter="{Binding}"/> </ContextMenu> 

    In this embodiment, the code works.

    • Here is my class - paste.org.ru/?wu3n62 , @VladD. I also use this class in another place by the same principle (CommandParametr! = Null, only ListBox.SelectedItem is passed as a parameter) and there CanExecute changes depending on whether lbItem is selected or not. As for manual activation, it seems to me that this is not an ideal solution for everyone. Moreover, the command is zabindena on the context menu (code above), then in which case to call manually? Thank you - bodynar
    • @bodynar: So, with a fixed can , your CanExecute value cannot change, I understand correctly? (The class is better to add to the question.) - VladD
    • can is just the name of a variable in a lambda expression. Or I did not quite understand you. - bodynar
    • @bodynar: Well, for the same can value, can the calculated value CanExecute change during the program run? - VladD
    • No, the value of CanExecute should be calculated 1 time, when fetching ListBoxItem , @VladD. Those. ListBoxItem got RightClick , the context menu fell out and the command should be available. I start thinking about changing the command, remove CanExecute and add to the handler a check similar to CanExecute - bodynar