WPF Prism(一)Region
WPF Prism(二)Module
WPF Prism(三)ViewModelLocator
WPF Prism(四)MVVM
WPF Prism(五)Navigation
WPF Prism(六)Dialog
在Prism当中,一个页面我们可以不再为其固定显示的内容,而这种概念变成了区域(Region)划分的概念。将页面显示的区域划分成N个Region,每一个Region将动态分配区域。它将负责承担我们的UI组件或者控件。
RegionManager功能
首先,我们需要将MainWindow的构造函数传入IRegionManager参数:
public partial class MainWindow : Window
{
public MainWindow(IRegionManager regionManager)
{
InitializeComponent();
}
}
注意:因为MainWindow对象是我们在App类中使用容器解析得到的,那么它需要的依赖IRegion也会自动被创建,不需要我们自己创建。
在定义视图与Region之间的映射关系之前我们需要先定义Region,定义Region有两种方法:
<Window
xmlns:prism="http://prismlibrary.com/"
Title="Shell">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.2*" />
<RowDefinition />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" prism:RegionManager.RegionName="HeaderRegion" />
</Grid>
</Window>
代码有省略,重点关注需要引入prism命名空间。
<ContentControl x:Name="Header" Grid.Row="0" />
public MainWindow(IRegionManager regionManager)
{
InitializeComponent();
RegionManager.SetRegionName(Header, "HeaderRegion");
}
上述两种方式都是可以的。
现在我们在MainWindow中定义了三个Region,同时定义了三个UserControl。
<Window
x:Class="PrismBlankAppCore.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
Title="Shell"
Width="525"
Height="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.2*" />
<RowDefinition />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" prism:RegionManager.RegionName="HeaderRegion" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0" prism:RegionManager.RegionName="MenuRegion" />
<ContentControl Grid.Column="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
</Grid>
</Window>
在Prism中有两种方式来定义视图与Region之间的映射关系——View Discovery和View Injection。
public MainWindow(IRegionManager regionManager)
{
InitializeComponent();
//View Dicovery
regionManager.RegisterViewWithRegion("HeaderRegion", typeof(HeaderView));
regionManager.RegisterViewWithRegion("MenuRegion", typeof(MenuView));
regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
}
public partial class MainWindow : Window
{
private IRegionManager regionManager;
private IContainerExtension container;
public MainWindow(IRegionManager regionManager, IContainerExtension container)
{
InitializeComponent();
//View Dicovery
//regionManager.RegisterViewWithRegion("HeaderRegion", typeof(HeaderView));
//regionManager.RegisterViewWithRegion("MenuRegion", typeof(MenuView));
//regionManager.RegisterViewWithRegion("ContentRegion", typeof(ContentView));
this.regionManager = regionManager;
this.container = container;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//View Injection
HeaderView headerView = container.Resolve<HeaderView>();
regionManager.Regions["HeaderRegion"].Add(headerView);
MenuView menuView = container.Resolve<MenuView>();
regionManager.Regions["MenuRegion"].Add(menuView);
ContentView contentView = container.Resolve<ContentView>();
regionManager.Regions["ContentRegion"].Add(contentView);
}
}
注入View的时候需要用到Add方法,还得提前将View对象创建好,所以在构造函数中我们需要注入IRegionManager和IContainerExtension的实现。
值得一提的是,在构造函数中IRegionManager对象还没有创建完成(应该是RegionName还没有创建完成),所以在Load函数中完成View Injection。如果需要View对象是单例的,可以提前在App类的重写函数中注册单例对象。
将App类修改为:View对象注册为单例对象。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterSingleton<HeaderView>();
containerRegistry.RegisterSingleton<MenuView>();
containerRegistry.RegisterSingleton<ContentView>();
}
MainWindow增加了三个按钮:
<UniformGrid Grid.Row="2" Columns="3">
<Button
Width="80"
Height="30"
Content="Activate" Click="Activate"/>
<Button
Width="80"
Height="30"
Content="Refresh" Click="Refresh" />
<Button
Width="80"
Height="30"
Content="Deactivate" Click="Deactivate
" />
</UniformGrid>
增加对应的三个方法:
private void Activate(object sender, RoutedEventArgs e)
{
//激活
HeaderView headerView = container.Resolve<HeaderView>();
regionManager.Regions["HeaderRegion"].Activate(headerView);
MenuView menuView = container.Resolve<MenuView>();
regionManager.Regions["MenuRegion"].Activate(menuView);
ContentView contentView = container.Resolve<ContentView>();
regionManager.Regions["ContentRegion"].Activate(contentView);
}
private void Refresh(object sender, RoutedEventArgs e)
{
//刷新 因为已经add过,所以需要先remove
HeaderView headerView = container.Resolve<HeaderView>();
regionManager.Regions["HeaderRegion"].Remove(headerView);
MenuView menuView = container.Resolve<MenuView>();
regionManager.Regions["MenuRegion"].Remove(menuView);
ContentView contentView = container.Resolve<ContentView>();
regionManager.Regions["ContentRegion"].Remove(contentView);
regionManager.Regions["HeaderRegion"].Add(headerView);
regionManager.Regions["MenuRegion"].Add(menuView);
regionManager.Regions["ContentRegion"].Add(contentView);
}
private void Deactivate(object sender, RoutedEventArgs e)
{
//使无效
HeaderView headerView = container.Resolve<HeaderView>();
regionManager.Regions["HeaderRegion"].Deactivate(headerView);
MenuView menuView = container.Resolve<MenuView>();
regionManager.Regions["MenuRegion"].Deactivate(menuView);
ContentView contentView = container.Resolve<ContentView>();
regionManager.Regions["ContentRegion"].Deactivate(contentView);
}
Prism提供了许多内置得RegionAdapter:
除此之外,如果想要实现控件作用域Region,则必须创建自定义RegionAdapter。
下面我们创建一个一个基于StackPanel的自定义适配器。
public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
{
public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, StackPanel regionTarget)
{
throw new System.NotImplementedException();
}
protected override IRegion CreateRegion()
{
throw new System.NotImplementedException();
}
}
public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
{
public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, StackPanel regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement element in e.NewItems)
{
regionTarget.Children.Add(element);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (FrameworkElement element in e.OldItems)
{
regionTarget.Children.Remove(element);
}
}
//handle other case
};
}
protected override IRegion CreateRegion()
{
return new Region();
}
}
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
}