Looking for Prism example of Modules loading themselves into a menu

前端 未结 2 1974
忘掉有多难
忘掉有多难 2020-12-08 05:22

Does anyone know of WPF code examples using Prism in which modules each register themselves as a menuitem in a menu within another module?

(I\'ve cu

相关标签:
2条回答
  • 2020-12-08 06:18

    I am using MEF along with prism 6.0 and MVVM

    1.Create a Menuviewmodel class for Leafmenu and TopLevel MenuViewmodel class for Toplevel menu. Menuviewmodel class will have all the properties you want to bind your menu with. Moduleui implementing this interafce must have an attribute like this

    [Export(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. Create an IMenu Interface wich has MenuViewModel property
        public interface IMenu
         {
             MenuViewModel ExtensionMenuViewModel
            {
                get;
    
            }
    
         }
    

    3.You need to implement IMenu Interface in ModuleUi of all your modules which will get loaded into a menu.

    4.Implement MefBootstrapper
    5.Override Configure aggregate catalog method 6.To the catalog add diretory catalog containing all your module dlls, IMenu interface dll.Code is below

    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. in your main project add refence to IMenu interafce dll

    8.In mainwindow.xaml.cs class declare a property

    public ObservableCollection ClientMenuViewModels { get; private set; }

    declare a private field

    private IEnumerable<IMenu> menuExtensions;

    1. In your mainwindow or shell constructor

      [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. In your Shell.xaml or Mainwindow.xaml define a menu region and bind the itemssource property to 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;
    
            }
        }
    

    here is ViewModelBase class

    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 class is below

     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
        }
    
    0 讨论(0)
  • 2020-12-08 06:24

    Update:

    I created a sample for you. It's here: Sample

    It's got a few things you've probably not thought of yet, like a contract that will allow your modules to control your shell (so you can do stuff like Open Window, that kind of thing). It's designed with MVVM in mind... I dunno if you are using that, but I would consider it.

    I tried for a few minutes to get the tab titles correct, but I ended up leaving off with "A Tab". It's left as an exercise for you if you go with a tabbed UI. I've designed it to be lookless, so you can replace the XAML in the Shell.xaml without breaking anything. That's one of the advantages to the RegionManager stuff if you use it right.

    Anyway, good luck!


    I've never seen an example of this, but you'd have to implement this yourself.

    You'd have to create your own interface, something like this:

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

    Your Modules then would declare a dependency on an IMenuRegistry and register their views.

    Your implementation of IMenuRegistry (which you would likely implement and register in the same project that hosts your Bootstrapper) you would add those menu items to your menu or treeview or whatever you are using for your menu.

    When a user clicks on an item you'll have to use your Bootstrapper.Container.Resolve(viewType) method to create an instance of the view and stuff it in whatever placeholder you want to show it in.

    0 讨论(0)
提交回复
热议问题