Ищите Prism пример загрузки модулей в меню - PullRequest
12 голосов
/ 20 июля 2009

Кто-нибудь знает примеры кода WPF, использующие Prism, в которых каждый модуль регистрируется как элемент меню в меню другого модуля?

(В настоящее время у меня есть приложение, которое пытается сделать это с помощью EventAggregator, поэтому один модуль прослушивает опубликованные события от других модулей, которым необходимо иметь заголовок в меню в качестве элемента меню, но у меня возникают проблемы с порядком загрузки, многопоточности и т. д. Я хочу найти пример, в котором для этого используется классическая структура Prism.)

Я думаю об этом:

Shell.xaml:

<DockPanel>
    <TextBlock Text="Menu:" DockPanel.Dock="Top"/>
    <Menu 
        Name="MenuRegion" 
        cal:RegionManager.RegionName="MenuRegion" 
        DockPanel.Dock="Top"/>
</DockPanel>

Просмотр контрактов:

<UserControl x:Class="ContractModule.Views.AllContracts"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <MenuItem Header="Contracts">
    </MenuItem>
</UserControl>

Просмотр клиентов:

<UserControl x:Class="CustomerModule.Views.CustomerView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <MenuItem Header="Customers">
    </MenuItem>
</UserControl>

Но стоит знать, что я создал структуру приложения не-Prism MVVM, и меню всегда были приятно привязаны к ObservableCollections в ViewModel, и вышеприведенное, кажется, нарушает этот красивый шаблон. Является ли вышеуказанный обычный способ сделать это в Prism?

Ответы [ 2 ]

14 голосов
/ 20 июля 2009

Обновление:

Я создал образец для вас. Это здесь: Образец

У него есть несколько вещей, о которых вы, вероятно, еще не думали, например, контракт, который позволит вашим модулям управлять вашей оболочкой (так что вы можете делать такие вещи, как Open Window, и тому подобное). Он разработан с учетом MVVM ... Я не знаю, используете ли вы это, но я бы рассмотрел это.

Я пытался в течение нескольких минут получить правильные заголовки вкладок, но в итоге ушел с «A Tab». Это оставлено для вас, если вы используете интерфейс с вкладками. Я разработал его так, чтобы он не выглядел, поэтому вы можете заменить XAML в Shell.xaml, ничего не нарушая. Это одно из преимуществ материала RegionManager, если вы правильно его используете.

В любом случае, удачи!


Я никогда не видел такого примера, но вам бы пришлось реализовать это самостоятельно.

Вы должны создать свой собственный интерфейс, что-то вроде этого:

public interface IMenuRegistry
{
     void RegisterViewWithMenu(string MenuItemTitle, System.Type viewType);
}

Ваши модули затем объявят зависимость от IMenuRegistry и зарегистрируют свои взгляды.

В своей реализации IMenuRegistry (которую вы, скорее всего, реализуете и зарегистрируете в том же проекте, в котором размещен ваш Bootstrapper), вы добавляете эти пункты меню в свое меню или в древовидную структуру или все, что вы используете для своего меню.

Когда пользователь нажимает на элемент, вам нужно использовать ваш метод Bootstrapper.Container.Resolve(viewType), чтобы создать экземпляр представления и поместить его в любой заполнитель, в котором вы хотите его отобразить.

0 голосов
/ 01 сентября 2016

Я использую MEF вместе с призмой 6.0 и MVVM

1.Создайте класс Menuviewmodel для Leafmenu и TopLevel MenuViewmodel класс для меню Toplevel. Класс menuviewmodel будет иметь все свойства, с которыми вы хотите связать свое меню. Moduleui, реализующий этот интерфейс, должен иметь такой атрибут

[Экспорт (TypeOf (IMenu))]

  public class MenuViewModel:ViewModelBase
        {
            public String Name { get; private set; }
            public UIMenuOptions ParentMenu { get; private set; }
            private bool _IsToolTipEnabled;
            public bool IsToolTipEnabled
            {
                get
                {
                    return _IsToolTipEnabled;
                }
                set
                {
                    SetField(ref _IsToolTipEnabled, value);
                }
            }

            private String _ToolTipMessage;

            public String ToolTipMessage
            {
                get
                {
                    return _ToolTipMessage;
                }
                set
                {
                    SetField(ref _ToolTipMessage, value);
                }
            }
            private IExtensionView extensionView;
            public MenuViewModel(String name, UIMenuOptions parentmenu,
             bool isMenuCheckable = false, 
             IExtensionView extensionView    =null)
            {
                if(name.Contains('_'))
                {
                  name= name.Replace('_', ' ');
                }
                name = "_" + name;
                this.Name = name;
                this.ParentMenu = parentmenu;
                this.IsMenuCheckable = isMenuCheckable;
                this.extensionView = extensionView ;
            }

            private RelayCommand<object> _OpenMenuCommand;

            public ObservableCollection<MenuViewModel> MenuItems { get; set; }
            public ICommand OpenMenuCommand
            {
                get
                {
                    if(_OpenMenuCommand==null)
                    {
                        _OpenMenuCommand = new RelayCommand<object>((args =>  
                          OpenMenu(null)));
                    }
                    return _OpenMenuCommand;
                }
            }

            private void OpenMenu(object p)
            {

                if (extensionView != null)
                {
                    extensionView .Show();
                }
            }

            private bool _IsMenuEnabled=true;
            public bool IsMenuEnabled
            {
                get
                {

                    return _IsMenuEnabled;
                }
                set
                {
                    SetField(ref _IsMenuEnabled, value);
                }
            }

            public bool IsMenuCheckable
            {
                get;
                private set;
            }
            private bool _IsMenuChecked;
            public bool IsMenuChecked
            {
                get
                {
                    return _IsMenuChecked;
                }
                set
                {
                    SetField(ref _IsMenuChecked, value);
                }
             }
        }

         public class ToplevelMenuViewModel:ViewModelBase
         {
            public ObservableCollection<MenuViewModel> ChildMenuViewModels { 
              get; private set; } 
            public  String Header { get; private set; }
            public  ToplevelMenuViewModel(String header,         
             IEnumerable<MenuViewModel> childs)
            {

                this.Header ="_"+ header;
                this.ChildMenuViewModels =new 
                ObservableCollection<MenuViewModel>(childs);
            }
        }
    }
  1. Создание интерфейса IMenu, который имеет свойство MenuViewModel
    public interface IMenu
     {
         MenuViewModel ExtensionMenuViewModel
        {
            get;

        }

     }

3.Вы должны внедрить интерфейс IMenu в ModuleUi всех ваших модулей, которые будут загружены в меню.

4. Реализация MefBootstrapper
5. Переопределить Настроить метод совокупного каталога 6. В каталог добавьте каталог директорий, содержащий все ваши dll модуля, интерфейс dll IMenu. Код ниже

protected override void ConfigureAggregateCatalog()
{
    base.ConfigureAggregateCatalog();
    AggregateCatalog.Catalogs.Add(new  
     AssemblyCatalog(typeof(Bootstrapper).Assembly));
       AggregateCatalog.Catalogs.Add(new 
      AssemblyCatalog(typeof(IMenu).Assembly));
     //create a directorycatalog with path of a directory conatining  
      //your module dlls                
    DirectoryCatalog dc = new DirectoryCatalog(@".\Extensions");
    AggregateCatalog.Catalogs.Add(dc);
}
  1. в вашем основном проекте добавить ссылку на IMenu interfce dll

8. В классе mainwindow.xaml.cs объявите свойство

public ObservableCollection ClientMenuViewModels { получить; приватный набор; }

объявить приватное поле

private IEnumerable<IMenu> menuExtensions;

  1. В вашем главном окне или конструкторе оболочки

    [ImportingConstructor]
       public MainWindow([ImportMany] IEnumerable<IMenu> menuExtensions)
        {
           this.menuExtensions = menuExtensions;
           this.DataContext=this;
    
        }
       private void InitalizeMenuAndOwners()
      {
       if (ClientMenuViewModels == null)
      {
        ClientMenuViewModels = new                                  
        ObservableCollection<ToplevelMenuViewModel>();
       }
       else
      {
        ClientMenuViewModels.Clear();
       }
      if (menuExtensions != null)
       {
         var groupings = menuExtensions.Select
          (mnuext =>   mnuext.ClientMenuViewModel).GroupBy(mvvm =>                                                                   
           mvvm.ParentMenu);
    
         foreach (IGrouping<UIMenuOptions, MenuViewModel> grouping in      
          groupings)         
          {
            UIMenuOptions parentMenuName = grouping.Key;
            ToplevelMenuViewModel parentMenuVM = new 
             ToplevelMenuViewModel(                                   
         parentMenuName.ToString(),
    
         grouping.Select(grp => { return (MenuViewModel)grp; }));
            ClientMenuViewModels.Add(parentMenuVM);
          }
    }}
    

}

  1. В вашем Shell.xaml или Mainwindow.xaml определите область меню и привяжите свойство itemssource к ClientMenuViewModels
       <Menu HorizontalAlignment="Left"
              Background="#FF0096D6"
              Foreground="{StaticResource menuItemForegroundBrush}"
              ItemsSource="{Binding ClientMenuViewModels}"
              TabIndex="3">
            <Menu.Resources>
                <Style x:Key="subMneuStyle" TargetType="{x:Type MenuItem}">
                    <Setter Property="Foreground" Value="#FF0096D6" />
                    <Setter Property="FontFamily" Value="HP Simplified" />
                    <Setter Property="FontSize" Value="12" />
                    <Setter Property="Background" Value="White" />
                    <Setter Property="Command" Value="{Binding   
                     OpenMenuCommand}" />                 
                    <Setter Property="IsCheckable" Value="{Binding

                     IsMenuCheckable}" />
                    <Setter Property="IsChecked" Value="{Binding 
                         IsMenuChecked, Mode=TwoWay}" />
                    <Setter Property="IsEnabled" Value="{Binding 
                   IsMenuEnabled, Mode=TwoWay}" />
                    <Setter Property="ToolTip" Value="{Binding  
                ToolTipMessage, Mode=OneWay}" />
                    <Setter Property="ToolTipService.ShowOnDisabled" Value=" 
            {Binding IsToolTipEnabled, Mode=OneWay}" />
                    <Setter Property="ToolTipService.IsEnabled" Value="
            {Binding IsToolTipEnabled, Mode=OneWay}" />
                    <Setter Property="ToolTipService.ShowDuration" 
         Value="3000" />
                    <Setter Property="ToolTipService.InitialShowDelay" 
                Value="10" />
                </Style>

                <my:MyStyleSelector x:Key="styleSelector" ChildMenuStyle="   
                     {StaticResource subMneuStyle}" />
                <HierarchicalDataTemplate DataType="{x:Type 
                  plugins:ToplevelMenuViewModel}"
                 ItemContainerStyleSelector="{StaticResource styleSelector}"
                  ItemsSource="{Binding ChildMenuViewModels}">
                    <Label Margin="0,-5,0,0"
                           Content="{Binding Header}"
                           FontFamily="HP Simplified"
                           FontSize="12"
                    Foreground="{StaticResource menuItemForegroundBrush}" />
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type plugins:MenuViewModel}">
                    <Label VerticalContentAlignment="Center"
                           Content="{Binding Name}"
                           Foreground="#FF0096D6" />
                </DataTemplate>
            </Menu.Resources>
            <Menu.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel Orientation="Horizontal" />
                </ItemsPanelTemplate>
            </Menu.ItemsPanel>
              </Menu>









 public class MyStyleSelector : StyleSelector
      {
        public Style ChildMenuStyle { get; set; }
        public Style TopLevelMenuItemStyle { get; set; }
        public override Style SelectStyle(object item, DependencyObject             
         container)                    
        {
            if (item is MenuViewModel)
            {
                return ChildMenuStyle;
            }
            //if(item is ToplevelMenuViewModel)
            //{
            //    return TopLevelMenuItemStyle;
            //}
            return null;

        }
    }

вот класс ViewModelBase

public class ViewModelBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler =Volatile.Read(ref PropertyChanged);
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        };
    }
    protected bool SetField<T>(ref T field, T value,[CallerMemberName] string propertyName="")
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

}

Класс RelayCommand ниже

 public class RelayCommand<T> : ICommand
    {
        #region Fields

        private readonly Action<T> _execute = null;
        private readonly Predicate<T> _canExecute = null;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new command that can always execute.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        public RelayCommand(Action<T> execute)
            : this(execute, null)
        {
        }


        /// <summary>
        /// Creates a new command with conditional execution.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action<T> execute, Predicate<T> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }

        #endregion

        #region ICommand Members

        /// <summary>
        /// Defines the method that determines whether the command can execute in its current state.
        /// </summary>
        /// <param name="parameter">Data used by the command.  If the command does not require data to be passed, this object can be set to null.</param>
        /// <returns>
        /// true if this command can be executed; otherwise, false.
        /// </returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute((T)parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        public void Execute(object parameter)
        {
            _execute((T)parameter);
        }

        #endregion
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...