Страницы

Поиск по вопросам

среда, 19 июня 2019 г.

c#, MVVM, Command, ContextMenu Binding

Столкнулся с проблемой. Имеется внутри View

И внутри ViewModel такое
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; }); }
В (can) все время (при запуске страницы) заходит null. Когда же я выбираю в ListBox элементо и жмакаю правой кнопкой, то меню айтем с данной командой не доступен.
Директория и файл существуют. Event - мой класс, в котором есть поле строковое Persons; Команду видит, биндинг работает.
Также прикрепляю класс RelayCommand - paste.org.ru/?wu3n62


Ответ

Проблема вот в чём.
У вас дана реализация CanExecute, но нет реализации CanExecuteChanged. Поэтому изменения CanExecute не подхватываются.
Есть несколько путей решения этой проблемы.
Во-первых, ваша реализация RelayCommand (это нестандартный класс, значит, вы берёте его из какого-то фреймворка или написали самостоятельно) может предоставлять возможность запустить CanExecuteChanged. Тогда вы должны сами реализовать логику, которая определяет эти обстоятельства, и «дёргает» CanExecuteChanged в нужный момент. Этот путь может быть сложен, если ваш параметр меняется со временем.
Во-вторых, вы можете в тот момент, когда по логике программы команда может активироваться или деактивироваться, вызвать вручную CommandManager.InvalidateRequerySuggested. После этого значение CanExecute будет перечитано.

У меня и правда InvalidateRequerySuggested не сработало на RelayCommand. Я поменял его на RoutedUICommand, и всё взлетело.
Смотрите. Во-первых, определяем нужную команду:
public class GlobalCommands : RoutedUICommand { private GlobalCommands() { } // запрещаем создавать экземпляры снаружи
public static GlobalCommands ShowStudents = new GlobalCommands() { Text = "Отобразить список студентов" }; // сюда можно добавлять ещё команды }
Затем, в главной VM создаём CommandBinding
public IEnumerable SupportedBindings { get { return supportedBindings; } } List supportedBindings;
void InitCommands() { supportedBindings = new List() { 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"); }
Таким образом создаётся привязка команды к её реализации.
Затем, привязки нужно зарегистрировать, чтобы какой-нибудь UI-объект их обрабатывал. Например, окно. Для этого в App.xaml.cs пишем:
// вызывается на 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(); }
И наконец, в UI пишем такое:

В таком варианте код работает.

Комментариев нет:

Отправить комментарий