Caliburn.Micro开发框架介绍 (Windows phone

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。

当然,你也可以自定义各种约定。

准备工作

下载

Caliburn.Micro可以通过Visualstudio的NuGet工具获得,也可以在其官网下载发布包、源代码和例子。http://caliburnmicro.codeplex.com/

入口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,其内容如下。

    public class AppBootstrapper : PhoneBootstrapper
    {
        PhoneContainer container;

        protected override void Configure()
        {
            container = new PhoneContainer(RootFrame);

            container.RegisterPhoneServices();
			//注册所有ViewModel
            container.PerRequest<MainPageViewModel>();

            AddCustomConventions();
        }

        static void AddCustomConventions()
        {
            //ellided  自定义命名约定
        }

        protected override object GetInstance(Type service, string key)
        {
            return container.GetInstance(service, key);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return container.GetAllInstances(service);
        }

        protected override void BuildUp(object instance)
        {
            container.BuildUp(instance);
        }
    }

 

初始化bootstrapper

         修改App.xml,初始化bootstrapper。

<Application
    x:Class="CSH.IntelliHIS.WP8.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:local="clr-namespace:CSH.IntelliHIS.WP8">

    <!--Application Resources-->
    <Application.Resources>
        <local:AppBootstrapper x:Key="bootstrapper" />
    </Application.Resources>

</Application>

 

         修改App.xml.cs,默认的初始化代码已经不需要了。

    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
        }
    }

 

命名约定(naming convention)

         命名约定让Caliburn.Micro能自动关联起View和View Model,如果我们运行工程,浏览MainPage页面,则MainPageViewModel自动被实例化。

接下来就可以看看Caliburn.Micro是如何扮演值转换器的角色,看它是如何在View和ViewModel之间传递和转换值,以便View绑定这些值。

我们在ViewModel里增加一个叫Name的属性(Property)。

public class MainPageViewModel: PropertyChangedBase
{
	public MainPageViewModel()
	{
		Name = "Matteo";
	}
	private string name;
	public string Name
	{
		get { return name; }
		set
		{
			name = value;
			NotifyOfPropertyChange(() => Name);
		}
	}
}

 

         在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里这样定义

<Button x:Name="ShowName">
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click">
			<cal:ActionMessage MethodName="ShowNameAction" />
		</i:EventTrigger>
	</i:Interaction.Triggers>
</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能否被激活。

<StackPanel>
	<Button x:Name="ShowName" Content="Click me" />
	<CheckBox x:Name="IsEnabled" Content="IsEnabled" />
</StackPanel>

 

CheckBox绑定到isEnabled,Button绑定到ShowName,CheckBox值改变的同时通知Button的控制属性CanShowName,从而实现关联控制。

public class MainPageViewModel: PropertyChangedBase
{
	private bool isEnabled;
	public bool IsEnabled
	{
		get { return isEnabled; }
		set
		{
			isEnabled = value;
			NotifyOfPropertyChange(() => IsEnabled);
			NotifyOfPropertyChange(() => CanShowName);
		}
	}
	public bool CanShowName
	{
		get { return IsEnabled; }
	}
	public void ShowName()
	{
		MessageBox.Show("Clicked");
	}
}

 

集合(Collections)

         View的列表控件和ViewModel的集合属性如果同名也能实现绑定,Selected关键字前缀加上属性名的单数形式就能实现选中控制。

         例如:ListBox控件使用复数形式的Items名称

<ListBox x:Name="Items">
	<ListBox.ItemTemplate>
		<DataTemplate>
			<StackPanel>
				<TextBlock Text="{Binding}" />
			</StackPanel>
		</DataTemplate>
	</ListBox.ItemTemplate>
</ListBox>

 

ViewModel的属性同名Items实现绑定。

private ObservableCollection<string> items;
public ObservableCollection<string> Items
{
	get { return items; }
	set
	{
		items = value;
		NotifyOfPropertyChange(() => Items);
	}
}

 

         SelectedItem实现选中控制。(注意:Selected+单数形式Item)

private string selectedItem;
public string SelectedItem
{
	get { return selectedItem; }
	set
	{
		selectedItem = value;
		NotifyOfPropertyChange(() => SelectedItem);
		MessageBox.Show(value);
	}
}

 

依赖注入(Dependency Injection)

         Caliburn.Micro有依赖注入的功能。用户类要在依赖注入时被使用到,就要在Bootstrapper的Configure函数向依赖注入容器注册,Caliburn.Micro既提供每次创建新实例的模式,也提供单一实例模式。同时Caliburn.Micro会自动注册一些系统工具类。

protected override void Configure()
{
	container = new PhoneContainer(RootFrame);
	container.RegisterPhoneServices();
	//注册,非单一实例模式
	container.PerRequest<MainPageViewModel>();
	container.PerRequest<Page2ViewModel>();
	//注册单一实例模式
	container.Singleton<IVisitDataProvider, VisitDataProvider>();
	AddCustomConventions();
}

 

在ViewModel被实例化时,如果其构造函数带有某种类型接口为参数,则依赖注入容器会提供它们的实例。例子如下。

导航(Navigation)

         Windows Phone使用NavigationService来完成页面间跳转,ViewModel如果要跳转页面,应利用依赖注入得到它的实例。

public class MainPageViewModel : PropertyChangedBase
{
	private readonly INavigationService navigationService;
	public MainPageViewModel(INavigationService navigationService)
	{
		this.navigationService = navigationService;
	}
	public void GoToPage2()
	{
		navigationService.UriFor<Page2ViewModel>()
			.Navigate();
	}
}

 

注入导航参数

         如果导航时带有参数,Caliburn.Micro会自动把参数值注入到同名属性。

例如:跳转时带上Name参数

public void GoToPage2()
{
	navigationService.UriFor<Page2ViewModel>()
	.WithParam(x => x.Name, "Matteo")
	.Navigate();
}

其导航字符串为/Page2View.xaml?Name=Matteo,则Page2的同名属性Name在实例化时就会被注入值。如果有控件绑定了该属性,则导航到该页面时就能显示出值。

public class Page2ViewModel: PropertyChangedBase
{
	private string name;
	public string Name
	{
		get { return name; }
		set
		{
			name = value;
			NotifyOfPropertyChange(() => Name);
		}
	}
}

墓碑(Tombstoning)

         应用程序被切换至后台时,如果有值需要暂存,Caliburn.Micro提供了很简洁的解决方法,这比普通XAML程序处理生命周期里各个事件要容易的多。我们只需要创建一个StorageHandler<T>的继承类,T是你要保存临时值的ViewModel。

         例如,在程序切换至后台时,需要暂存View里名为Name的文本框的值,即暂存MainPageViewModel的Name属性。

public class MainPageModelStorage: StorageHandler<MainPageViewModel>
{
	public override void Configure()
	{
		Property(x => x.Name)
		.InPhoneState();
	}
}

         InPhoneState()函数把值暂存在内存中,程序退出后就不存在。相对应的InAppSettings()则会持久保存。

深度链接(Deep Links)

         Windows phone支持跳过首页的实例化,通过URL链接直接打开应用程序里的某个页面,并可携带参数。Caliburn.Micro的依赖注入和属性注入机制能保证深度链接的正常打开。

         例如:

<StackPanel>

         <TextBoxText="{Binding Name}" />

         <ButtonContent="Create secondary tile" x:Name="CreateTile" />

</StackPanel>

         该ViewModel的按钮事件动态创建一个Tile,点击该Tile打开深度链接,这里的链接使用了首页。

public class MainPageViewModel: PropertyChangedBase
{
	private string name;
	public string Name
	{
		get { return name; }
		set
		{
			name = value;
			NotifyOfPropertyChange(() => Name);
		}
	}
	public void CreateTile()
	{
		ShellTileData tile = new StandardTileData
		{
			Title = "Test",
		};
		ShellTile.Create(new Uri("/Views/MainPage.xaml?Name=Matteo",
			UriKind.Relative), tile);
	}
}

生命周期的事件

         ViewModel并非WP8页面的继承类,为了能在ViewModel里响应页面基类PhoneApplicationPage生命周期的事件,Caliburn.Micro介绍了Screen类。当一个应用初次打开,依次会触发下列事件。

public class MainPageViewModel: Screen
{
	protected override void OnViewAttached(object view, object context)
	{
		base.OnViewAttached(view, context);
		Debug.WriteLine("OnViewAttached:ViewModel和View建立关联时被调用");
	}
	protected override void OnInitialize()
	{
		base.OnInitialize();
		Debug.WriteLine("OnInitialize:初始化结束");
	}
	protected override void OnViewReady(object view)
	{
		base.OnViewReady(view);
		Debug.WriteLine("OnViewReady:初始化结束,准备绘制");
	}
	protected override void OnViewLoaded(object view)
	{
		base.OnViewLoaded(view);
		Debug.WriteLine("OnViewLoaded:页面和子控件全部初始化完成");
	}
	protected override void OnActivate()
	{
		base.OnActivate();
		Debug.WriteLine("OnActivate:切换成为当前窗口");
	}
	protected override void OnDeactivate(bool close)
	{
		base.OnDeactivate(close);
		Debug.WriteLine("OnDeactivate:切换至后台");
	}
}

         Screen的OnActivate和OnDeactivate事件是最常使用的事件,它们分别对应了页面OnNavigatedTo和OnNavigatedFrom事件。值得注意的是,ViewModel加载数据应尽量避免在构造函数和初始化函数中实行,而应该在OnActivate中。

消息传递(Messaging)

         Caliburn.Micro为应用程序内已经打开的多个ViewModel之间提供消息传递的功能。

例子:从页面1打开页面2,点击页面2的SendMessage Button向页面1发送一个消息,回退到页面1就会看到这个值显示在文本框里。

如下定义的消息类

public class SampleMessage
{
	public string Name { get; set; }
	public SampleMessage(string name)
	{
		Name = name;
	}
}

消息接收者需要实现接口IHandle<T>,本例T就是SampleMessage,并需要接受消息接口IEventAggregator的注入。

注意:消息接收者需要调用IEventAggregator的Subscribe函数订阅消息。

public class MainPageViewModel: Screen, IHandle<SampleMessage>
{
	private readonly IEventAggregator eventAggregator;
	private readonly INavigationService navigationService;
	
	private string name;
	public string Name
	{
		get { return name; }
		set
		{
			name = value;
			NotifyOfPropertyChange(() => Name);
		}
	}
	
	public MainPageViewModel(IEventAggregator eventAggregator, INavigationService
		navigationService)
	{
		this.eventAggregator = eventAggregator;
		this.navigationService = navigationService;
		eventAggregator.Subscribe(this);
	}
	
	public void GoToPage2()
	{
		navigationService.UriFor<SecondPageViewModel>().Navigate();
	}
	
	public void Handle(SampleMessage message)
	{
		Name = message.Name;
	}
}

         消息发送者只需要接受IEventAggregator的注入,并用它的Publish方法发送消息。

public class SecondPageViewModel: Screen
{
	private readonly IEventAggregator eventAggregator;
	public SecondPageViewModel(IEventAggregator eventAggregator)
	{
		this.eventAggregator = eventAggregator;
	}
	public void SendMessage()
	{
		eventAggregator.Publish(new SampleMessage("Matteo"));
	}
}

 

View和ViewModel之间的通讯

         View类只要得到IEventAggregator的实例也能在Views和ViewModels之间接收或发送消息。

         此时我们需要改造Bootstrapper类,要得到其container变量,就可以调用其GetAllInstances或GetInstance得到IEventAggregator。

         例如:

public class Bootstrapper : PhoneBootstrapper
{
	// 把原来的私有变量container改造成公共属性
	public PhoneContainer container { get; set; }
	//其他方法不变
	//……
}


 

         MainPage的View类通过bootstrapper中Container的GetAllInstances方法得到IEventAggregator实例,并可以订阅或发送消息。

public partial class MainPage : PhoneApplicationPage, IHandle<SampleMessage>
{
	private IEventAggregator eventAggregator;
	// Constructor
	public MainPage()
	{
		InitializeComponent();
		Bootstrapper bootstrapper = Application.Current.Resources["bootstrapper"]
				as Bootstrapper;
		IEventAggregator eventAggregator =
				bootstrapper.container.GetAllInstances(typeof
				(IEventAggregator)).FirstOrDefault() as IEventAggregator;
		this.eventAggregator = eventAggregator;
		eventAggregator.Subscribe(this);
	}
	public void Handle(SampleMessage message)
	{
		MessageBox.Show(message.Name);
	}
}

 

Launcher与Chooser

         Caliburn.Micro借用IEventAggregator消息类还提供了调用Launcher和Chooser的功能。

         例子:调用Launcher打开地图程序。

public class MainPageViewModel: Screen
{
	public MainPageViewModel(IEventAggregator eventAggregator)
	{
		this.eventAggregator = eventAggregator;
		eventAggregator.Subscribe(this);
	}
	public void LaunchMap()
	{
		eventAggregator.RequestTask<MapsTask>(task =>
				{
					task.SearchTerm = "Milan";
				});
	}
}

 

         Chooser由于需要接收返回值,需要实现IHandle<TaskCompleted<T>>接口。

public class MainPageViewModel: Screen, IHandle<TaskCompleted<PhoneNumberResult>>
{
	private readonly IEventAggregator eventAggregator;
	public MainPageViewModel(IEventAggregator eventAggregator)
	{
		this.eventAggregator = eventAggregator;
	}
	protected override void OnActivate()
	{
		eventAggregator.Subscribe(this);
		base.OnActivate();
	}
	protected override void OnDeactivate(bool close)
	{
		eventAggregator.Unsubscribe(this);
		base.OnDeactivate(close);
	}
	public void OpenContact()
	{
		eventAggregator.RequestTask<PhoneNumberChooserTask>();
	}
	public void Handle(TaskCompleted<PhoneNumberResult> message)
	{
		MessageBox.Show(message.Result.DisplayName);
	}
}

 

         注意:由于移动应用生命周期的特殊性,ViewModel应该在OnActivate和OnDeactivate事件里订阅消息和取消订阅。

用户服务

         用户自定义的服务类,可以通过依赖注入提供给使用方,前提是必须在Bootstrapper的Configure函数中注册。这样的服务类一般使用接口编程。

例子,  先定义数据和服务类接口。

public interface IFeedService
{
	Task<List<FeedItem>> GetNews(string url);
}
public class FeedItem
{
	public string Title { get; set; }
	public string Description { get; set; }
	public Uri Url { get; set; }
}

 

         实现类

public class FeedService: IFeedService
{
	public async Task<List<FeedItem>> GetNews(string url)
	{
		WebClient client = new WebClient();
		string content = await client.DownloadStringTaskAsync(url);
		XDocument doc = XDocument.Parse(content);
		var result =
			doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new
					FeedItem
					{
						{
						Title = x.Element("title").Value,
						Description = x.Element("description").Value
					}).ToList();
		return result;
	}
}

 

         在bootstrapper的Configure注册该类

protected override void Configure()
{
	container = new PhoneContainer(RootFrame);
	container.RegisterPhoneServices();
	container.PerRequest<MainPageViewModel>();
	container.PerRequest<IFeedService, FeedService>();
	AddCustomConventions();
}

 

         然后使用类的构造函数就能使用依赖注入得到它的实例。

public class MainPageViewModel: Screen
{
	private readonly IFeedService feedService;
	private List<FeedItem> news;
	public List<FeedItem> News
	{
		get { return news; }
		set
		{
			news = value;
			NotifyOfPropertyChange(() => News);
		}
	}
	public MainPageViewModel(IFeedService feedService)
	{
		this.feedService = feedService;
	}
	public async void LoadWebsite()
	{
		News = await
			feedService.GetNews("http://feeds.feedburner.com/qmatteoq_eng");
	}
}

 

用户类的单一实例

Caliburn.Micro也支持把用户服务类注册为单一实例,每个使用者得到的都是同一个实例。利用这个特性,多个ViewModel可以共享数据。

protected override void Configure()
{
	container = new PhoneContainer(RootFrame);
	container.RegisterPhoneServices();
	container.PerRequest<MainPageViewModel>();
	container.PerRequest<DetailPageViewModel>();
	container.PerRequest<IFeedService, FeedService>();
	container.Singleton<DataService>();
	AddCustomConventions();
}

 

应用程序工具条(Application Bar)

         由于系统默认Applicationbar不是页面控件,不支持绑定。Caliburn.Micro提供了代替类Caliburn.Micro.BindableAppBar(可以通过NuGet获得)。

         View中引用。

xmlns:bab=”clrnamespace:Caliburn.Micro.BindableAppBar;assembly=Caliburn.Micro.BindableAppBar”

并在页面中添加该控件。

<Grid x:Name="LayoutRoot" Background="Transparent">
	<Grid.RowDefinitions>
	<RowDefinition Height="Auto"/>
	<RowDefinition Height="*"/>
	</Grid.RowDefinitions>

	<bab:BindableAppBar x:Name="AppBar">
		<bab:BindableAppBarButton x:Name="AddItem"
			Text="{Binding AddItemText}"
			IconUri="{Binding Icon}"
			Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
		/>
		<bab:BindableAppBarMenuItem x:Name="RemoveItem"
			Text="Remove"
			/>
	</bab:BindableAppBar>
</Grid>

 

自定义命名约定

         为了让按钮在Click事件发生时不可用,我们在bootstrapper中增加自定义命名约定。

static void AddCustomConventions()
{
	ConventionManager.AddElementConvention<BindableAppBarButton>(
		Control.IsEnabledProperty, "DataContext", "Click");
	ConventionManager.AddElementConvention<BindableAppBarMenuItem>(
		Control.IsEnabledProperty, "DataContext", "Click");
}

 

         该约定把IsEnabled属性绑定给Click事件。

         下面ViewModel能给自定义bar提供按钮文本、图标、可见状态和响应事件。

public class MainPageViewModel: Screen
{
	private string addItemText;
	public string AddItemText
	{
		get { return addItemText; }
		set
		{
		{
			addItemText = value;
			NotifyOfPropertyChange(() => AddItemText);
		}
	}
	private Uri icon;
	public Uri Icon
	{
		get { return icon; }
		set
		{
			icon = value;
			NotifyOfPropertyChange(() => Icon);
		}
	}
	private bool isVisible;
	public bool IsVisible
	{
		get { return isVisible; }
		set
		{
			isVisible = value;
			NotifyOfPropertyChange(() => IsVisible);
		}
	}
	public MainPageViewModel()
	{
		AddItemText = "Add";
		Icon = new Uri("/Assets/AppBar/appbar.add.rest.png", UriKind.Relative);
		IsVisible = false;
	}
	public void AddItem()
	{
		MessageBox.Show("Item added");
	}
	public void RemoveItem()
	{
		MessageBox.Show("Item removed");
	}
}

 

Pivot和Panorama

         Pivot和Panorama作为Windows phone的特色控件广泛的到使用,由于Panorama的选中事件有Bug,导致子页面的生命周期的事件不完整,所以重点介绍Pivot。

         Caliburn.Micro为Pivot提供了工具类,能把各个功能区分离成子页面,每个子页面都有自己的生命周期。

         为了能让Pivot集成多个页面作为子页面,首先需要在bootstrapper中注册这些ViewModel。

Conductor类

         Conductor类作为多页面的管理者,Pivot主页面的ViewModel需要继承它。

         例如:Pivot主页面继承Conductor类,便拥有了Items属性,利用依赖注入得到子页面的实例,并把子页面添加到Items集成属性中,以便Conductor管理它们。

public class PivotViewModel: Conductor<IScreen>.Collection.OneActive
{
	private readonly PivotItem1ViewModel item1;
	private readonly PivotItem2ViewModel item2;
	public PivotViewModel(PivotItem1ViewModel item1, PivotItem2ViewModel item2)
	{
		this.item1 = item1;
		this.item2 = item2;
	}
	protected override void OnInitialize()
	{
		base.OnInitialize();
		Items.Add(item1);
		Items.Add(item2);
		ActivateItem(item1);
	}
}

 

         而View类中的Pivot控件仅需要指定其名称为Items即可。

<Grid x:Name="LayoutRoot" Background="Transparent">
	<!--Pivot Control-->
	<phone:Pivot Title="MY APPLICATION" x:Name="Items" SelectedItem="{Binding
			ActiveItem, Mode=TwoWay}">
		<phone:Pivot.HeaderTemplate>
			<DataTemplate>
				<TextBlock Text="{Binding DisplayName}" />
			</DataTemplate>
		</phone:Pivot.HeaderTemplate>
	</phone:Pivot>
</Grid>

 

         子页面需要继承Screen类,其DisplayName属性作为Title显示在Pivot上。

public class PivotItem1ViewModel: Screen
{
	public PivotItem1ViewModel()
	{
		DisplayName = "First pivot";
	}
}

 

延迟加载(Lazy Loading)

         在构造函数或初始化函数中加载数据会带来很糟糕的用户体验。Pivot的子页面也具有完整的生命周期事件,可以在ViewModel的OnActivate()事件中加载数据。

         例子,Pivot子页面1负责读取RSS,我们先定义数据和数据操作类,

public class FeedItem
{
	public string Title { get; set; }
	public string Description { get; set; }
}
public static class RssParser
{
	public static IEnumerable<FeedItem> ParseXml(string content)
	{
		XDocument doc = XDocument.Parse(content);
		var result =
			doc.Descendants("rss").Descendants("channel").Elements("item").Select(x => new
				FeedItem
				{
					Title = x.Element("title").Value,
					Description = x.Element("description").Value
				});
		return result;
	}
}

 

         再定义View中的绑定行为。

<ListBox x:Name="FeedItems">
	<ListBox.ItemTemplate>
		<DataTemplate>
			<StackPanel>
				<TextBlock Text="{Binding Path=Title}" />
			</StackPanel>
		</DataTemplate>
	</ListBox.ItemTemplate>
</ListBox>

 

         ViewModel类的OnActivate()事件里使用异步方法下载并解析数据,然后更新绑定源。

public class PivotItem1ViewModel: Screen
{
	public PivotItem1ViewModel()
	{
		DisplayName = "Pivot 1";
	}
	protected override async void OnActivate()
	{
		base.OnActivate();
		WebClient client = new WebClient();
		string result = await
			client.DownloadStringTaskAsync("http://feeds.feedburner.com/qmatteoq_eng");
		IEnumerable<FeedItem> feedItems = RssParser.ParseXml(result);
		FeedItems = feedItems.ToList();
	}
	private List<FeedItem> _feedItems;
	public List<FeedItem> FeedItems
	{
		get { return _feedItems; }
		set
		{
			_feedItems = value;
			NotifyOfPropertyChange(() => FeedItems);
		}
	}
}

 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WPF(Windows Presentation Foundation)是一种用于创建Windows桌面应用程序的用户界面框架,而Caliburn.Micro是一种针对WPF应用程序的轻量级MVVM框架。通过结合使用这两个框架,可以更高效地编写WPF应用程序的界面和逻辑部分。 一个基本的WPF Caliburn.Micro框架编写案例可以是一个简单的待办事项列表应用程序。首先,我们可以创建一个WPF应用程序,并添加Caliburn.Micro框架的引用。然后,我们可以定义待办事项的模型,包括待办事项的名称、描述和状态等属性。 接下来,我们可以使用Caliburn.Micro框架提供的ViewModelBase类来创建一个待办事项的视图模型。在视图模型中,我们可以定义与界面交互的属性和命令,比如显示待办事项列表、添加新的待办事项、标记已完成的待办事项等功能。 然后,我们需要创建一个WPF的视图,用于显示待办事项列表和与用户进行交互。在视图中,我们可以绑定视图模型中的属性和命令,并使用WPF的控件来显示和编辑待办事项。 最后,我们将视图模型绑定到视图上,使得视图与视图模型能够实现双向的数据绑定和命令绑定。这样,当用户在界面上进行操作时,视图和视图模型之间的数据和行为可以实现同步。 通过这样一个简单的案例,我们可以看到WPF Caliburn.Micro框架编写的优势,它能够帮助我们更快速和高效地构建WPF应用程序,并实现良好的界面和逻辑分离。同时,借助MVVM架构,我们也能够实现更好的代码可读性和可维护性。 WPF Caliburn.Micro框架编写案例的实践将有助于我们更好地理解和运用这些框架,提升WPF应用程序的开发效率和质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值