Prism for WPF 搭建一個簡單的模塊化開發框架(一)

原文: Prism for WPF 搭建一個簡單的模塊化開發框架(一)

最近閒來無事又想搞搞WPF。。。。。
作個框架吧,可能又是半途而廢。。。。老是堅持不下來css

不廢話了,html

先看一下工程結構shell

佈局大概是這樣的express

SystemHeader
Aside Main
Footer

 

PW.SystemHeader是用來放導航條,Log或者系統名稱的bootstrap

PW.Aside是準備放每一個模塊的樹形菜單的,能夠根據後續系統的須要作相應的變更和擴展app

PW.Login就是一個登錄的模塊了框架

PW.Footer只是一個底部的佔位欄,須要的話能夠放置一些顯示信息ide

Main初始化時存放了Login的region 此時SystemHeader、Aside、Footer還未加載,因此主界面就算一個登錄頁面模塊化

在登錄以後LoadModule,對應的區域也就加載上對用的模塊了
主窗體佈局代碼是這樣的佈局

<Window x:Class="PW.Desktop.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
    xmlns:local="clr-namespace:PW.Desktop"	
        xmlns:prism="http://www.codeplex.com/prism"
	x:Name="Window"
	Title="Desktop"
    WindowStartupLocation="CenterScreen"
	UseLayoutRounding="True"
	Width="1024" Height="768" WindowState="Maximized" Loaded="Window_Loaded" WindowStyle="None" ResizeMode="NoResize">
    <Window.Background>
        <ImageBrush ImageSource="Images/bg/ocean.jpg"/>
    </Window.Background>
    <Grid x:Name="window">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid Height="30" Background="{DynamicResource ModuleControl.Panel.BackgroundBrush}" MouseLeftButtonDown="Grid_MouseLeftButtonDown" Cursor="Hand">
            <StackPanel  VerticalAlignment="Center" Orientation="Horizontal" FlowDirection="RightToLeft">
                <Button Width="25" Height="25" Content="X"  x:Name="closeBtn" Click="closeBtn_Click"></Button>
                <Button Width="25" Height="25" Content="口" x:Name="maxBtn" Click="maxBtn_Click"></Button>         
                <Button Width="25" Height="25" Content="-" x:Name="minBtn" Click="minBtn_Click"></Button>
            </StackPanel>
        </Grid>
        <Grid x:Name="LayoutRoot" Grid.Row="1">
               
    	<Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
    	</Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Row="0" Grid.Column="0" x:Name="gridHeader" Grid.ColumnSpan="2" Margin="0">
            <ContentControl x:Name="headerContentControl" prism:RegionManager.RegionName="HeaderRegion" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
        </Grid>
        <Grid Grid.Row="1" Grid.Column="0" x:Name="gridAside" Margin="0">
            <ContentControl x:Name="asideContentControl" prism:RegionManager.RegionName="AsideRegion" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
        </Grid>
        <Grid  Grid.Row="1" Grid.Column="1" x:Name="gridMain" Margin="0">
            <ContentControl x:Name="mainContentControl" prism:RegionManager.RegionName="MainRegion" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
        </Grid>
        <Grid  Grid.Row="3" Grid.Column="0" x:Name="gridFooter"  Grid.ColumnSpan="2" Margin="0">
                <ContentControl x:Name="footerContentControl" prism:RegionManager.RegionName="FooterRegion" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
        </Grid>
    </Grid>
    </Grid>
</Window>

那麼究竟是怎麼實現模塊化鬆耦合呢,先貼一下代碼

Bootstrapper.cs

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

namespace PW.Desktop
{
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.Windows;
    using Prism.Logging;
    using Prism.Modularity;
    using Prism.Mef;

    /// <summary>
    /// Initializes Prism to start this quickstart Prism application to use Managed Extensibility Framework (MEF).
    /// </summary>
    public class Bootstrapper : MefBootstrapper
    {
        private readonly CallbackLogger callbackLogger = new CallbackLogger();

        /// <summary>
        /// Creates the shell or main window of the application.
        /// </summary>
        /// <returns>The shell of the application.</returns>
        /// <remarks>
        /// If the returned instance is a <see cref="DependencyObject"/>, the
        /// <see cref="MefBootstrapper"/> will attach the default <seealso cref="Microsoft.Practices.Composite.Regions.IRegionManager"/> of
        /// the application in its <see cref="Microsoft.Practices.Composite.Presentation.Regions.RegionManager.RegionManagerProperty"/> attached property
        /// in order to be able to add regions by using the <seealso cref=""Microsoft.Practices.Composite.Presentation.Regions.RegionManager.RegionNameProperty"/>
        /// attached property from XAML.
        /// </remarks>
        protected override DependencyObject CreateShell()
        {
            return this.Container.GetExportedValue<Shell>();
        }

        /// <summary>
        /// Initializes the shell.
        /// </summary>
        /// <remarks>
        /// The base implemention ensures the shell is composed in the container.
        /// </remarks>
        protected override void InitializeShell()
        {
            base.InitializeShell();

            Application.Current.MainWindow = (Shell) this.Shell;
            Application.Current.MainWindow.Show();
        }

        /// <summary>
        /// Configures the <see cref="AggregateCatalog"/> used by MEF.
        /// </summary>
        /// <remarks>
        /// The base implementation does nothing.
        /// </remarks>
        protected override void ConfigureAggregateCatalog()
        {
            base.ConfigureAggregateCatalog();

            // Add this assembly to export ModuleTracker
            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

            // Module A is referenced in in the project and directly in code.
            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(PW.LogIn.LoginModule).Assembly));

            // Module B and Module D are copied to a directory as part of a post-build step.
            // These modules are not referenced in the project and are discovered by inspecting a directory.
            // Both projects have a post-build step to copy themselves into that directory.
            DirectoryCatalog catalog = new DirectoryCatalog("DirectoryModules");
            this.AggregateCatalog.Catalogs.Add(catalog);
        }

        /// <summary>
        /// Configures the <see cref="CompositionContainer"/>.
        /// May be overwritten in a derived class to add specific type mappings required by the application.
        /// </summary>
        /// <remarks>
        /// The base implementation registers all the types direct instantiated by the bootstrapper with the container.
        /// The base implementation also sets the ServiceLocator provider singleton.
        /// </remarks>
        protected override void ConfigureContainer()
        {
            base.ConfigureContainer();

            // Because we created the CallbackLogger and it needs to be used immediately, we compose it to satisfy any imports it has.
            this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);
        }

        /// <summary>
        /// Creates the <see cref="IModuleCatalog"/> used by Prism.
        /// </summary>
        /// <remarks>
        /// The base implementation returns a new ModuleCatalog.
        /// </remarks>
        /// <returns>
        /// A ConfigurationModuleCatalog.
        /// </returns>
        protected override IModuleCatalog CreateModuleCatalog()
        {
            // When using MEF, the existing Prism ModuleCatalog is still the place to configure modules via configuration files.
            return new ConfigurationModuleCatalog();
        }

        /// <summary>
        /// Create the <see cref="ILoggerFacade"/> used by the bootstrapper.
        /// </summary>
        /// <remarks>
        /// The base implementation returns a new TextLogger.
        /// </remarks>
        /// <returns>
        /// A CallbackLogger.
        /// </returns>
        protected override ILoggerFacade CreateLogger()
        {
            // Because the Shell is displayed after most of the interesting boostrapper work has been performed,
            // this quickstart uses a special logger class to hold on to early log entries and display them 
            // after the UI is visible.
            return this.callbackLogger;
        }
    }
}

這裏能夠看到,基本上就算copy了Prism的demo代碼,只是略微改動了一下ConfigureAggregateCatalog方法

手動加載了LogIn模塊,這裏工程裏須要引入Login模塊,而後剩餘的全部佈局模塊以及系統模塊都是在DirectoryModules下面
程序生成目錄下面是這樣的

這樣的好處就算,各模塊互不相干,之後能夠像插件同樣作一個新的模塊放到DirectoryModules就能運行

而後每一個模塊下面只要作一個module的接口實現類相似AsideModule.cs這樣

using Prism.Modularity;
using Prism.Mef.Modularity;
using Prism.Regions;
using System.ComponentModel.Composition;
using System;
using PW.Infrastructure;

namespace PW.Aside
{
    [ModuleExport(typeof(AsideModule), InitializationMode = InitializationMode.OnDemand)]
    public class AsideModule : IModule
    {
        private readonly IModuleTracker moduleTracker;
        private readonly IRegionManager regionManager;

        /// <summary>
        /// Initializes a new instance of the <see cref="ModuleB"/> class.
        /// </summary>
        /// <param name="moduleTracker">The module tracker.</param>
        [ImportingConstructor]
        public AsideModule(IModuleTracker moduleTracker, IRegionManager regionManager)
        {
            if (moduleTracker == null)
            {
                throw new ArgumentNullException("moduleTracker");
            }

            this.moduleTracker = moduleTracker;
            this.moduleTracker.RecordModuleConstructed(ModuleNames.Aside);
            this.regionManager = regionManager;
        }


        /// <summary>
        /// Notifies the module that it has be initialized.
        /// </summary>
        public void Initialize()
        {
            this.moduleTracker.RecordModuleInitialized(ModuleNames.Aside);
            regionManager.RegisterViewWithRegion(RegionNames.Aside, typeof(MenuView));
        }
    }
}

每一個模塊的基本上都同樣,只是在Initialize中去爲當前Region註冊不一樣的view
好像也沒什麼好寫的了,差很少就這樣吧

說一下我遇到的坑和怎麼解決的吧

就說佈局中的MainRegion,由於初始化拿它加載了login模塊,可是在登錄成功後想去加載Map或者SystemSet時,原本想的是用RegionManager更改mainContentControl的Region名稱,可是死活不行,各類報錯,有一種方法是能作到去加載不一樣的Region的,就是用ItemsControl,可是用ItemsControl效果很很差,不是全屏顯示在MainRegion裏

而後查資料,問csdn,都沒有結構,多是如今用wpf的原本就少吧,

最後沒事兒時偶然就想到了,在不一樣的模塊能有本身獨立的Region作導航,MainRegion只是導航到各模塊的主頁面,就這樣解決了

下面是作到如今的截圖

這個原本是打算作一個登錄後的主導航的,上邊和左邊是要去掉的,還沒處理,這時候佈局其實已經出來了

再來幾個換個風格的,其實就是換個背景圖

寫的比較亂,想用到項目裏,就不上源碼了,框架樣式都還須要調整,對wpf不熟悉,也是寫到哪查到哪,各類不會

如今準備給各模塊作公用樣式呢,又不知道怎麼弄好了,,,,我多想有一個wpf的前輩給指教指教啊。。。。看到的朋友知道怎麼弄能夠給說一下啊,有好的方法,好的建議都能寫下來啊,,,,感受本身實在是有點瞎弄啊

相關文章
相關標籤/搜索