#c_sharp #wpf #net #xaml
Придумал такую схему. Создал класс public class MyCommand : ICommand, ICollectionТо есть каждая команда включает еще вложенные команды (вложенные меню) В классе окна добавляю свойство public MyCommand Menu { get; set; } В дизайнере делаю так И так же в ресурсы окна добавляю вот что Все. В коде формирую дерево из MyCommand, и меню в окне создается автоматически по дереву. Класс MyCommand реализует слияние, так что я спокойно могу добавлять к основному меню дополнительные пункты (например меню, которое создано в контенте TabItem). Все гениально и просто :), вот только я не знаю, как отделить пункты меню сепараторами. Я ведь не оперирую контейнерами (MenuItem), поэтому не могу просто взять и добавить new Separator(). Посоветуйте что нибудь для этого.
Ответы
Ответ 1
Разделитель — это часть представления, а не вью-модели. Идея делать маппинг один-к-одному между абстрактными командами и элементами управления во всём приложении — порочна. Подобный подход может использоваться в отдельных случаях, например, при генерации списка недавно открытых файлов в меню или предоставлении плагину абстрактного интерфейса для управления представлением (и то я бы задумался об альтернативах). Запихнуть разделитель в список можно. Всего-то нужно сделать иерархию классов ("пункт меню", "пункт меню-разделитель", "пункт меню-команда"), присыпать шаблончиками и биндингами... Но хорошо задумайтесь: а правильно ли это? Если вы пойдёте по пути маппинга один-к-одному между элементами управления и вью-моделями, то вы превратите MVVM в фарс: у вас в слое вью-моделей появится упрощённая проекция представления, и представление станет полностью управляться из слоя вью-моделей. Сама вью-модель превратится в помесь контроллера и презентера. Это уже не MVVM. Короче, не надо так делать.Ответ 2
Отвечу не совсем на данный вопрос, но тоже про меню. Просто, в комментарии на один вопрос сказал, что покажу вариант, как можно собрать меню... Сепараторы тут тоже используются. Чтобы собрать меню, можно реализовать сервис AppCommandService, который позволяет регистрировать в себе реализации ICommand и содержит свойство Menu, к которому можно привязаться из xaml. Сразу скажу, что это не идеальная реализация такого сервиса, в том числе и потому что нельзя динамически добавлять и убирать пункты меню. Тут встроен IoC контейнер - Windsor, который в принципе можно заменить на другой контейнер или заменить коллекцией. Смысл такой: 1) команде даешь атрибут, который содержит строку с названиями пунктов разделенными \: [Menu(@"Файл\Сохранить Как\", "Сохранить Как PDF", Order = 2, Break = MenuBreak.Before)] public class SaveAsPdfCommand: ICommand { ... } //order - порядковый номер при отображении меню //Break - указывать, если надо поставить СЕПАРАТОР 2) Регистрируешь эту команду в сервисе CommandService.RegisterCommand(); //CommandService = new AppCommandService() - публичное свойство вью модели 3) Привязываешься к Xaml: Реализация: Атрибут: [AttributeUsage(AttributeTargets.Class, AllowMultiple=true)] public class MenuAttribute : Attribute { public string Path { get; protected set; } public string Name { get; protected set; } public MenuAttribute(string path, string name) :this(path) { Name = name; } protected MenuAttribute (string path) { Path = path; Break = MenuBreak.None; } public int Order { get; set; } public MenuBreak Break { get; set; } public static MenuAttribute Extract(Type t) { var attributes = t.GetCustomAttributes(typeof(MenuAttribute), false).OfType (); if (attributes == null || attributes.Count() == 0) return null; MenuAttribute result = attributes.FirstOrDefault(); //здесь можно вставить условие по выбору атрибута, если их много return result; } } public enum MenuBreak : byte { None, Before, After, Both } Сервис: public class AppCommandService { private IWindsorContainer _InternalContainer = new WindsorContainer(new XmlInterpreter(new StaticContentResource(@" "))); public void RegisterCommand () where T: ICommand { _InternalContainer.Register(Component.For ().LifeStyle.Singleton); } public T ResolveCommand () where T : ICommand { return _InternalContainer.Resolve (); } public void Execute (object parameter = null) where T : ICommand { var command = _InternalContainer.Resolve (); if (command != null && command.CanExecute(parameter)) { command.Execute(parameter); } } public void ExecuteOnAppDispatcher (object parameter = null) where T : ICommand { Application.Current.Dispatcher.BeginInvoke(new Action
Комментариев нет:
Отправить комментарий