【问题标题】:Looking for Prism example of Modules loading themselves into a menu寻找将自身加载到菜单中的模块的 Prism 示例
【发布时间】:2009-07-20 08:19:21
【问题描述】:

有谁知道使用 Prism 的 WPF 代码示例,其中每个模块都将自己注册为另一个模块内的菜单中的菜单项?

(我目前有一个应用程序尝试使用 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 应用程序结构,并且菜单总是很好地绑定到 ViewModel 中的 ObservableCollections,上面似乎打破了这个很好的模式。以上是 Prism 中的惯用方式吗?

【问题讨论】:

    标签: wpf prism containers bootstrapper


    【解决方案1】:

    更新:

    我为您创建了一个示例。在这里:Sample (现在是死链接)

    它有一些你可能还没有想到的东西,比如允许你的模块控制你的 shell 的合约(这样你就可以做像打开窗口之类的事情)。它的设计考虑到了 MVVM。我不知道你是否正在使用它,但我会考虑它。

    我尝试了几分钟来让标签标题正确,但最终还是选择了“A Tab”。如果您使用选项卡式 UI,则将其作为练习留给您。我将其设计为无外观,因此您可以在不破坏任何内容的情况下替换 Shell.xaml 中的 XAML。如果使用得当,这就是 RegionManager 的优势之一。

    不管怎样,祝你好运!


    我从未见过这样的例子,但你必须自己实现。

    您必须创建自己的界面,如下所示:

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

    然后,您的模块将声明对 IMenuRegistry 的依赖并注册它们的视图。

    您对 IMenuRegistry 的实现(您可能会在托管 Bootstrapper 的同一个项目中实现和注册它)您会将这些菜单项添加到您的菜单或树形视图或您用于菜单的任何内容中。

    当用户点击某个项目时,您必须使用 Bootstrapper.Container.Resolve(viewType) 方法创建视图实例并将其填充到您想要显示的任何占位符中。

    【讨论】:

    • 您的 CAGMenus 示例看起来很有趣,我会仔细研究一下。我通过制作一个 LayoutManager 解决了这个问题,每个模块在其中注册自己,然后在 LayoutManager 的 Intialize() 方法中,它基于所有注册的部分构建应用程序。
    • 您的链接 404 的。
    • @anderson - 链接坏了,你能分享一下代码吗?
    【解决方案2】:

    我将 MEF 与 prism 6.0 和 MVVM 一起使用

    1.为Leafmenu创建一个Menuviewmodel类,为Toplevel菜单创建一个TopLevel MenuViewmodel类。 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. 创建一个具有 MenuViewModel 属性的 IMenu 接口
        public interface IMenu
         {
             MenuViewModel ExtensionMenuViewModel
            {
                get;
    
            }
    
         }
    

    3.您需要在ModuleUi中实现所有模块的IMenu接口,这些模块将被加载到菜单中。

    4.实现 MefBootstrapper
    5.Override配置聚合目录方法 6.向目录添加包含所有模块dll的目录目录,IMenu接口dll。代码如下

    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 interafce dll 的引用

    8.在mainwindow.xaml.cs类中声明一个属性

    public ObservableCollection ClientMenuViewModels { 得到;私人套装; }

    声明一个私有字段

    private IEnumerable&lt;IMenu&gt; menuExtensions;

    1. 在您的主窗口或 shell 构造函数中

      [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
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 2013-10-06
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多