I will answer not entirely on this question, but also about the menu. Simply, in the comments on one question said that I will show
option how to build a menu ...
Separators are also used here.
To build a menu, you can implement the AppCommandService service, which allows you to register an ICommand implementation in yourself and contains the Menu property to which you can snap from xaml .
I’ll say at once that this is not an ideal implementation of such a service, also because you cannot dynamically add and remove menu items. There is a built-in IoC container - Windsor, which in principle can be replaced with another container or replaced with a collection.
The meaning is:
1) give the command an attribute that contains a string with the item names separated by \ :
[Menu(@"Файл\Сохранить Как\", "Сохранить Как PDF", Order = 2, Break = MenuBreak.Before)] public class SaveAsPdfCommand: ICommand { ... } //order - порядковый номер при отображении меню //Break - указывать, если надо поставить СЕПАРАТОР
2) Register this command in the service
CommandService.RegisterCommand<SaveAsPdfCommand>(); //CommandService = new AppCommandService() - публичное свойство вью модели
3) Xaml to Xaml :
<Menu ItemsSource="{Binding CommandService.Menu}"/>
Implementation:
Attribute:
[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<MenuAttribute>(); if (attributes == null || attributes.Count() == 0) return null; MenuAttribute result = attributes.FirstOrDefault(); //здесь можно вставить условие по выбору атрибута, если их много return result; } } public enum MenuBreak : byte { None, Before, After, Both }
Service:
public class AppCommandService { private IWindsorContainer _InternalContainer = new WindsorContainer(new XmlInterpreter(new StaticContentResource(@"<configuration></configuration>"))); public void RegisterCommand<T>() where T: ICommand { _InternalContainer.Register(Component.For<ICommand, T>().LifeStyle.Singleton); } public T ResolveCommand<T>() where T : ICommand { return _InternalContainer.Resolve<T>(); } public void Execute<T>(object parameter = null) where T : ICommand { var command = _InternalContainer.Resolve<T>(); if (command != null && command.CanExecute(parameter)) { command.Execute(parameter); } } public void ExecuteOnAppDispatcher<T>(object parameter = null) where T : ICommand { Application.Current.Dispatcher.BeginInvoke(new Action<object>(Execute<T>), parameter); } #region MENU //MENU... private List<string> _DefaultMenuPaths = new List<string>(); public void RegisterMenuPath(string path) { _DefaultMenuPaths.Add(path); } //сборка дерева меню из аттрибутов private IEnumerable<MenuItem> GetMenuItems() { var handlers = _InternalContainer.Kernel.GetHandlers(typeof(ICommand)); var items = ( from x in handlers let attr = MenuAttribute.Extract(x.ComponentModel.Implementation) where attr != null orderby attr.Order let command = _InternalContainer.Resolve(x.ComponentModel.Implementation) as ICommand select new { attr, command} ).ToArray(); var generatedItems = new Dictionary<string, MenuItem>(); var menuRoots = new List<MenuItem>(); foreach (var item in _DefaultMenuPaths) { var path = item.Split('\\'); String currentPath = String.Empty; MenuItem parent = null; MenuItem lastItem = null; foreach (var menuPath in path) { currentPath += menuPath + "\\"; parent = lastItem; if (!generatedItems.TryGetValue(currentPath, out lastItem)) { lastItem = new MenuItem() { Header = menuPath }; generatedItems.Add(currentPath, lastItem); if (parent != null) { parent.Items.Add(lastItem); } else { menuRoots.Add(lastItem); } } } } foreach (var item in items) { var path = PreparePath(item.attr.Path).Split('\\'); MenuItem lastItem = null; MenuItem parent = null; String currentPath = String.Empty; foreach (var menuPath in path) { parent = lastItem; currentPath += menuPath + "\\"; if (!generatedItems.TryGetValue(currentPath, out lastItem)) { lastItem = new MenuItem() { Header = menuPath }; generatedItems.Add(currentPath, lastItem); if (parent != null) { parent.Items.Add(lastItem); } else { menuRoots.Add(lastItem); } } } var menu = new MenuItem { Header = item.attr.Name, Command = item.command }; if (lastItem != null) { if (item.attr.Break == MenuBreak.Before || item.attr.Break == MenuBreak.Both) { lastItem.Items.Add(new Separator()); } lastItem.Items.Add(menu); if (item.attr.Break == MenuBreak.After || item.attr.Break == MenuBreak.Both) { lastItem.Items.Add(new Separator()); } } else { menuRoots.Add(menu); } } return menuRoots; } protected ObservableCollection<MenuItem> _MenuItems = new ObservableCollection<MenuItem>(); public ObservableCollection<MenuItem> MenuItems { get { return _MenuItems; } } public void GenerateMenuItems() { MenuItems.Clear(); foreach (var item in GetMenuItems()) { MenuItems.Add(item); } } #endregion //добвление child - контейнера, чтобы резолвить команды с инъекциями public AppCommandService(IWindsorContainer container) { container.AddChildContainer(_InternalContainer); } //пустой конструктор, когда не надо вставлять зависимости public AppCommandService() { } private static string PreparePath(string path) { if (String.IsNullOrWhiteSpace(path)) return String.Empty; if (path[path.Length - 1] == '\\') { return path.Substring(0, path.Length - 1); } else { return path; } } }