MVVM模式,即Model - View - ViewModel模式,这是Microsoft推荐WPF开发者使用的一种模式。
MVVM模式的目的是为了使View(即xaml)只处理UI效果,与数据相关的操作都由ViewModel处理,代码结构更清晰,容易维护。
要深入理解MVVM模式,推荐阅读这篇 章:
http://www.codeproject.com/KB/WPF/WpfMvvmQuickStart.aspx
而实际开发中实现这个模式,推荐使用一个开源辅助toolkit:mvvmlight
toolkit源代码在这里:
http://mvvmlight.codeplex.com/
如下例,mvvmlight的使用重点是把控件Event通过EventToCommand转换为Command交由ViewModel处理数据,数据处理完毕后如果需要通知View进行效果动画或者页面跳转之类的纯UI操作,则可以在ViewModel的Command里向View发送一个Message,View通过注册该类型Message就可以接收到该Message,然后进行纯UI处理。
当然,如果控件事件响应时只有纯粹UI效果处理而不牵涉到数据,那么就没必要使用繁琐的EventToCommand转换为Command交给ViewModle,还是在xaml.cs里注册该事件处理吧,不要为了死模式而不变通。
mvvmlight简单使用方法例子:
以wp8默认创建的pivot application为例,以下修改框架生成的ItemViewModel为:
using GalaSoft.MvvmLight; namespace PivotApp.ViewModels { public class ItemViewModel : ViewModelBase { private string _lineOne; public string LineOne { get { return _lineOne; } set { if (value != _lineOne) { _lineOne = value; RaisePropertyChanged("LineOne"); } } } private string _lineTwo; public string LineTwo { get { return _lineTwo; } set { if (value != _lineTwo) { _lineTwo = value; RaisePropertyChanged("LineTwo"); } } } private string _lineThree; public string LineThree { get { return _lineThree; } set { if (value != _lineThree) { _lineThree = value; RaisePropertyChanged("LineThree"); } } } } }
using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using GalaSoft.MvvmLight.Messaging; using System.Collections.ObjectModel; namespace PivotApp.ViewModels { public class MainViewModel : ViewModelBase { public MainViewModel() { this.Items = new ObservableCollection<ItemViewModel>(); ItemClickCommand = new RelayCommand<object>(OnItemClick); } public ObservableCollection<ItemViewModel> Items { get; private set; } public bool IsDataLoaded { get; private set; } public RelayCommand<object> ItemClickCommand { get; private set; } private void OnItemClick(object selectedItem) { if (selectedItem != null && (selectedItem as ItemViewModel) != null) { (selectedItem as ItemViewModel).LineOne += "+"; } Messenger.Default.Send<NotificationMessage>(new NotificationMessage("OnItemClick")); } public void LoadData() { // 示例数据;替换为实际数据 this.Items.Add(new ItemViewModel() { LineOne = "runtime one", LineTwo = "Maecenas praesent accumsan bibendum", LineThree = "Facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime two", LineTwo = "Dictumst eleifend facilisi faucibus", LineThree = "Suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime three", LineTwo = "Habitant inceptos interdum lobortis", LineThree = "Habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu suscipit torquent" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime four", LineTwo = "Nascetur pharetra placerat pulvinar", LineThree = "Ultrices vehicula volutpat maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime five", LineTwo = "Maecenas praesent accumsan bibendum", LineThree = "Maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos interdum lobortis nascetur" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime six", LineTwo = "Dictumst eleifend facilisi faucibus", LineThree = "Pharetra placerat pulvinar sagittis senectus sociosqu suscipit torquent ultrices vehicula volutpat maecenas praesent" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime seven", LineTwo = "Habitant inceptos interdum lobortis", LineThree = "Accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime eight", LineTwo = "Nascetur pharetra placerat pulvinar", LineThree = "Pulvinar sagittis senectus sociosqu suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan bibendum" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime nine", LineTwo = "Maecenas praesent accumsan bibendum", LineThree = "Facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime ten", LineTwo = "Dictumst eleifend facilisi faucibus", LineThree = "Suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime eleven", LineTwo = "Habitant inceptos interdum lobortis", LineThree = "Habitant inceptos interdum lobortis nascetur pharetra placerat pulvinar sagittis senectus sociosqu suscipit torquent" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime twelve", LineTwo = "Nascetur pharetra placerat pulvinar", LineThree = "Ultrices vehicula volutpat maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime thirteen", LineTwo = "Maecenas praesent accumsan bibendum", LineThree = "Maecenas praesent accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos interdum lobortis nascetur" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime fourteen", LineTwo = "Dictumst eleifend facilisi faucibus", LineThree = "Pharetra placerat pulvinar sagittis senectus sociosqu suscipit torquent ultrices vehicula volutpat maecenas praesent" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime fifteen", LineTwo = "Habitant inceptos interdum lobortis", LineThree = "Accumsan bibendum dictumst eleifend facilisi faucibus habitant inceptos interdum lobortis nascetur pharetra placerat" }); this.Items.Add(new ItemViewModel() { LineOne = "runtime sixteen", LineTwo = "Nascetur pharetra placerat pulvinar", LineThree = "Pulvinar sagittis senectus sociosqu suscipit torquent ultrices vehicula volutpat maecenas praesent accumsan bibendum" }); this.IsDataLoaded = true; } } }
把MainPage.xaml里第一个LongListSelector的SelectionChanged事件转换成Command交由ViewModel处理:
<phone:LongListSelector Name="longList1" Margin="0,0,-12,0" ItemsSource="{Binding Items}"> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectionChanged"> <cmd:EventToCommand Command="{Binding ItemClickCommand}" CommandParameter="{Binding SelectedItem, ElementName=longList1}"> </cmd:EventToCommand> </i:EventTrigger> </i:Interaction.Triggers> <phone:LongListSelector.ItemTemplate> <DataTemplate> <StackPanel Margin="0,0,0,17"> <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" /> <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" /> </StackPanel> </DataTemplate> </phone:LongListSelector.ItemTemplate> </phone:LongListSelector>
以上,xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP8"
上面代码有个错误,SelectedItem在Command触发之后才被LongListSelector修改,所以每次拿到的都是上一次Selection的值,可以改为使用PassEventArgsToCommand传递EventArgs
在MainPage.xaml.cs的成员函数如下:
public MainPage() { InitializeComponent(); DataContext = App.ViewModel; } // 为 ViewModel 项加载数据 protected override void OnNavigatedTo(NavigationEventArgs e) { if (!App.ViewModel.IsDataLoaded) { App.ViewModel.LoadData(); } Messenger.Default.Register<NotificationMessage>(this, HandleNotificationMessage); } protected override void OnNavigatedFrom(NavigationEventArgs e) { Messenger.Default.Unregister(this); } private void HandleNotificationMessage(NotificationMessage msg) { longList1.SelectedItem = null; this.NavigationService.Navigate(new Uri("/NextPage.xaml", UriKind.Relative)); }