MVFM框架----示例

   本章介绍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的高级用法,熟练使用以后编写代码效率将会大大提高。

   示例代码在本人空间资源中

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章