Region

Region可以帮助我们开发模块化程序,将程序分割成一个个独立的Module,分别进行开发。然后在程序运行的时候,将各个Module组合到一起,为程序提供各种各样的功能。通常来说,Module是一些视图和功能的集合,那么就需要一种办法来将这些视图以某种形式,在特定的时间展示出来。Prism通过Shell+Region来组织视图的布局,完成视图间的转换等。

如上图所示,Shell相当于ASP.NET中的母版页,它定义了页面的布局、主题等。其中的导航区和内容区是预留出来的需要进行填充内容的部分,也就是Region,起到占位符的作用,程序会在运行时动态地向Region中填充内容。

那么如何将一个区域定义为Region呢?

1. 首先要引入Prism命名空间

xmlns:prism="http://www.codeplex.com/prism" 如果IDE无法找到这个命名空间的话,需要先注册Prism。

2. 然后再需要定义为Region的控件上加上Attached Property。

<ContentCotrol prism:RegionManager.RegionName = "MainRegion"/>

并不是所哟的控件都可以作为Region的,需要为需要定义为Region的控件添加RegionAdapter。RegionAdapter的作用是为特定的控件创建相应的Region,并将控件与Region进行绑定,然后为Region添加一些行为。一个RegionAdapter需要实现IRegionAdapte接口,如果你需要自定义一个RegionAdapter,可以通过继承RegionAdapterBase类来省去一些工作。Prism为Silverlight提供了几个RegionAdapter:

ContentControlRegionAdapter:创建一个SingleActiveRegion并将其与ContentControl绑定。

ItemsControlRegionAdapter: 创建一个AllActiveRegion并将其与ItemsControl绑定。

SelectorRegionAdapter: 创建一个Region并将其与Selector绑定。

TabControlRegionAdapter: 创建一个Region并将其与TabControl绑定。

从图中可以看到,导航区对应的NavigationRegion中四个视图都是亮着的,而内容区对应的ContentRegion中四个视图只有一个是亮着的(橙色代表显示在页面中)。ItemsControl本来就是由许多个Items组成的,因此ItemsControlRegionAdapter会创建AllActiveRegion,这种类型的Region中所有Active的视图都会显示在ItemsControl中;而ContentControl只能容纳一个Content,所以ContentControlRegionAdapter创建了一个SingleActiveRegion,其中的视图只有一个处于Active状态的,会显示在ContentControl中,其他的都是不可见的,需要将他们激活(Active),才能使其显示。

通常我们并不直接和Region打交道,而是通过RegionManager,它实现了IRegionManager接口。IRegionManager接口包含一个只读属性Regions,是Region的集合,还有一个CreatRegionManager方法。Prism通过RegionManagerExtensions类使用扩展方法为IRegionManager添加更多的功能。

AddToRegion:将一个视图添加到一个Region中。

RegisterViewWithRegion:将一个视图和一个Region进行关联。当Region显示的时候,关联的视图才会显示,也就是说,在这个Region显示之前,关联的视图是不会被创建的。

RequestNavigate:进行页面切换,将指定的Region中显示的视图切换为指定的视图。

本文开头说过,需要在运行时将分散的各个Module的视图显示在页面特定的位置上。那么首先就需要定义页面显示的地方,即Region。然后就是要定义创建视图的时机和方式。在Prism中有两种方式来定义视图与Region之间的映射关系——View Discovery和View Injection。

View Discovery是以声明式的方式来建立Region和视图之间的关系。如上图中的导航区,需要在导航区显示的时候就将各个导航视图填充到其中。而内容区也需要一个默认显示的内容视图。因此也可以这样理解View Discovery,就是指定一个Region的默认视图。我们可以使用IRegionManager.RegisterViewWithRegion方法来声明某个Region默认应该显示哪个视图。注意这里的是Register,是注册,也就是说不会马上创建该视图。当Region显示在页面中的时候,它回去寻找与自己关联的视图,并对其进行初始化。

这样做的好处是我们不必关心在什么时候创建视图,一切都会自动完成。缺点就是默认视图是确定的,当需要进行视图转换的时候,这种方式就行不通了。这时候就需要View Injection。

View Injection可以让我们对于Region中显示的视图有更精确的控制。通常可以通过调试IRegionManager.AddToRegion方法或者死IRegionManager.Regions["RegionName"].Add方法来向一个Region中添加一个视图的实例。对于SingleActiveRegion(ContentControlRegionAdapter会创建这种类型的Region),可以通过IRegion.Activate方法将一个已经添加到Region中的视图显示出来。当然也可以通过IRegion.Deactivate方法来将视图状态置为非激活或者干脆调用IRegion.Remove方法将视图移除。可以看到,因为要添加的是视图的实例,所以需要仔细地设计在什么时候使用View Injection,以免造成不必要的开销。

在Prism 4.0 中新添加了一些导航API,这套API大大地简化了View Injection的流程,它使用URI来进行Region中视图的导航,然后会根据URI来创建视图,并将其添加到Region中,然后激活该视图。导航API的出现不只是为了简化View Injection的过程,它还提供了前进、后退的功能,并且对MVVM模式下的导航有良好的支持,还能够在进行导航的时候传递参数等等。所以推荐的方式是使用新的导航API,也就是使用IRegionManager.RequestNavigate方法。

如果一个页面相对来说不大变化,如导航区,在程序初始化的过程完成后就不会轻易变动,这时候就较适合私用RegisterViewRegion方法,通常可以在Module的Initialize方法中完成这个过程。

 

 

下面通过一个Demo来学习Region:

1. 新建一个Silverlight Application——“PrismRegionDemo”,然后再创建3个Silverlight类库工程:

2. 首先在Infrastructure——基础结构项目中来实现一些需要使用的Prism的接口。首先在该项目中引用Microsoft.Practices.Prism程序集,然后新建一个类CallbackLogger.cs该类实现了Prism内置的ILoggerFacade接口,在Prism提供的QuickStart项目里的Modularity中,有一个CallBackLogger,这里我们直接拿过来使用。该类是为了在UI上显示LOG信息使用。然后再PrismRegionShell中放一个TextBox,将log的内容显示在这个TextBox中。

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.Logging;
using System.Collections.Generic;
 
namespace Infrastrcture
{
    //A logger that holds on to log entries until a callback delegate is set, then plays back log entries and sends new log entries.
 
    public class CallBackLogger:ILoggerFacade
    {
        private Queue<Tuple<string, Category, Priority>> saveLogs = new Queue<Tuple<string, Category, Priority>>();
        private Action<string, Category, Priority> callback;
 
 
        //Gets or Sets the callback to receive logs.
        public Action<string, Category, Priority> CallBack
        {
            get
            {
                return this.callback;
            }
            set
            {
                this.callback = value;
            }
        }
 
        /// <summary>
        /// Write a new log entry with the specified category and priority.
        /// </summary>
        /// <param name="message">Message body to log.</param>
        /// <param name="category">Category of the entry.</param>
        /// <param name="priority">The priority of the entry.</param>
        public void Log(string message, Category category, Priority priority)
        {
            if (this.CallBack != null)
            {
                this.CallBack(message, category, priority);
            }
            else
            {
                saveLogs.Enqueue(new Tuple<string, Category, Priority>(message, category, priority));
            }
        }
 
        //Replay the saved logs if the Callback has been set.
        public void ReplaySaveLogs()
        {
            if (this.CallBack != null)
            {
                while (this.saveLogs.Count > 0)
                {
                    var log = this.saveLogs.Dequeue();
                    this.CallBack(log.Item1, log.Item2, log.Item3);
                }
            }
        }
    }
}

 

 


3. 在Infrastructure里创建一个类RegionNames,用来存储RegionName的常量字符串。

 

 
  1. public class RegionNames

  2. {

  3. public const string NavRegion = "NavRegion";

  4. public const string MainRegion = "MainRegion";

  5. public const string NavDemoShowRegion = "NavDemoShowRegion";

  6. public const string NavDemoActionRegion = "NavDemoActionRegion";

  7. public const string TabShowRegion = "TabShowRegion";

  8. }


4. 在Infrastructure里新建一个类ViewNames,用来存储ViewNames常量字符串。

 
  1. public class ViewNames

  2. {

  3. public const string NavigationContainer = "NavigationContainer";

  4. public const string EmptyPage = "EmptyPage";

  5. public const string ViewA = "ViewA";

  6. public const string ViewB = "ViewB";

  7. public const string ViewC = "ViewC";

  8. }


5. 删除PrismRegionDemo项目中自动生成的MainPage.xaml,创建一个新的UserControl,叫做PrismRegionShell。添加对Prism程序集的引用(Microsoft.Practices.Prim;Microsoft.Pratices.Prism.UnityExtensions;Microsoft.Practices.Unity.Silverlight),并且还要添加对页面导航的引用(System.Windows.Controls.Navigatio),以及Infrastructure项目的引用,页面代码如下:

 

 
  1. <UserControl x:Class="PrismRegionDemo.PrismRegionShell"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. xmlns:prism="http://www.codeplex.com/prism"

  8. d:DesignHeight="600" d:DesignWidth="800">

  9.  
  10. <UserControl.Resources>

  11. <Style TargetType="Border" x:Key="BorderStyle">

  12. <Setter Property="Background" Value="RosyBrown"/>

  13. <Setter Property="Opacity" Value="0.8"/>

  14. <Setter Property="Margin" Value="15"/>

  15. <Setter Property="CornerRadius" Value="5"/>

  16. <Setter Property="Effect">

  17. <Setter.Value>

  18. <DropShadowEffect ShadowDepth="15" Color="Black" Direction="320" Opacity="0.5"/>

  19. </Setter.Value>

  20. </Setter>

  21. </Style>

  22. </UserControl.Resources>

  23.  
  24. <Grid ShowGridLines="True">

  25. <Grid.Background>

  26. <ImageBrush ImageSource="/PrismRegionDemo;component/Images/20070825000100735.jpg" Stretch="UniformToFill"/>

  27. </Grid.Background>

  28. <Grid.RowDefinitions>

  29. <RowDefinition MinHeight="400" Height="0.6*"/>

  30. <RowDefinition MinHeight="200" Height="0.4*"/>

  31. </Grid.RowDefinitions>

  32. <Grid.ColumnDefinitions>

  33. <ColumnDefinition MinWidth="280" Width="0.4*"/>

  34. <ColumnDefinition Width="0.6*"/>

  35. </Grid.ColumnDefinitions>

  36.  
  37. <Border Style="{StaticResource BorderStyle}" Width="250" HorizontalAlignment="Right">

  38. <ItemsControl prism:RegionManager.RegionName="NavRegion"/>

  39. </Border>

  40.  
  41. <Border Style="{StaticResource BorderStyle}" HorizontalAlignment="Left" Width="550" Grid.Column="1">

  42. <ContentControl VerticalAlignment="Center" HorizontalAlignment="Center" prism:RegionManager.RegionName="MainRegion" Background="White"/>

  43. </Border>

  44.  
  45. <Border Style="{StaticResource BorderStyle}" HorizontalAlignment="Left" Width="550" Grid.Row="1" Grid.Column="1">

  46. <ContentControl Padding="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">

  47. <TextBox x:Name="LogContainer" IsReadOnly="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" BorderThickness="0" Background="{x:Null}"/>

  48. </ContentControl>

  49. </Border>

  50. </Grid>

  51. </UserControl>


PrismRegionShell中放一个TextBox,将log的内容显示在这个TextBox中。值得一提的是,为了让输出新log的直接显示出来,需要将TextBox的滚动条滚动到最下面。PrismRegionShell.xaml.cs如下:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Infrastrcture;

  13. using Microsoft.Practices.Prism.Logging;

  14. using System.Globalization;

  15.  
  16. namespace PrismRegionDemo

  17. {

  18. public partial class PrismRegionShell : UserControl

  19. {

  20. public PrismRegionShell(CallBackLogger logger)

  21. {

  22. InitializeComponent();

  23. //显示当前Log信息

  24. logger.CallBack = Log;

  25. }

  26.  
  27. public void Log(string message, Category category, Priority priority)

  28. {

  29. //[Debug][Low] Setting the RegionManager.

  30. this.LogContainer.Text += string.Format(CultureInfo.CurrentUICulture, "[{0}][{1}] {2}\r\n", category, priority, message);

  31. //这段代码的作用是让文本框的滚动条滚动到最底部

  32. LogContainer.Select(LogContainer.Text.Length, LogContainer.Text.Length);

  33. }

  34. }

  35. }

 

6. 在EmptyDemoModule中新建一个UserControl——EmptyNavigationItem.xaml,这里只有一个导航按钮"空示例"

 
  1. <UserControl x:Class="EmptyDemoModule.EmptyNavigationItem"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <Button Content="空示例" FontSize="20" Click="Button_Click"/>

  10. </UserControl>


这里也要对该项目添加3个Prim的类库引用(Microsoft.Practices.Prism; Microsoft.Practices.Prism.UnityExtensions;Microsoft.Practces.Unity.Silverlight)以及Infrastructure项目,CodeBehind代码如下:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Prism.Logging;

  14. using Infrastrcture;

  15.  
  16. namespace EmptyDemoModule

  17. {

  18. //ViewSortHintAttribute是为了指定哪个View先在Region中显示出来,设置为02的意思是当前排序优先级为02

  19. [ViewSortHint("02")]

  20. public partial class EmptyNavigationItem : UserControl

  21. {

  22. IRegionManager _regionManager;

  23. ILoggerFacade logger;

  24.  
  25. public EmptyNavigationItem(IRegionManager regionManager,ILoggerFacade logger)

  26. {

  27. logger.Log("初始化【空示例】按钮", Category.Info, Priority.Low); //显示Log信息

  28. InitializeComponent();

  29. this._regionManager = regionManager;

  30. this.logger = logger;

  31. }

  32.  
  33. private void Button_Click(object sender, RoutedEventArgs e)

  34. {

  35. Uri uri = new Uri(ViewNames.EmptyPage, UriKind.Relative);

  36. //导航到MainRegion,把Uri指向的Page显示在PrismRegionShell里命名为MainRegion的Region里

  37. _regionManager.RequestNavigate(RegionNames.MainRegion, uri);

  38. //显示Log信息

  39. logger.Log("导航到Empty示例", Category.Info, Priority.Low);

  40. }

  41. }

  42. }

 

这里需要注意的是,一个Region里需要同时显示多个视图时,视图的顺序问题。比如ItemsControl,哪个先被注册就哪个显示在上面,但是由于Module的加载速度等原因,这时候就无法判断哪个视图会在上面,现在我们需要指定[导航示例]这个按钮在上,那么Prism为我们提供了ViewSortHintAttribute来解决这个问题。在需要进行排序的视图上添加上相应的Attribute就可以了。

7. 在EmptyDemoModule里新建一个EmptyPage的Usercontrol,用于显示在点击“空示例”后,在MainRegion里显示的Page:

 
  1. <UserControl x:Class="EmptyDemoModule.EmptyPage"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <Grid x:Name="LayoutRoot" Background="White">

  10. <TextBlock Text="Empty" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="100"/>

  11. </Grid>

  12. </UserControl>

 

8. 在EmptyDemoModule里新建一个类EmptyModule用于初始化Empty模块,该类实现了Prism的IModule接口。

 
  1. using System;

  2. using System.Net;

  3. using System.Windows;

  4. using System.Windows.Controls;

  5. using System.Windows.Documents;

  6. using System.Windows.Ink;

  7. using System.Windows.Input;

  8. using System.Windows.Media;

  9. using System.Windows.Media.Animation;

  10. using System.Windows.Shapes;

  11. using Microsoft.Practices.Prism.Modularity;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Unity;

  14. using Microsoft.Practices.Prism.Logging;

  15. using Infrastrcture;

  16.  
  17. namespace EmptyDemoModule

  18. {

  19. public class EmptyModule:IModule

  20. {

  21. IRegionManager _regionManager;

  22. IUnityContainer _container;

  23. ILoggerFacade logger;

  24.  
  25. public EmptyModule(IUnityContainer container,IRegionManager regionManager,ILoggerFacade logger)

  26. {

  27. _container = container;

  28. _regionManager = regionManager;

  29. this.logger = logger;

  30. }

  31.  
  32. public void Initialize()

  33. {

  34. //添加显示Log信息

  35. logger.Log("初始化Empty模块", Category.Debug, Priority.Low);

  36. //把EmptyNavigationItem这个"按钮"View注册到RegionName为NavRegion的Region上

  37. _regionManager.RegisterViewWithRegion(RegionNames.NavRegion, typeof(EmptyNavigationItem));

  38. //由于EmptyPage是需要显示在ContentControl里的,同一时刻只能显示一个视图,所以在添加进去之后还需要一个Active的过程,

  39. //因为Prism无法确定每个视图是什么类型,所以就使用了Object,因此在根据ViewName获取实例时,

  40. //会使用IServiceLocator.GetInstance<Object>(VivewName),来把当前container里显示的View指定为EmptyPage页面

  41. _container.RegisterType<object, EmptyPage>(ViewNames.EmptyPage);

  42. }

  43. }

  44. }


9. 下一步我们来在NavigationDemoModule中创建一个ActionController的UserControl,该页面是为了在Container里显示MainRegion中显示导航按钮的。

 
  1. <UserControl x:Class="NavigationDemoModule.ActionController"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <UserControl.Resources>

  10. <Style TargetType="Button">

  11. <Setter Property="Width" Value="70"/>

  12. <Setter Property="Height" Value="25"/>

  13. <Setter Property="Margin" Value="5,0"/>

  14. </Style>

  15. </UserControl.Resources>

  16.  
  17. <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

  18. <Button Content="上一页" Command="{Binding Previous}" IsEnabled="{Binding CanGoBack,Mode=TwoWay}"/>

  19. <Button Content="View A" Command="{Binding SwitchView}" CommandParameter="ViewA"/>

  20. <Button Content="View B" Command="{Binding SwitchView}" CommandParameter="ViewB"/>

  21. <Button Content="View C" Command="{Binding SwitchView}" CommandParameter="ViewC"/>

  22. <Button Content="下一页" Command="{Binding Next}" IsEnabled="{Binding CanGoForwar,Mode=TwoWay}"/>

  23. </StackPanel>

  24. </UserControl>


在该项目中引用Prism类库(Microsoft.Practices.Prism; Microsoft.Practices.Prism.UnityExtensions; Microsoft.Practices.Unity.Silverlight),这里采用了Binding绑定到ViewModel的MVVM架构的方式来构筑。

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Logging;

  13. using Microsoft.Practices.Unity;

  14.  
  15. namespace NavigationDemoModule

  16. {

  17. public partial class ActionController : UserControl

  18. {

  19. public ActionController(ILoggerFacade logger,IUnityContainer container)

  20. {

  21. logger.Log("初始化导航示例中的几个按钮的视图", Category.Info, Priority.Low);

  22. InitializeComponent();

  23.  
  24. //指定当前Page的DataContext为ActionControllerViewModel这个ViewModel,这是MVVM架构的特性

  25. this.Loaded += (s, e) =>

  26. {

  27. this.DataContext = container.Resolve<ActionControllerViewModel>();

  28. };

  29. }

  30. }

  31. }


在该项目中创建一个上面的View对应的ViewModel——ActionControlerViewModel,这个类实现了Prism的NotificationObject,该类实现了MVVM的绑定通知功能

 

 
  1. using System;

  2. using System.Net;

  3. using System.Windows;

  4. using System.Windows.Controls;

  5. using System.Windows.Documents;

  6. using System.Windows.Ink;

  7. using System.Windows.Input;

  8. using System.Windows.Media;

  9. using System.Windows.Media.Animation;

  10. using System.Windows.Shapes;

  11. using Microsoft.Practices.Prism.ViewModel;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Unity;

  14. using Microsoft.Practices.Prism.Logging;

  15. using Infrastrcture;

  16. using Microsoft.Practices.Prism.Commands;

  17. using Microsoft.Practices.Prism;

  18.  
  19. namespace NavigationDemoModule

  20. {

  21. public class ActionControllerViewModel:NotificationObject

  22. {

  23. private IRegionManager _regionManager;

  24. private IRegion _demoShowRegion;

  25. private IUnityContainer _container;

  26. private ILoggerFacade logger;

  27.  
  28. public ActionControllerViewModel(IUnityContainer container, IRegionManager regionManager, ILoggerFacade logger)

  29. {

  30. _container = container;

  31. _regionManager = regionManager;

  32. //获取Region为RegionName为NavDemoShowRegion的Region

  33. _demoShowRegion = _regionManager.Regions[RegionNames.NavDemoShowRegion];

  34. this.logger = logger;

  35.  
  36. //把MVVM中的动作属性方法委托到DelegateCommand()方法里的委托方法去

  37. this.Previous = new DelegateCommand(ToPrevious);

  38. this.Next = new DelegateCommand(ToNext);

  39. this.SwitchView = new DelegateCommand<string>(ToSpecifiedView);

  40. }

  41.  
  42. //MVVM用于与页面Binding的动作属性方法

  43. public ICommand Previous { get; private set; }

  44. public ICommand Next { get; private set; }

  45. public ICommand SwitchView { get; private set; }

  46.  
  47. //数据属性 是否可以回退

  48. public bool CanGoBack

  49. {

  50. get

  51. {

  52. //使用导航API的有点就是可以进行页面的前进和后退,一切后Prism完成。

  53. //这一功能是由IRegionNavigationJournal接口提供

  54. return _demoShowRegion.NavigationService.Journal.CanGoBack;

  55. }

  56. }

  57.  
  58. //数据属性 是否可以前进

  59. public bool CanGoForward

  60. {

  61. get

  62. {

  63. return _demoShowRegion.NavigationService.Journal.CanGoForward;

  64. }

  65. }

  66.  
  67. //动作属性Previous的实际执行的委托方法ToPrevious

  68. void ToPrevious()

  69. {

  70. _demoShowRegion.NavigationService.Journal.GoBack();

  71. ResetNavigationButtonState();

  72. }

  73.  
  74. //动作属性Next的实际执行的委托方法ToNext

  75. void ToNext()

  76. {

  77. _demoShowRegion.NavigationService.Journal.GoForward();

  78. ResetNavigationButtonState();

  79. }

  80.  
  81. //动作属性SwitchView的实际执行的委托方法ToSpecifiedView(string viewName)

  82. void ToSpecifiedView(string viewName)

  83. {

  84. //Prism提供了UriQuery类来帮助我们在导航的时候传递参数

  85. UriQuery query = new UriQuery();

  86. if (viewName == ViewNames.ViewA)

  87. {

  88. query.Add("Time", DateTime.Now.ToShortTimeString());

  89. }

  90. Uri uri = new Uri(viewName + query.ToString(), UriKind.Relative);

  91. //RegionManager.RequestNavigate方法用于进行页面切换,将指定的Region中显示的视图切换为指定的视图

  92. _regionManager.RequestNavigate(RegionNames.NavDemoShowRegion, uri);

  93. logger.Log("跳转到视图[" + viewName + "]", Category.Info, Priority.Low);

  94. ResetNavigationButtonState();

  95. }

  96.  
  97. private void ResetNavigationButtonState()

  98. {

  99. RaisePropertyChanged(() => this.CanGoBack);

  100. RaisePropertyChanged(() => this.CanGoForward);

  101. }

  102. }

  103. }


在该项目里新建一个UserControl——NavigationContainer,用于做ViewA B C的容器Region使用

 
  1. <UserControl x:Class="NavigationDemoModule.NavigationContainer"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. xmlns:prism="http://www.codeplex.com/prism"

  8. d:DesignHeight="300" d:DesignWidth="400">

  9.  
  10. <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

  11. <Border Margin="15" BorderThickness="2" BorderBrush="Red" Width="200" Height="200">

  12. <ContentControl prism:RegionManager.RegionName="NavDemoShowRegion"/>

  13. </Border>

  14.  
  15. <ContentControl prism:RegionManager.RegionName="NavDemoActionRegion"/>

  16. </StackPanel>

  17. </UserControl>

 

后台代码为:

 
  1. namespace NavigationDemoModule

  2. {

  3. public partial class NavigationContainer : UserControl

  4. {

  5. ILoggerFacade logger;

  6. public NavigationContainer(ILoggerFacade logger)

  7. {

  8. logger.Log("初始化【导航示例】", Category.Info, Priority.Low);

  9. InitializeComponent();

  10. this.logger = logger;

  11. }

  12. }

  13. }

 

在该项目中在新建一个NavigationItem的UserControl,该项用于在导航里显示“导航示例”按钮:

 
  1. <UserControl x:Class="NavigationDemoModule.NavigationItem"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <Button Content="导航示例" FontSize="20" Click="Button_Click"/>

  10. </UserControl>


后台代码为:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Prism.Logging;

  14. using Infrastrcture;

  15.  
  16. namespace NavigationDemoModule

  17. {

  18. [ViewSortHint("01")] //注意这个特性标示 用于在ItemControl显示时排序使用

  19. public partial class NavigationItem : UserControl

  20. {

  21. IRegionManager _regionManager;

  22. ILoggerFacade logger;

  23.  
  24. public NavigationItem(IRegionManager regionManager,ILoggerFacade logger)

  25. {

  26. logger.Log("初始化【导航示例】按钮", Category.Info, Priority.Low);

  27. InitializeComponent();

  28. this._regionManager = regionManager;

  29. this.logger = logger;

  30. }

  31.  
  32. private void Button_Click(object sender, RoutedEventArgs e)

  33. {

  34. Uri uri = new Uri(ViewNames.NavigationContainer, UriKind.Relative);

  35. //把Region为MainRegion里的视图导航成为uri——也就是NavigationContainer的视图

  36. _regionManager.RequestNavigate(RegionNames.MainRegion, uri);

  37. logger.Log("导航到Navigation示例", Category.Info, Priority.Low);

  38. }

  39. }

  40. }


接下来,我们来创建在NavigationContainer里要显示的另外3个View

先新建一个ViewA的UserControl:

 
  1. <UserControl x:Class="NavigationDemoModule.ViewA"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <Grid x:Name="LayoutRoot" Background="White">

  10. <TextBlock Text="ViewA" FontSize="23"/>

  11. </Grid>

  12. </UserControl>


后台代码:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Unity;

  13. using Microsoft.Practices.Prism.Logging;

  14.  
  15. namespace NavigationDemoModule

  16. {

  17. public partial class ViewA : UserControl

  18. {

  19. public ViewA(IUnityContainer container,ILoggerFacade logger)

  20. {

  21. InitializeComponent();

  22. logger.Log("创建一个ViewA",Category.Info,Priority.Low);

  23. //指定当前View的ViewModel是ViewAViewModel

  24. this.DataContext = container.Resolve<ViewAViewModel>();

  25. }

  26. }

  27. }


接下来要创建一个ViewAViewModel的类,用于作为ViewA这个View的ViewModel使用,这里需要提一下的就是INavigationAware接口,这个接口使得视图或者其对应的ViewModel也可以参与到导航的过程中来。所以这个接口既可以由视图来实现,也可以由视图的DataContext——也就是通常所指的ViewModel来实现。

在这个接口中,当从本页面转到其他页面的时候,会调用本页面的OnNavigatedFrom方法,navigationContext中会包含目标页面的URI;

当从其他页面导航到本页面的时候,首先会调用本页面的IsNavigationTarget方法,这个方法返回的是一个bool值,简单的说这个方法就是告诉Prism,是否重复使用这个视图的实例还是在创建一个;然后再调用OnNavigationTo方法。在导航到本页面的时候,就可以从NavigationContext中取出传递的参数。

这里还需要说明一下的是在ActionControllerViewModel中我们在点击ViewA 的时候会把Region指向的页面导航到ViwA上,并且由于使用了UriQuery.Add()方法把key为“Time”的参数的值也传递到了ViewA页面上,我们就可以在ViewA页面中获取到该参数的值,使用的是UriQuery["Key"]来获取。

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Text;

  5. using Microsoft.Practices.Prism.Regions;

  6. using Microsoft.Practices.Prism.Logging;

  7. using Microsoft.Practices.Prism;

  8.  
  9. namespace NavigationDemoModule

  10. {

  11. //INavigationAware接口可以使视图或者其对应的ViewModel可以参加到页面导航的过程中来,

  12. //这个接口既可以由视图来实现,也可以由视图的DataContext——也就是ViewModel来实现

  13. public class ViewAViewModel:INavigationAware

  14. {

  15. ILoggerFacade logger;

  16.  
  17. public ViewAViewModel(ILoggerFacade logger)

  18. {

  19. this.logger = logger;

  20. }

  21.  
  22. //当从其他页面导航到本页面的时候,首先调用该方法,判断是否要新建一个本页面还是使用原来已有该视图的实例,是否可以重用(true如果之前已经创建了ViewA的对象就使用之前的对象;false不管之前有没有创建ViewA的对象,每次导航到ViewA页面的时候都要创建一个新的ViewA对象)

 
  1. public bool IsNavigationTarget(NavigationContext navigationContext)

  2. {

  3. return true;

  4. }

  5.  
  6. //当从本页面导航到其他页面的时候调用该方法

  7. public void OnNavigatedFrom(NavigationContext navigationContext)

  8. {

  9.  
  10. }

  11.  
  12. //当从其他页面导航到该页面时候触发该方法

  13. public void OnNavigatedTo(NavigationContext navigationContext)

  14. {

  15. //获取从其他页面导航到该页面时传递的参数集合Parameters

  16. UriQuery query = navigationContext.Parameters;

  17. //在ActionControllerViewModel里传递过来的UriQuery里有Time这个Key的值,那么在ViewA里就可以获取到该值

  18. string time = query["Time"];

  19. logger.Log(string.Format("ViewA:现在时间{0}", time), Category.Info, Priority.Medium);

  20. }

  21. }

  22. }


紧接着我们来创建ViewB,也就是点击在ActionController中的“ViewB”按钮的时候,NavigationContainer里所显示的View

 
  1. <UserControl x:Class="NavigationDemoModule.ViewB"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <TextBlock Text="ViewB" FontSize="23"/>

  10. </UserControl>

 

后台代码实现了INavigationAware接口,也就是可以接受从别的页面导航过来的时候参数,这里就先了实现INavigationAware的两种方式一种是前面我们提到的在ViewModel中继承,另一种就是直接在后台代码里实现该接口。ViewA和ViewB都实现了INavigationAware接口,不同之处在于ViewA是在其对应的ViewModel ViewAViewModel类中实现的,而ViewB是直接在Code Behind中实现的。Prism对MVVM提供了良好的支持,因此既可以选择在视图中实现该接口也可以在对应的ViewModel中实现。

这里还需要注意的是ViewB中的IsNavigationTarget方法返回的是false,而ViewA中则返回了True,可以通过点击三个按钮进行页面跳转,观察Log可以发现,ViewA只创建了一次,而ViewB则每次都要新创建,还有就是在跳转到ViewA的时候传递了参数,可以在OnNavigatedTo方法中取出参数。

 

ViewB的CodeBehind:

 

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Prism.Logging;

  14.  
  15. namespace NavigationDemoModule

  16. {

  17. public partial class ViewB : UserControl,INavigationAware

  18. {

  19. public ViewB(ILoggerFacade logger)

  20. {

  21. InitializeComponent();

  22. logger.Log("创建了一个ViewB", Category.Info, Priority.Low);

  23. }

  24.  
  25. public bool IsNavigationTarget(NavigationContext navigationContext)

  26. {

  27. return false;

  28. }

  29.  
  30. public void OnNavigatedFrom(NavigationContext navigationContext)

  31. {

  32.  
  33. }

  34.  
  35. public void OnNavigatedTo(NavigationContext navigationContext)

  36. {

  37.  
  38. }

  39. }

  40. }


 

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Prism.Logging;

  14.  
  15. namespace NavigationDemoModule

  16. {

  17. public partial class ViewB : UserControl,INavigationAware

  18. {

  19. public ViewB(ILoggerFacade logger)

  20. {

  21. InitializeComponent();

  22. logger.Log("创建了一个ViewB", Category.Info, Priority.Low);

  23. }

  24.  
  25. public bool IsNavigationTarget(NavigationContext navigationContext)

  26. {

  27. return false;

  28. }

  29.  
  30. public void OnNavigatedFrom(NavigationContext navigationContext)

  31. {

  32.  
  33. }

  34.  
  35. public void OnNavigatedTo(NavigationContext navigationContext)

  36. {

  37.  
  38. }

  39. }

  40. }


下一步创建ViewC:

 
  1. <UserControl x:Class="NavigationDemoModule.ViewC"

  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  6. mc:Ignorable="d"

  7. d:DesignHeight="300" d:DesignWidth="400">

  8.  
  9. <Grid x:Name="LayoutRoot" Background="White">

  10. <TextBlock Text="ViewC" FontSize="23"/>

  11. </Grid>

  12. </UserControl>


 

ViewC的CodeBehind:

 
  1. using System;

  2. using System.Collections.Generic;

  3. using System.Linq;

  4. using System.Net;

  5. using System.Windows;

  6. using System.Windows.Controls;

  7. using System.Windows.Documents;

  8. using System.Windows.Input;

  9. using System.Windows.Media;

  10. using System.Windows.Media.Animation;

  11. using System.Windows.Shapes;

  12. using Microsoft.Practices.Prism.Logging;

  13.  
  14. namespace NavigationDemoModule

  15. {

  16. public partial class ViewC : UserControl

  17. {

  18. public ViewC(ILoggerFacade logger)

  19. {

  20. InitializeComponent();

  21. logger.Log("创建一个ViewC", Category.Info, Priority.Low);

  22. }

  23. }

  24. }


下一步是添加该项目的Module文件,新建一个NavDemoModule类:

 
  1. using System;

  2. using System.Net;

  3. using System.Windows;

  4. using System.Windows.Controls;

  5. using System.Windows.Documents;

  6. using System.Windows.Ink;

  7. using System.Windows.Input;

  8. using System.Windows.Media;

  9. using System.Windows.Media.Animation;

  10. using System.Windows.Shapes;

  11. using Microsoft.Practices.Prism.Modularity;

  12. using Microsoft.Practices.Prism.Regions;

  13. using Microsoft.Practices.Unity;

  14. using Microsoft.Practices.Prism.Logging;

  15. using Infrastrcture;

  16.  
  17. namespace NavigationDemoModule

  18. {

  19. public class NavDemoModule:IModule

  20. {

  21. IRegionManager _regionManager;

  22. IUnityContainer _container;

  23. ILoggerFacade logger;

  24.  
  25. public NavDemoModule(IUnityContainer container,IRegionManager regionManager,ILoggerFacade logger)

  26. {

  27. _container = container;

  28. _regionManager = regionManager;

  29. this.logger = logger;

  30. }

  31.  
  32. //如果一个页面相对来说不大变化,如导航区,在程序初始化的过程完成后就不会轻易变动,

  33. //这时候就比较适合使用RegisterViewWithRegion方法,通常可以在Module的Initialize方法中完成这个过程

  34. public void Initialize()

  35. {

  36. logger.Log("初始化Navigation模块", Category.Debug, Priority.Low);

  37. _regionManager.RegisterViewWithRegion(RegionNames.NavRegion, typeof(NavigationContainer));

  38. _regionManager.RegisterViewWithRegion(RegionNames.MainRegion, () => _container.Resolve<NavigationContainer>());

  39. _regionManager.RegisterViewWithRegion(RegionNames.NavDemoActionRegion, typeof(ActionController));

  40.  
  41. // 注意注册的类型的必须是object,因为Prism无法确定视图的类型,所以就用了object

  42. _container.RegisterType<object, ViewA>(ViewNames.ViewA);

  43. _container.RegisterType<object, ViewB>(ViewNames.ViewB);

  44. _container.RegisterType<object, ViewC>(ViewNames.ViewC);

  45. }

  46. }

  47. }


10. 最后我们来补全PrismRegionDemo里的内容,先添加一个类Bootstrapper:

 
  1. using System;

  2. using System.Windows;

  3. using Microsoft.Practices.Prism.UnityExtensions;

  4. using Microsoft.Practices.Prism.Logging;

  5. using Microsoft.Practices.Prism.Modularity;

  6. using Microsoft.Practices.Unity;

  7. using Infrastrcture;

  8.  
  9.  
  10. namespace PrismRegionDemo

  11. {

  12. public class Bootstrapper : UnityBootstrapper

  13. {

  14. CallBackLogger logger = new CallBackLogger();

  15.  
  16. protected override ILoggerFacade CreateLogger()

  17. {

  18. return logger;

  19. }

  20.  
  21. protected override IModuleCatalog CreateModuleCatalog()

  22. {

  23. return Microsoft.Practices.Prism.Modularity.ModuleCatalog.

  24. CreateFromXaml(new Uri("/PrismRegionDemo;component/ModulesCatalog.xaml", UriKind.Relative));

  25. }

  26.  
  27. protected override void ConfigureContainer()

  28. {

  29. base.ConfigureContainer();

  30. this.Container.RegisterInstance<CallBackLogger>(logger);

  31. }

  32.  
  33. protected override DependencyObject CreateShell()

  34. {

  35. return Container.TryResolve<PrismRegionShell>();

  36. }

  37.  
  38. protected override void InitializeShell()

  39. {

  40. App.Current.RootVisual = (UIElement)Shell;

  41. }

  42. }

  43. }


在添加一个Silverlight REsource Dictionary——ModulesCatalog.xaml:

 
  1. <Modularity:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  3. xmlns:sys="clr-namespace:System;assembly=mscorlib"

  4. xmlns:Modularity="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">

  5.  
  6. <Modularity:ModuleInfo Ref="NavigationDemoModule.xap" ModuleName="NavigationDemoModule"

  7. ModuleType="NavigationDemoModule.NavDemoModule, NavigationDemoModule,

  8. Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

  9. <Modularity:ModuleInfo Ref="EmptyDemoModule.xap" ModuleName="EmptyDemoModule"

  10. ModuleType="EmptyDemoModule.EmptyModule, EmptyDemoModule,

  11. Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

  12. </Modularity:ModuleCatalog>


最后我们还要修改App.xaml.cs里的启动信息:

 
  1. private void Application_Startup(object sender, StartupEventArgs e)

  2. {

  3. Bootstrapper bootstrapper = new Bootstrapper();

  4. bootstrapper.Run();

  5. }


 源代码 : http://download.csdn.net/detail/eric_k1m/6216167

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值