本章介紹mvfm的詳細使用方法,並提供win8下的demo以供參考。
用VS2012建立工程,選擇windows應用商店中的網格應用程序。本次的demo就是將這個vs2012自帶的示例改造成mvfm方式運行。
由於這個示例已經有了view和model層(SampleDataSource類),這兩塊就不需要重寫了,我們直接開始構造fuction model。
首先引入Windows.UI.Interactivity類庫,這個庫是微軟提供的一個擴展庫,需要從NuGet下載安裝。
然後再引入mvfm庫,這是mvfm開發模式所依賴的動態庫。
構建function model,代碼如下:
public class SampleFunctionModel : FunctionModelBase
{
#region Property
IEnumerable<SampleDataGroup> _groups;
public IEnumerable<SampleDataGroup> Groups
{
get
{
if (_groups == null)
_groups = SampleDataSource.GetGroups("AllGroups");
return _groups;
}
}
SampleDataGroup _currentGroup;
public SampleDataGroup CurrentGroup
{
get
{
return _currentGroup;
}
set
{
this.SetProperty(ref _currentGroup, value);
}
}
SampleDataItem _currentItem;
public SampleDataItem CurrentItem
{
get
{
return _currentItem;
}
set
{
this.SetProperty(ref _currentItem, value);
}
}
#endregion
#region Command
public ActionCommand<object> SetCurrentGroupCommand { get; private set; }
public ActionCommand<object> SetCurrentItemCommand { get; private set; }
#endregion
#region Command Function
void SetCurrentGroup(object parameter)
{
var Group = (parameter as ExCommandParameter).Parameter as SampleDataGroup;
if (Group != null)
CurrentGroup = Group;
}
void SetCurrentItem(object parameter)
{
var Item = (parameter as ExCommandParameter).Parameter as SampleDataItem;
if (Item != null)
{
CurrentItem = Item;
}
}
#endregion
#region Public Function
#endregion
#region Private Function
#endregion
#region protected Function
protected override void InitializeData()
{
SetCurrentGroupCommand = new ActionCommand<object>(SetCurrentGroup);
SetCurrentItemCommand = new ActionCommand<object>(SetCurrentItem);
}
#endregion
}
其中Groups是整個工程的數據源,Groups從SampleDataSource中獲取,並交由view曾顯示。mvfm中function model並不存儲數據,只負責業務邏輯處理,function model僅保存臨時數據。例如CurrentGroup和CurrentItem,這兩個屬性分別代表當先選擇或顯示的SampleDataGroup和SampleDataItem。還需要說明一點的時,有些UI邏輯放在頁面裏處理,並不放在function model中。像頁面跳轉這樣的常用ui邏輯都放在界面中處理。
SetCurrentGroupCommand和SetCurrentItemCommand分別代表設置當前的SampleDataGroup命令和當前的SampleDataItem命令。將邏輯封裝成命令進行交互稱作命令模式,mvfm中view與function model的絕大部分交互都通過這種模式進行。
在InitializeData函數中指定了具體執行命令的函數,SetCurrentGroupCommand由SetCurrentGroup函數執行。所有的命令都是ICommand類型,而ActionCommand<T>則是將ICommand封裝了,方便使用。
下面看看SetCurrentGroup中德代碼。若界面上有參數傳過來,都將是ExCommandParameter這種類型,下面是ExCommandParameter類的代碼:
public class ExCommandParameter
{
/// <summary>
/// 事件觸發源
/// </summary>
public object Sender { get; set; }
/// <summary>
/// 事件參數
/// </summary>
public object EventArgs { get; set; }
/// <summary>
/// 額外參數
/// </summary>
public object Parameter { get; set; }
/// <summary>
/// 額外參數 2
/// </summary>
public object Parameter2 { get; set; }
/// <summary>
/// 額外參數 3
/// </summary>
public object Parameter3 { get; set; }
}
其中Sender爲發送着,比如控件之類的,EventArgs則是某些代理中德事件參數,比如這個命令是button的click觸發的,則EventArgs是對應的RoutedEventArgs事件。Parameter則是附帶額外的參數,最多可以帶三個。
各位看到這裏可能對function model還有些迷惑,等看完xaml中德處理在回頭看就好理解了。
function model寫完後在APP中聲明 function model的資源:
<x:String x:Key="AppName">MvfmText</x:String>
這樣就可以在其他的頁面中使用function model中Command了。
首先看GroupedItemsPage頁面,刪光cs中德代碼,僅保留構造函數
public sealed partial class GroupedItemsPage : MvfmText.Common.LayoutAwarePage
{
public GroupedItemsPage()
{
this.InitializeComponent();
}
}
在xaml頁中聲明數據源
<common:LayoutAwarePage
x:Name="pageRoot"
x:Class="MvfmText.GroupedItemsPage"
DataContext="{StaticResource SampleFunctionModel}"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MvfmText"
xmlns:data="using:MvfmText.Data"
xmlns:common="using:MvfmText.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mvfm="using:Mvfm"
xmlns:i="using:Windows.UI.Interactivity"
mc:Ignorable="d">
DataContext就是數據源,數據從SampleFunctionModel中獲取。特別注意 xmlns:mvfm="using:Mvfm"和 xmlns:i="using:Windows.UI.Interactivity"這兩條語句,xaml與function將同過這兩個命名空間進行。
然後聲明網格所需要使用的數據資源
<Page.Resources>
<!--
此頁所顯示的分組項的集合,綁定到完整
項列表的子集,因爲無法虛擬化組中的項
-->
<CollectionViewSource
x:Name="groupedItemsViewSource"
Source="{Binding Groups}"
IsSourceGrouped="true"
ItemsPath="TopItems"
d:Source="{Binding AllGroups, Source={d:DesignInstance Type=data:SampleDataSource, IsDesignTimeCreatable=True}}"/>
</Page.Resources>
其中Groups就是SampleFunctionModel中的Groups屬性。
然後再造GridView:
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
SelectionMode="None"
IsSwipeEnabled="false"
IsItemClickEnabled="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<mvfm:EventArgNameCommandAction EventArgName="ClickedItem" Command="{Binding SetCurrentItemCommand}"/>
<local:NavigateAction NavigateName="MvfmText.ItemDetailPage"/>
<!--<mvfm:NavigateAction NavigateName="MvfmText.ItemDetailPage"/>-->
</i:EventTrigger>
</i:Interaction.Triggers>
</GridView>
GridView自帶的屬性設置就不說明了,重點介紹 <i:Interaction.Triggers>
<i:EventTrigger EventName="ItemClick">
<mvfm:EventArgNameCommandAction EventArgName="ClickedItem" Command="{Binding SetCurrentItemCommand}"/>
<local:NavigateAction NavigateName="MvfmText.ItemDetailPage"/>
</i:EventTrigger>
</i:Interaction.Triggers>
這幾行語句。Triggers,Behavior,Action是mvfm所依賴的核心技術中德3個。Triggers爲觸發器,Behavior爲行爲附加,Action則負責執行某些命令。Triggers往往與Action相伴出現,通過控件中的某些行爲或event觸發執行Action。
這幾行語句中EventTrigger指定由GridView中的ItemClick事件觸發,觸發的Action分別爲NavigateAction和EventArgNameCommandAction。NavigateAction執行導航,NavigateName參數指定導航頁,此Action觸發後界面將直接跳轉到ItemDetailPage頁面。也就是說若點擊了GridView中的某一項,界面就會跳轉到ItemDetailPage頁。EventArgNameCommandAction中的Command指定所要執行的命令,這裏則指定了執行SampleFunctionModel中的SetCurrentItemCommand命令。EventArgName參數指定將EventArg中的哪個屬性作爲參數傳遞到Command中。
然後看下HeaderTemplate中的代碼:
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Grid Margin="1,0,0,6">
<Button
AutomationProperties.Name="Group Title"
Style="{StaticResource TextPrimaryButtonStyle}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Title}" Margin="3,-7,10,10" Style="{StaticResource GroupHeaderTextStyle}" />
<TextBlock Text="{StaticResource ChevronGlyph}" FontFamily="Segoe UI Symbol" Margin="0,-7,0,10" Style="{StaticResource GroupHeaderTextStyle}"/>
</StackPanel>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<mvfm:ExInvokeCommandAction Command="{Binding SetCurrentGroupCommand, Source={StaticResource SampleFunctionModel}}" CommandParameter="{Binding}"/>
<local:NavigateAction NavigateName="MvfmText.GroupDetailPage"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
注意其中的這幾行語句
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<mvfm:ExInvokeCommandAction Command="{Binding SetCurrentGroupCommand, Source={StaticResource SampleFunctionModel}}" CommandParameter="{Binding}"/>
<local:NavigateAction NavigateName="MvfmText.GroupDetailPage"/>
</i:EventTrigger>
</i:Interaction.Triggers>
原理與前面是一樣的,主要說明下ExInvokeCommandAction, ExInvokeCommandAction中Command指定的是完整的Command路徑,這樣的好處在於不管Action在何處聲明,都可以執行到functiong model中的命令。 其中CommandParameter則是將自身的數據源作爲參數傳輸到了執行對應命令的函數中,此命令對應的是如下函數 void SetCurrentGroup(object parameter)
{
var Group = (parameter as ExCommandParameter).Parameter as SampleDataGroup;
if (Group != null)
CurrentGroup = Group;
}
剩下兩個頁面就不額外說明了,原理差不多,運行後你會發現執行效果和原來沒有任何差別。由於page對應的.cs中不需要寫人和代碼,這將大大減輕我們的工作量,並且這樣的邊寫方式易於修改,當需求頻繁變化是也能輕鬆應對。
並且我們還有兩個原model中提供的兩個函數沒有用到,GetGroup和GetItem完全沒用用武之地了,因爲我們可以從頁面中直接獲取對應的數據,並在另一個頁面中使用,這樣省去了頁面間的參數傳遞以及部分數據處理邏輯。這樣輕鬆的編程方式
你還有什麼理由拒絕呢?以後我會介紹mvfm的高級用法,熟練使用以後編寫代碼效率將會大大提高。
示例代碼在本人空間資源中