Caliburn.Micro开发框架介绍--Windowsphone
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Caliburn.Micro开发框架介绍--Windowsphone
Caliburn.Micro开发框架介绍
Caliburn是⼀套基于XAML的开发框架,它⼩巧⽽强⼤。
利⽤它不但能提⾼开发效率,还可以提⾼XAML程序开发的可维护⾏、可扩展性和可测试性。
Caliburn.Micro则是专门针对Windows phone开发的版本。
MVVM简介
MVVM源于微软的软件开发模式,可以粗略的认为它是MVC模式的发展,原来Controller的职能被拆分,其中值转换器(Value Converter)和绑定器(binder)已经由框架实现,程序员可以更关注在逻辑实现上。
MVVM的开发基于事件驱动,实现UI层和逻辑层的分离,从⽽使UI设计⼈员和程序员各施其职。
MVVM中的View Model在Model和View之间扮演着值转换器的⾓⾊,把Model的数据交给View去绑定,把View的数据提交给Model;同时也要实现mediator设计模式,成为View和Model之间的逻辑协调者。
Caliburn.Micro简介
Caliburn.Micro使⽤各种的配置和约定使得代码⼯作变得简洁。
⽐如:你⽆需使⽤ViewModelLocator为某个View定位它的View Model,在Caliburn.Micro中只需要按照约定把View的名字加上后缀ViewModel,就是它的View Model的名字,如:MainPage和MainPageViewModel。
Caliburn.Micro⾃动把ViewModel绑定到View的DataContext。
如果ViewModel的属性名和控件的名称相同,那么就会⾃动绑定上。
如果该属性的值发⽣变化,控件的也能得到更新。
此外,Caliburn.Micro还为Windows phone的特性提供辅助,例如:tombstone的管理,应⽤程序⽣命周期和launcher。
当然,你也可以⾃定义各种约定。
准备⼯作
下载
⼊⼝bootstrapper
Bootstrapper是Caliburn.Micro的⼊⼝,所有的ViewModel必须在这个类⾥注册,否则Caliburn.Micro⽆法为你的View和ViewModel建⽴关联。
如果需要⾃定义命名约定,也是在这个类⾥定义。
我们新建⼀个WP8⼯程,先删除默认创建的MainPage.xaml,创建Views⽬录,在Views⽬录下创建MainPage.xaml,创建ViewModels⽬录,在ViewModels下创建MainPageViewModel.cs类,修改WMAppManifest.xml中的起始页⾯为Views/MainPage.xaml。
在⼯程的根⽬录下创建bootstrapper.cs,其内容如下。
1.
public class AppBootstrapper : PhoneBootstrapper
2.
{
3.
PhoneContainer container;
4.
5.
protected override void Configure()
6.
{
7.
container = new PhoneContainer(RootFrame);
8.
9.
container.RegisterPhoneServices();
10.
//注册所有ViewModel
11.
container.PerRequest<MainPageViewModel>();
12.
13.
AddCustomConventions();
14.
}
15.
16.
static void AddCustomConventions()
17.
{
18.
//ellided ⾃定义命名约定
19.
}
20.
protected override object GetInstance(Type service, string key)
22.
{
23.
return container.GetInstance(service, key);
24.
}
25.
26.
protected override IEnumerable<object> GetAllInstances(Type service)
27.
{
28.
return container.GetAllInstances(service);
29.
}
30.
31.
protected override void BuildUp(object instance)
32.
{
33.
container.BuildUp(instance);
34.
}
35.
}
初始化bootstrapper
修改App.xml,初始化bootstrapper。
1.
<Application
2.
x:Class="CSH.IntelliHIS.WP8.App"
3.
xmlns="/winfx/2006/xaml/presentation"
4.
xmlns:x="/winfx/2006/xaml"
5.
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
6.
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
7.
xmlns:local="clr-namespace:CSH.IntelliHIS.WP8">
8.
9.
<!--Application Resources-->
10.
<Application.Resources>
11.
<local:AppBootstrapper x:Key="bootstrapper" />
12.
</Application.Resources>
13.
14.
</Application>
修改App.xml.cs,默认的初始化代码已经不需要了。
1.
public partial class App : Application
2.
{
3.
public App()
4.
{
5.
InitializeComponent();
6.
7.
}
命名约定(naming convention)
命名约定让Caliburn.Micro能⾃动关联起View和View Model,如果我们运⾏⼯程,浏览MainPage页⾯,则MainPageViewModel⾃动被实例化。
接下来就可以看看Caliburn.Micro是如何扮演值转换器的⾓⾊,看它是如何在View和ViewModel之间传递和转换值,以便View绑定这些值。
我们在ViewModel⾥增加⼀个叫Name的属性(Property)。
1.
public class MainPageViewModel: PropertyChangedBase
2.
{
3.
public MainPageViewModel()
4.
{
5.
Name = "Matteo";
6.
}
7.
private string name;
8.
public string Name
9.
{
10.
get { return name; }
11.
set
12.
{
13.
name = value;
14.
NotifyOfPropertyChange(() => Name);
15.
}
16.
}
17.
}
在View⾥增加⼀个⽂本控件,使⽤和这个属性相同的名字,值就会⾃动绑定上去。
(Caliburn.Micro也⽀持原有的binding语法)
<TextBlock x:Name="Name"/>
注意,ViewModel继承了PropertyChangedBase类,在Name属性被修改的时候,调⽤NotifyOfPropertyChange⽅法发出通知,这使得Name属性被修改时,View⾥的绑定控件TextBlock能⾃动地更新。
⾏为(Actions)
命令(Commands)
Caliburn.Micro使⽤⼀种叫做⾏为的机制,使得ViewModel响应View的事件。
它很简单。
View控件定义了名字。
<Button Content="Show name"x:Name="ShowNameAction" />
ViewModel的⽅法只要使⽤相同名字就会得到调⽤。
public void ShowNameAction()
{
MessageBox.Show("Clicked");
}
当然,也⽀持⾃定义调⽤⽅法。
需先引⽤
xmlns:i=”clrnamespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity”xmlns:cal=”clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro”
View⾥这样定义
1.
<Button x:Name="ShowName">
<i:Interaction.Triggers>
3.
<i:EventTrigger EventName="Click">
4.
<cal:ActionMessage MethodName="ShowNameAction" />
5.
</i:EventTrigger>
6.
</i:Interaction.Triggers>
7.
</Button>
Caliburn.Micro也有简写⽅式,但Blend不⽀持。
使⽤Message.Attach,在Event和Action关键字后使⽤你想要的事件名和⽅法名。
<Button cal:Message.Attach="[Event Click] = [Action ShowName]" />
控制⾏为
Caliburn.Micro有⼀个约定,如果⼀个控件绑定在⼀个属性上,则另⼀个属性能很容易控制它的命令能否被执⾏,只需要这个新属性的名字是那个绑定属性的名字加上Can关键字。
例如:⼀个Button的名字和ViewModel的属性名字叫Execute,则ViewModel⾥叫CanExecute的属性能控制该Button能否被点击。
还有更复杂⼀点的情况,⼀个控件控制另⼀个控件能否被执⾏。
例如:CheckBox控制Button能否被激活。
1.
<StackPanel>
2.
<Button x:Name="ShowName" Content="Click me" />
3.
<CheckBox x:Name="IsEnabled" Content="IsEnabled" />
4.
</StackPanel>
CheckBox绑定到isEnabled,Button绑定到ShowName,CheckBox值改变的同时通知Button的控制属性CanShowName,从⽽实现关联控制。
1.
public class MainPageViewModel: PropertyChangedBase
2.
{
3.
private bool isEnabled;
4.
public bool IsEnabled
5.
{
6.
get { return isEnabled; }
7.
set
8.
{
9.
isEnabled = value;
10.
NotifyOfPropertyChange(() => IsEnabled);
11.
NotifyOfPropertyChange(() => CanShowName);
12.
}
13.
}
14.
public bool CanShowName
15.
{
16.
get { return IsEnabled; }
17.
}
18.
public void ShowName()
19.
{
20.
MessageBox.Show("Clicked");
21.
}
22.
集合(Collections)
View的列表控件和ViewModel的集合属性如果同名也能实现绑定,Selected关键字前缀加上属性名的单数形式就能实现选中控制。
例如:ListBox控件使⽤复数形式的Items名称
1.
<ListBox x:Name="Items">
2.
<ListBox.ItemTemplate>
3.
<DataTemplate>
4.
<StackPanel>
5.
<TextBlock Text="{Binding}" />
6.
</StackPanel>
7.
</DataTemplate>
8.
</ListBox.ItemTemplate>
9.
</ListBox>
ViewModel的属性同名Items实现绑定。
1.
private ObservableCollection<string> items;
2.
public ObservableCollection<string> Items
3.
{
4.
get { return items; }
5.
set
6.
{
7.
items = value;
8.
NotifyOfPropertyChange(() => Items);
9.
}
10.
}
SelectedItem实现选中控制。
(注意:Selected+单数形式Item)
1.
private string selectedItem;
2.
public string SelectedItem
3.
{
4.
get { return selectedItem; }
5.
set
6.
{
7.
selectedItem = value;
8.
NotifyOfPropertyChange(() => SelectedItem);
9.
MessageBox.Show(value);
10.
}
11.
}
Caliburn.Micro有依赖注⼊的功能。
⽤户类要在依赖注⼊时被使⽤到,就要在Bootstrapper的Configure函数向依赖注⼊容器注册,Caliburn.Micro既提供每次创建新实例的模式,也提供单⼀实例模式。
同时Caliburn.Micro会⾃动注册⼀些系统⼯具类。
1.
protected override void Configure()
2.
{
3.
container = new PhoneContainer(RootFrame);
4.
container.RegisterPhoneServices();
5.
//注册,⾮单⼀实例模式
6.
container.PerRequest<MainPageViewModel>();
7.
container.PerRequest<Page2ViewModel>();
8.
//注册单⼀实例模式
9.
container.Singleton<IVisitDataProvider, VisitDataProvider>();
10.
AddCustomConventions();
11.
}
在ViewModel被实例化时,如果其构造函数带有某种类型接⼝为参数,则依赖注⼊容器会提供它们的实例。
例⼦如下。
导航(Navigation)
Windows Phone使⽤NavigationService来完成页⾯间跳转,ViewModel如果要跳转页⾯,应利⽤依赖注⼊得到它的实例。
1.
public class MainPageViewModel : PropertyChangedBase
2.
{
3.
private readonly INavigationService navigationService;
4.
public MainPageViewModel(INavigationService navigationService)
5.
{
6.
this.navigationService = navigationService;
7.
}
8.
public void GoToPage2()
9.
{
10.
navigationService.UriFor<Page2ViewModel>()
11.
.Navigate();
12.
}
13.
}
注⼊导航参数
如果导航时带有参数,Caliburn.Micro会⾃动把参数值注⼊到同名属性。
例如:跳转时带上Name参数
1.
public void GoToPage2()
2.
{
3.
navigationService.UriFor<Page2ViewModel>()
4.
.WithParam(x => , "Matteo")
5.
.Navigate();
}
其导航字符串为/Page2View.xaml?Name=Matteo,则Page2的同名属性Name在实例化时就会被注⼊值。
如果有控件绑定了该属性,则导航到该页⾯时就能显⽰出值。
1.
public class Page2ViewModel: PropertyChangedBase
2.
{
3.
private string name;
4.
public string Name
5.
{
6.
get { return name; }
7.
set
8.
{
9.
name = value;
10.
NotifyOfPropertyChange(() => Name);
11.
}
12.
}
13.
}
墓碑(Tombstoning)
应⽤程序被切换⾄后台时,如果有值需要暂存,Caliburn.Micro提供了很简洁的解决⽅法,这⽐普通XAML程序处理⽣命周期⾥各个事件要容易的多。
我们只需要创建⼀个StorageHandler<T>的继承类,T是你要保存临时值的ViewModel。
例如,在程序切换⾄后台时,需要暂存View⾥名为Name的⽂本框的值,即暂存MainPageViewModel的Name属性。
1.
public class MainPageModelStorage: StorageHandler<MainPageViewModel>
2.
{
3.
public override void Configure()
4.
{
5.
Property(x => )
6.
.InPhoneState();
7.
}
8.
}
InPhoneState()函数把值暂存在内存中,程序退出后就不存在。
相对应的InAppSettings()则会持久保存。
深度链接(Deep Links)
Windows phone⽀持跳过⾸页的实例化,通过URL链接直接打开应⽤程序⾥的某个页⾯,并可携带参数。
Caliburn.Micro的依赖注⼊和属性注⼊机制能保证深度链接的正常打开。
例如:
<StackPanel>
<TextBoxText="{Binding Name}" />
<ButtonContent="Create secondary tile" x:Name="CreateTile" />
</StackPanel>
该ViewModel的按钮事件动态创建⼀个Tile,点击该Tile打开深度链接,这⾥的链接使⽤了⾸页。
1.
public class MainPageViewModel: PropertyChangedBase
2.
{
3.
private string name;
4.
public string Name
5.
{
get { return name; }
7.
set
8.
{
9.
name = value;
10.
NotifyOfPropertyChange(() => Name);
11.
}
12.
}
13.
public void CreateTile()
14.
{
15.
ShellTileData tile = new StandardTileData
16.
{
17.
Title = "Test",
18.
};
19.
ShellTile.Create(new Uri("/Views/MainPage.xaml?Name=Matteo",
20.
UriKind.Relative), tile);
21.
}
22.
}
⽣命周期的事件
ViewModel并⾮WP8页⾯的继承类,为了能在ViewModel⾥响应页⾯基类PhoneApplicationPage⽣命周期的事件,Caliburn.Micro介绍了Screen类。
当⼀个应⽤初次打开,依次会触发下列事件。
1.
public class MainPageViewModel: Screen
2.
{
3.
protected override void OnViewAttached(object view, object context)
4.
{
5.
base.OnViewAttached(view, context);
6.
Debug.WriteLine("OnViewAttached:ViewModel和View建⽴关联时被调⽤");
7.
}
8.
protected override void OnInitialize()
9.
{
10.
base.OnInitialize();
11.
Debug.WriteLine("OnInitialize:初始化结束");
12.
}
13.
protected override void OnViewReady(object view)
14.
{
15.
base.OnViewReady(view);
16.
Debug.WriteLine("OnViewReady:初始化结束,准备绘制");
17.
}
18.
protected override void OnViewLoaded(object view)
19.
{
20.
base.OnViewLoaded(view);
Debug.WriteLine("OnViewLoaded:页⾯和⼦控件全部初始化完成");
22.
}
23.
protected override void OnActivate()
24.
{
25.
base.OnActivate();
26.
Debug.WriteLine("OnActivate:切换成为当前窗⼝");
27.
}
28.
protected override void OnDeactivate(bool close)
29.
{
30.
base.OnDeactivate(close);
31.
Debug.WriteLine("OnDeactivate:切换⾄后台");
32.
}
33.
}
Screen的OnActivate和OnDeactivate事件是最常使⽤的事件,它们分别对应了页⾯OnNavigatedTo和OnNavigatedFrom事件。
值得注意的是,ViewModel加载数据应尽量避免在构造函数和初始化函数中实⾏,⽽应该在OnActivate中。
消息传递(Messaging)
Caliburn.Micro为应⽤程序内已经打开的多个ViewModel之间提供消息传递的功能。
例⼦:从页⾯1打开页⾯2,点击页⾯2的SendMessage Button向页⾯1发送⼀个消息,回退到页⾯1就会看到这个值显⽰在⽂本框⾥。
如下定义的消息类
1.
public class SampleMessage
2.
{
3.
public string Name { get; set; }
4.
public SampleMessage(string name)
5.
{
6.
Name = name;
7.
}
8.
}
消息接收者需要实现接⼝IHandle<T>,本例T就是SampleMessage,并需要接受消息接⼝IEventAggregator的注⼊。
注意:消息接收者需要调⽤IEventAggregator的Subscribe函数订阅消息。
1.
public class MainPageViewModel: Screen, IHandle<SampleMessage>
2.
{
3.
private readonly IEventAggregator eventAggregator;
4.
private readonly INavigationService navigationService;
5.
6.
private string name;
7.
public string Name
8.
{
9.
get { return name; }
10.
set
11.
{
12.
13.
NotifyOfPropertyChange(() => Name);
14.
}
15.
}
16.
17.
public MainPageViewModel(IEventAggregator eventAggregator, INavigationService
18.
navigationService)
19.
{
20.
this.eventAggregator = eventAggregator;
21.
this.navigationService = navigationService;
22.
eventAggregator.Subscribe(this);
23.
}
24.
25.
public void GoToPage2()
26.
{
27.
navigationService.UriFor<SecondPageViewModel>().Navigate();
28.
}
29.
30.
public void Handle(SampleMessage message)
31.
{
32.
Name = ;
33.
}
34.
}
消息发送者只需要接受IEventAggregator的注⼊,并⽤它的Publish⽅法发送消息。
1.
public class SecondPageViewModel: Screen
2.
{
3.
private readonly IEventAggregator eventAggregator;
4.
public SecondPageViewModel(IEventAggregator eventAggregator)
5.
{
6.
this.eventAggregator = eventAggregator;
7.
}
8.
public void SendMessage()
9.
{
10.
eventAggregator.Publish(new SampleMessage("Matteo"));
11.
}
12.
}
View和ViewModel之间的通讯
View类只要得到IEventAggregator的实例也能在Views和ViewModels之间接收或发送消息。
此时我们需要改造Bootstrapper类,要得到其container变量,就可以调⽤其GetAllInstances或GetInstance得到IEventAggregator。
例如:
public class Bootstrapper : PhoneBootstrapper
2.
{
3.
// 把原来的私有变量container改造成公共属性
4.
public PhoneContainer container { get; set; }
5.
//其他⽅法不变
6.
//……
7.
}
MainPage的View类通过bootstrapper中Container的GetAllInstances⽅法得到IEventAggregator实例,并可以订阅或发送消息。
1.
public partial class MainPage : PhoneApplicationPage, IHandle<SampleMessage>
2.
{
3.
private IEventAggregator eventAggregator;
4.
// Constructor
5.
public MainPage()
6.
{
7.
InitializeComponent();
8.
Bootstrapper bootstrapper = Application.Current.Resources["bootstrapper"]
9.
as Bootstrapper;
10.
IEventAggregator eventAggregator =
11.
bootstrapper.container.GetAllInstances(typeof
12.
(IEventAggregator)).FirstOrDefault() as IEventAggregator;
13.
this.eventAggregator = eventAggregator;
14.
eventAggregator.Subscribe(this);
15.
}
16.
public void Handle(SampleMessage message)
17.
{
18.
MessageBox.Show();
19.
}
20.
}
Launcher与Chooser
Caliburn.Micro借⽤IEventAggregator消息类还提供了调⽤Launcher和Chooser的功能。
例⼦:调⽤Launcher打开地图程序。
1.
public class MainPageViewModel: Screen
2.
{
3.
public MainPageViewModel(IEventAggregator eventAggregator)
4.
{
}
8.
public void LaunchMap()
9.
{
10.
eventAggregator.RequestTask<MapsTask>(task =>
11.
{
12.
task.SearchTerm = "Milan";
13.
});
14.
}
15.
}
Chooser由于需要接收返回值,需要实现IHandle<TaskCompleted<T>>接⼝。
1.
public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>>
2.
{
3.
private readonly IEventAggregator eventAggregator;
4.
public MainPageViewModel(IEventAggregator eventAggregator)
5.
{
6.
this.eventAggregator = eventAggregator;
7.
}
8.
protected override void OnActivate()
9.
{
10.
eventAggregator.Subscribe(this);
11.
base.OnActivate();
12.
}
13.
protected override void OnDeactivate(bool close)
14.
{
15.
eventAggregator.Unsubscribe(this);
16.
base.OnDeactivate(close);
17.
}
18.
public void OpenContact()
19.
{
20.
eventAggregator.RequestTask<PhoneNumberChooserTask>();
21.
}
22.
public void Handle(TaskCompleted<PhoneNumberResult> message)
23.
{
24.
MessageBox.Show(message.Result.DisplayName);
25.
}
26.
}
⽤户⾃定义的服务类,可以通过依赖注⼊提供给使⽤⽅,前提是必须在Bootstrapper的Configure函数中注册。
这样的服务类⼀般使⽤接⼝编程。
例⼦,先定义数据和服务类接⼝。
1.
public interface IFeedService
2.
{
3.
Task<List<FeedItem>> GetNews(string url);
4.
}
5.
public class FeedItem
6.
{
7.
public string Title { get; set; }
8.
public string Description { get; set; }
9.
public Uri Url { get; set; }
10.
}
实现类
1.
public class FeedService: IFeedService
2.
{
3.
public async Task<List<FeedItem>> GetNews(string url)
4.
{
5.
WebClient client = new WebClient();
6.
string content = await client.DownloadStringTaskAsync(url);
7.
XDocument doc = XDocument.Parse(content);
8.
var result =
9.
doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new
10.
FeedItem
11.
{
12.
{
13.
Title = x.Element("title").Value,
14.
Description = x.Element("description").Value
15.
}).ToList();
16.
return result;
17.
}
18.
}
在bootstrapper的Configure注册该类
1.
protected override void Configure()
2.
{
3.
container = new PhoneContainer(RootFrame);
4.
AddCustomConventions();
8.
}
然后使⽤类的构造函数就能使⽤依赖注⼊得到它的实例。
1.
public class MainPageViewModel: Screen
2.
{
3.
private readonly IFeedService feedService;
4.
private List<FeedItem> news;
5.
public List<FeedItem> News
6.
{
7.
get { return news; }
8.
set
9.
{
10.
news = value;
11.
NotifyOfPropertyChange(() => News);
12.
}
13.
}
14.
public MainPageViewModel(IFeedService feedService)
15.
{
16.
this.feedService = feedService;
17.
}
18.
public async void LoadWebsite()
19.
{
20.
News = await
21.
feedService.GetNews("/qmatteoq_eng");
22.
}
23.
}
⽤户类的单⼀实例
Caliburn.Micro也⽀持把⽤户服务类注册为单⼀实例,每个使⽤者得到的都是同⼀个实例。
利⽤这个特性,多个ViewModel可以共享数据。
1.
protected override void Configure()
2.
{
3.
container = new PhoneContainer(RootFrame);
4.
container.RegisterPhoneServices();
5.
container.PerRequest<MainPageViewModel>();
6.
container.PerRequest<DetailPageViewModel>();
7.
container.PerRequest<IFeedService, FeedService>();
8.
应⽤程序⼯具条(Application Bar)
由于系统默认Applicationbar不是页⾯控件,不⽀持绑定。
Caliburn.Micro提供了代替类Caliburn.Micro.BindableAppBar(可以通过NuGet获得)。
View中引⽤。
xmlns:bab=”clrnamespace:Caliburn.Micro.BindableAppBar;assembly=Caliburn.Micro.BindableAppBar”
并在页⾯中添加该控件。
1.
<Grid x:Name="LayoutRoot" Background="Transparent">
2.
<Grid.RowDefinitions>
3.
<RowDefinition Height="Auto"/>
4.
<RowDefinition Height="*"/>
5.
</Grid.RowDefinitions>
6.
7.
<bab:BindableAppBar x:Name="AppBar">
8.
<bab:BindableAppBarButton x:Name="AddItem"
9.
Text="{Binding AddItemText}"
10.
IconUri="{Binding Icon}"
11.
Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
12.
/>
13.
<bab:BindableAppBarMenuItem x:Name="RemoveItem"
14.
Text="Remove"
15.
/>
16.
</bab:BindableAppBar>
17.
</Grid>
⾃定义命名约定
为了让按钮在Click事件发⽣时不可⽤,我们在bootstrapper中增加⾃定义命名约定。
1.
static void AddCustomConventions()
2.
{
3.
ConventionManager.AddElementConvention<BindableAppBarButton>(
4.
Control.IsEnabledProperty, "DataContext", "Click");
5.
ConventionManager.AddElementConvention<BindableAppBarMenuItem>(
6.
Control.IsEnabledProperty, "DataContext", "Click");
7.
}
该约定把IsEnabled属性绑定给Click事件。
下⾯ViewModel能给⾃定义bar提供按钮⽂本、图标、可见状态和响应事件。
1.
public class MainPageViewModel: Screen
2.
public string AddItemText
5.
{
6.
get { return addItemText; }
7.
set
8.
{
9.
{
10.
addItemText = value;
11.
NotifyOfPropertyChange(() => AddItemText);
12.
}
13.
}
14.
private Uri icon;
15.
public Uri Icon
16.
{
17.
get { return icon; }
18.
set
19.
{
20.
icon = value;
21.
NotifyOfPropertyChange(() => Icon);
22.
}
23.
}
24.
private bool isVisible;
25.
public bool IsVisible
26.
{
27.
get { return isVisible; }
28.
set
29.
{
30.
isVisible = value;
31.
NotifyOfPropertyChange(() => IsVisible);
32.
}
33.
}
34.
public MainPageViewModel()
35.
{
36.
AddItemText = "Add";
37.
Icon = new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative);
38.
IsVisible = false;
39.
}
40.
public void AddItem()
41.
{
public void RemoveItem()
45.
{
46.
MessageBox.Show("Item removed");
47.
}
48.
}
Pivot和Panorama
Pivot和Panorama作为Windows phone的特⾊控件⼴泛的到使⽤,由于Panorama的选中事件有Bug,导致⼦页⾯的⽣命周期的事件不完整,所以重点介绍Pivot。
Caliburn.Micro为Pivot提供了⼯具类,能把各个功能区分离成⼦页⾯,每个⼦页⾯都有⾃⼰的⽣命周期。
为了能让Pivot集成多个页⾯作为⼦页⾯,⾸先需要在bootstrapper中注册这些ViewModel。
Conductor类
Conductor类作为多页⾯的管理者,Pivot主页⾯的ViewModel需要继承它。
例如:Pivot主页⾯继承Conductor类,便拥有了Items属性,利⽤依赖注⼊得到⼦页⾯的实例,并把⼦页⾯添加到Items集成属性中,以便Conductor管理它们。
1.
public class PivotViewModel: Conductor<IScreen>.Collection.OneActive
2.
{
3.
private readonly PivotItem1ViewModel item1;
4.
private readonly PivotItem2ViewModel item2;
5.
public PivotViewModel(PivotItem1ViewModel item1, PivotItem2ViewModel item2)
6.
{
7.
this.item1 = item1;
8.
this.item2 = item2;
9.
}
10.
protected override void OnInitialize()
11.
{
12.
base.OnInitialize();
13.
Items.Add(item1);
14.
Items.Add(item2);
15.
ActivateItem(item1);
16.
}
17.
}
⽽View类中的Pivot控件仅需要指定其名称为Items即可。
1.
<Grid x:Name="LayoutRoot" Background="Transparent">
2.
<!--Pivot Control-->
3.
<phone:Pivot Title="MY APPLICATION" x:Name="Items" SelectedItem="{Binding
4.
ActiveItem, Mode=TwoWay}">
5.
<phone:Pivot.HeaderTemplate>
6.
<DataTemplate>
</phone:Pivot.HeaderTemplate>
10.
</phone:Pivot>
11.
</Grid>
⼦页⾯需要继承Screen类,其DisplayName属性作为Title显⽰在Pivot上。
1.
public class PivotItem1ViewModel: Screen
2.
{
3.
public PivotItem1ViewModel()
4.
{
5.
DisplayName = "First pivot";
6.
}
7.
}
延迟加载(Lazy Loading)
在构造函数或初始化函数中加载数据会带来很糟糕的⽤户体验。
Pivot的⼦页⾯也具有完整的⽣命周期事件,可以在ViewModel的OnActivate()事件中加载数据。
例⼦,Pivot⼦页⾯1负责读取RSS,我们先定义数据和数据操作类,
1.
public class FeedItem
2.
{
3.
public string Title { get; set; }
4.
public string Description { get; set; }
5.
}
6.
public static class RssParser
7.
{
8.
public static IEnumerable<FeedItem> ParseXml(string content)
9.
{
10.
XDocument doc = XDocument.Parse(content);
11.
var result =
12.
doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new
13.
FeedItem
14.
{
15.
Title = x.Element("title").Value,
16.
Description = x.Element("description").Value
17.
});
18.
return result;
19.
}
20.
}
<ListBox.ItemTemplate>
3.
<DataTemplate>
4.
<StackPanel>
5.
<TextBlock Text="{Binding Path=Title}" />
6.
</StackPanel>
7.
</DataTemplate>
8.
</ListBox.ItemTemplate>
9.
</ListBox>
ViewModel类的OnActivate()事件⾥使⽤异步⽅法下载并解析数据,然后更新绑定源。
1.
public class PivotItem1ViewModel: Screen
2.
{
3.
public PivotItem1ViewModel()
4.
{
5.
DisplayName = "Pivot 1";
6.
}
7.
protected override async void OnActivate()
8.
{
9.
base.OnActivate();
10.
WebClient client = new WebClient();
11.
string result = await
12.
client.DownloadStringTaskAsync("/qmatteoq_eng");
13.
IEnumerable<FeedItem> feedItems = RssParser.ParseXml(result);
14.
FeedItems = feedItems.ToList();
15.
}
16.
private List<FeedItem> _feedItems;
17.
public List<FeedItem> FeedItems
18.
{
19.
get { return _feedItems; }
20.
set
21.
{
22.
_feedItems = value;
23.
NotifyOfPropertyChange(() => FeedItems);
24.
}
25.
}
26.
}。