Prism学习之SilverlightWindowRegionAdapter
很多应用都需要多窗口支持,例如IM通讯工具,多窗口显示也能够提高的操作的灵活性,这个论据可以参考windows OS,但Silverlight中却没有内置提供多窗口显示支持,我们只能自己开发个“窗口”控件了,其实这样也好,省得还要去掉Windows窗口那些默认的显示效果;开发Silverlight或者WPF的人都喜欢用Prism来作为开发框架(Prism2.2发布了,全面支持Silverlight4 );本文讨论的是解决在Prism中使用多窗口的问题。
Prism是靠一堆统一管理的“Region”来动态显示模块界面的:先在需要显示界面的地方放个Region
然后在模块文件(继承自IModule)中
这样就可以把指定的模块显示在指定的区域当中了。
Prism提供了三种类型的Region,分别是ContentControl、ItemsControl、Selector,偏偏没有用来显示“多窗口”的,我就找啊找啊,终于在这个项目http://compositewpfcontrib.codeplex.com/ 中找到了一个WPF的WindowRegionAdapter,所以我就依葫芦画瓢,给弄出个Silverlight的WindowRegionAdapter。
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.Composite.Regions;
using Microsoft.Practices.Composite.Presentation.Regions;
using System.Collections.Specialized;
namespace CompositeTest1.Common.Utilities
{
public class SLWindowRegionAdapter : RegionAdapterBase<Grid>
{
public SLWindowRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
: base(regionBehaviorFactory)
{ }
protected override void Adapt(IRegion region, Grid regionTarget)
{
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
protected override void AttachBehaviors(IRegion region, Grid regionTarget)
{
base.AttachBehaviors(region, regionTarget);
WindowRegionBehavior behavior = new WindowRegionBehavior(regionTarget, region);
behavior.Attach();
}
}
public class WindowRegionBehavior
{
private readonly WeakReference _ownerWeakReference;
private readonly WeakReference _regionWeakReference;
public WindowRegionBehavior(Grid owner, IRegion region)
{
_ownerWeakReference = new WeakReference(owner);
_regionWeakReference = new WeakReference(region);
}
public void Attach()
{
IRegion region = _regionWeakReference.Target as IRegion;
if (region != null)
{
region.Views.CollectionChanged += new NotifyCollectionChangedEventHandler(Views_CollectionChanged);
region.ActiveViews.CollectionChanged += new NotifyCollectionChangedEventHandler(ActiveViews_CollectionChanged);
}
}
public void Detach()
{
IRegion region = _regionWeakReference.Target as IRegion;
if (region != null)
{
region.Views.CollectionChanged -= Views_CollectionChanged;
region.ActiveViews.CollectionChanged -= ActiveViews_CollectionChanged;
}
}
void window_LostFocus(object sender, RoutedEventArgs e)
{
IRegion region = _regionWeakReference.Target as IRegion;
System.Windows.Controls.Window window = sender as System.Windows.Controls.Window;
if (window != null && region != null)
if (region.Views.Contains(window.Content))
region.Deactivate(window.Content);
}
void window_GotFocus(object sender, RoutedEventArgs e)
{
IRegion region = _regionWeakReference.Target as IRegion;
System.Windows.Controls.Window window = sender as System.Windows.Controls.Window;
if (window != null && !region.ActiveViews.Contains(window.Content) && region.Views.Contains(window.Content))
region.Activate(window.Content);
}
private void window_Closed(object sender, EventArgs e)
{
System.Windows.Controls.Window window = sender as System.Windows.Controls.Window;
IRegion region = _regionWeakReference.Target as IRegion;
if (window != null && region != null)
if (region.Views.Contains(window.Content))
region.Remove(window.Content);
Grid owner = _ownerWeakReference.Target as Grid;
StackPanel spanel = owner.FindName("SLWindow") as StackPanel;
if (spanel != null)
{
Button btn = spanel.FindName(window.Name.ToString().TrimEnd('M')) as Button;
spanel.Children.Remove(btn);
}
}
private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Grid owner = _ownerWeakReference.Target as Grid;
if (owner == null)
{
Detach();
return;
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (object view in e.NewItems)
{
System.Windows.Controls.Window window = GetContainerWindow(owner, view);
if (window != null)
{
window.Focus();
}
}
}
}
private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Grid owner = _ownerWeakReference.Target as Grid;
if (owner == null)
{
Detach();
return;
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (object view in e.NewItems)
{
System.Windows.Controls.Window window = new System.Windows.Controls.Window();
window.GotFocus += new RoutedEventHandler(window_GotFocus);
window.LostFocus += new RoutedEventHandler(window_LostFocus);
window.Closed += new EventHandler(window_Closed);
window.Content = view;
StackPanel spanel = owner.FindName("SLWindow") as StackPanel;
if (spanel != null)
{
int index = spanel.Children.Count + 1;
Button btn = new Button() { Name = "Window" + index, Content = "窗口" + index };
window.Name = "Window" + index+"M";
window.TitleContent = "窗口" + index;
btn.Tag = window;
btn.Click += new RoutedEventHandler(btn_Click);
spanel.Children.Add(btn);
}
window.Container = owner;
window.Show(DialogMode.Default);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (object view in e.OldItems)
{
System.Windows.Controls.Window window = GetContainerWindow(owner, view);
if (window != null)
window.Close();
}
}
}
void btn_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
System.Windows.Controls.Window window = btn.Tag as System.Windows.Controls.Window;
if (window != null)
{
window.Visibility = window.Visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
}
private System.Windows.Controls.Window GetContainerWindow(Grid owner, object view)
{
foreach (UIElement ui in owner.Children)
{
System.Windows.Controls.Window window = ui as System.Windows.Controls.Window;
if (window != null && window.Content == view)
return window;
}
return null;
}
}
}
自定义的RegionAdapter要实现RegionAdapterBase<T>,这个T就是要承载Region的容器owner,我们的Window控件是需要一个Grid的容器的,所以就是RegionAdapterBase<Grid>,注意里面并没有直接实现Adapt方法,而是在重载AttachBehaviors方法中实现了对region.Views和region.ActiveViews的管理,并实现了窗口操作对View的影响,里面我还加了个类似于Windows任务栏的功能,当然实现的不完善,需要改进。
我们需要把自定义的RegionAdapter介绍给Prism中,让Prism认识他,通过在Bootstrapper中重载ConfigureRegionAdapterMappings方法实现
{
RegionAdapterMappings regionAdapterMappings = Container.TryResolve<RegionAdapterMappings>();
IRegionBehaviorFactory regionBehaviorFactory = Container.TryResolve<IRegionBehaviorFactory>();
if (regionAdapterMappings != null && regionBehaviorFactory != null)
{
regionAdapterMappings.RegisterMapping(typeof(Grid), new SLWindowRegionAdapter(regionBehaviorFactory));
}
return base.ConfigureRegionAdapterMappings();
}
然后在我需要承载弹出窗口的Grid更改成
然后在要呈现弹出窗口的地方
if (region != null)
region.Add(view);
搞定,看效果:
1、自动载入模块A,在模块A中弹出窗口显示模块A中的其他View
2、手动载入模块B,并在新的窗口中显示模块B的View