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");
}
}
}
}
}
将框架生成的MainViewModel修改为:
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));
}