如何利用 CM 实现多视图切换

一直以来,都在大屏幕(相对于小屏的笔记本而言,21.5'')上做开发,写的东西搬到小屏幕上有点不适应。功能太密集,视图太紧凑,操作感不强。

如果针对小屏幕调整视图,那势必会影响大屏幕的体验。

Caliburn.Micro (简称CM) 有个功能:多个VIEW使用同一MODEL. 网上很少有相关文章对它进行描述,官方文档里有一小段对它进行描述,但只是一笔带过,在加上是英文的,看的似懂非懂。

上两张图,看一下效果:

如何利用 CM 实现多视图切换_第1张图片

 

如何利用 CM 实现多视图切换_第2张图片

 

一个是普通视图,一个是紧凑视图。可能你会说,没什么差别啊?是的,因为我缩小了窗口,紧凑视图是针对 1280*800的分辨率做了简单优化了的。

切换视图的时候,不需要重新加载数据,因为这两个视图用的是同一个MODEL。

 

看一下视图的命名规则:

如何利用 CM 实现多视图切换_第3张图片

OrderQueryView -> OrderQuery 文件夹(命名空间)-> Normal, Small

原来OrderQueryView.xaml 的内容就是普通视图。现在改了:

1     <Grid>
2         <ContentControl cal:View.Model="{Binding}" cal:View.Context="{Binding Source={x:Static s:GlobalData.Instance}, Path=ViewMode, Mode=TwoWay}" />
3     </Grid>

只有这一段,其它的都挪到 Normal.xaml 和 Small.xaml 里了。至于怎么切换,都在 View.Context 上.

ViewContext 绑定到一个静态对象 的 静态属性 的 属性上。这样当这个属性的属性改变时,所有绑定在它上的视图都会跟着切换。

为了能绑定静态属性上(作用就是全局),而且还支持 INotifyPropertyChanged, 我绕了一个大圈子和很多弯路。

 1     public static class GlobalData {
 2 
 3         public static CompositionContainer MefContainer = null;
 4 
 5         public static T GetInstance<T>() {
 6             return MefContainer.GetExportedValue<T>();
 7         }
 8 
 9         private static GlobalDataHolder instance;
10         public static GlobalDataHolder Instance {
11             get {
12                 if (instance == null)
13                     instance = new GlobalDataHolder();
14                 return instance;
15             }
16         }
17 
18 
19 
20 
21         public class GlobalDataHolder : INotifyPropertyChanged {
22 
23             internal GlobalDataHolder() { }
24 
25             public event PropertyChangedEventHandler PropertyChanged;
26             public void NotifyPropertyChanged(string propertyName) {
27                 if (PropertyChanged != null) {
28                     PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
29                 }
30             }
31 
32             private ViewModes viewMode;
33             public ViewModes ViewMode {
34                 get {
35                     return this.viewMode;
36                 }
37                 set {
38                     if (this.viewMode != value) {
39                         this.viewMode = value;
40                         this.NotifyPropertyChanged("ViewMode");
41                     }
42                 }
43             }
44         }
45     }

虽然不是很美观,但是目的达到了。

来看一下 ViewModes 的定义

1     public enum ViewModes {
2         [Description("普通视图")]
3         Normal,
4         [Description("紧凑视图")]
5         Small
6     }

同样是 Normal 和 Small ,对应 Normal.xaml 和 Small.xaml

 

另外,有人问我,插件化开发,将 View 和 ViewMode 放到 DLL里去,提示找不到 View ,该怎么办?

我用的是 MEF,我的解决办法是在 Bootstrapper 里重写:

 1         protected override void StartRuntime() {
 2             base.StartRuntime();
 3 
 4             //用 Assembly.Instance.Add 可以用于解决加载不同DLL内的View
 5             var dllFiles = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.AllDirectories);
 6             foreach (var dll in dllFiles) {
 7                 try {
 8                     var asm = Assembly.LoadFrom(dll);
 9                     if (asm.GetTypes().Any(t =>
10                         t.GetInterfaces().Contains(typeof(IViewAware))
11                         || t.GetInterfaces().Contains(typeof(IScreen))
12                         )) {
13                         AssemblySource.Instance.Add(asm);
14                     }
15                 } catch {
16                 }
17             }
18         }
19 
20         ////主屏幕启动后才会执行这个方法,所以 AssemblySource 的操作不能放到这里
21         //protected override void OnStartup(object sender, System.Windows.StartupEventArgs e) {
22         //    base.OnStartup(sender, e);
23         //}

即扫描DLL内 IScreen 和 IViewAware 的实现,将相关的 Assembly 加入到 AssemblySource 内,这样就不用强引用了!

 

 

你可能感兴趣的:(视图)