WP8 MVVM设计模式

  类似了Android里边的MVC模式, Windows Phone 有自己的Model-View-ViewModel模式,这种模式的作用就是为了Data和UI分离开来。

  如果你英文较好的话,你可以不再阅读以下内容,直接访问 WindowsPhone Dev Center 学习阅读,因为以下内容是对其的一个中文复述。 

  

  这次演练,将执行以下的任务:

  1.  创建一个Model、一个ModelView和两个View;
  2.  使用XAML文件绑定数据;
  3.  创建一个定制数据的转换器;
  4.  保留页面的数据;
  5.  在isolated storage保存数据;
  6.  使用App Bar暴露保存的功能。

  创建一个Model、一个ModelView和两个View

  •   右击MVVMTestApp->Add->New folder 添加Model、ModelView和View文件夹如下:

  

  •  右击Model->Add->class 新建数据Model,命名为Accomplishment.cs, 添加内容如下:

  

using System;
using System.ComponentModel;


namespace MVVMTestApp.Model
{
    public class Accomplishment : INotifyPropertyChanged
    {
        // The name of the accomplishment.
        public string Name { get; set; }

        // The type of the accomplishment, Item or Level.
        public string Type { get; set; }

        // The number of each item that has been collected.
        private int _count;
        public int Count
        {
            get
            {
                return _count;
            }
            set
            {
                _count = value;
                RaisePropertyChanged("Count");
            }
        }

        // Whether a level has been completed or not
        private bool _completed;
        public bool Completed
        {
            get
            {
                return _completed;
            }
            set
            {
                _completed = value;
                RaisePropertyChanged("Completed");
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        // Create a copy of an accomplishment to save.
        // If your object is databound, this copy is not databound.
        public Accomplishment GetCopy()
        {
            Accomplishment copy = (Accomplishment)this.MemberwiseClone();
            return copy;
        }
    }
}

  当某个属性改变的时候,这个类通过执行INotifyPropertyChanged接口,使用PropertyChanged事件通知绑定了(OneWay或者TwoWay绑定)这个属性的View更新数据。

  

  •  右击Model->Add->class 新建数据ViewModel,命名为ViewModel.cs,它是连接View和Model的一个中枢。 现添加内容如下:  

  

using System;
using System.Windows;
using System.Collections.ObjectModel;
using System.IO.IsolatedStorage;
using MVVMTestApp.Model;

namespace MVVMTestApp.ViewModelNamespace
{
    public class ViewModel
    {
        public ObservableCollection<Accomplishment> Accomplishments { get; set; }


        public void GetAccomplishments()
        {
            if (IsolatedStorageSettings.ApplicationSettings.Count > 0)
            {
                GetSavedAccomplishments();
            }
            else
            {
                GetDefaultAccomplishments();
            }
        }


        public void GetDefaultAccomplishments()
        {
            ObservableCollection<Accomplishment> a = new ObservableCollection<Accomplishment>();

            // Items to collect
            a.Add(new Accomplishment() { Name = "Potions", Type = "Item" });
            a.Add(new Accomplishment() { Name = "Coins", Type = "Item" });
            a.Add(new Accomplishment() { Name = "Hearts", Type = "Item" });
            a.Add(new Accomplishment() { Name = "Swords", Type = "Item" });
            a.Add(new Accomplishment() { Name = "Shields", Type = "Item" });

            // Levels to complete
            a.Add(new Accomplishment() { Name = "Level 1", Type = "Level" });
            a.Add(new Accomplishment() { Name = "Level 2", Type = "Level" });
            a.Add(new Accomplishment() { Name = "Level 3", Type = "Level" });

            Accomplishments = a;
            //MessageBox.Show("Got accomplishments from default");
        }


        public void GetSavedAccomplishments()
        {
            ObservableCollection<Accomplishment> a = new ObservableCollection<Accomplishment>();

            foreach (Object o in IsolatedStorageSettings.ApplicationSettings.Values)
            {
                a.Add((Accomplishment)o);
            }

            Accomplishments = a;
            //MessageBox.Show("Got accomplishments from storage");
        }
    }
}
  •  右击View->New Item->Windows Phone User Control新建第一个View,命名为ItemView.xaml,在GRID布局里边添加如下内容:

  

<ListBox ItemsSource="{Binding}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="80"/>
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <TextBlock x:Name="Item" Text="{Binding Path=Name, Mode=OneWay}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" />
                <TextBox x:Name="Count" Text="{Binding Path=Count, Mode=TwoWay}" Grid.Column="1" TextAlignment="Center" InputScope="Number"/>
                <TextBlock x:Name="Check" Text="{Binding Path=Count, Mode=OneWay}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
  •  右击View->New Item->Windows Phone User Control新建第二个View,命名为LevelView.xaml,右击xaml->View Code,替换一下内容: 

  

using System;
using System.Windows.Controls;
using System.Globalization;

namespace MVVMTestApp.View
{
    public partial class LevelView : UserControl
    {
        public LevelView()
        {
            InitializeComponent();
        }
    }


    public class BoolOpposite : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            bool b = (bool)value;
            return !b;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            string s = value as string;
            bool b;

            if (bool.TryParse(s, out b))
            {
                return !b;
            }
            return false;
        }
    }
}
  •  在LevelView.xaml文件,<UserControl>标签中,加入本地的命名空间:

  

xmlns:src="clr-namespace:MVVMTestApp.View"

  

  •  在GRID布局前,将转换器类(src:BoolOpposite)的实例作为唯一标识(BoolOpposite)的资源, 在第二个LevelView 的CheckBox IsEnabled中,数据在绑定的View和data Model中传递时,调用该引用修改数据。

  

<UserControl.Resources>
    <src:BoolOpposite x:Key="BoolOpposite"/>
</UserControl.Resources>
  •  在LevelView的GRID布局中加入如下内容:

  

<ListBox ItemsSource="{Binding}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="200"/>
                    <ColumnDefinition Width="80"/>
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <TextBlock x:Name="Level" Text="{Binding Path=Name, Mode=OneWay}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
                <CheckBox x:Name="Completed" IsChecked="{Binding Path=Completed, Mode=TwoWay}" Grid.Column="1" HorizontalAlignment="Center" IsEnabled="{Binding Path=Completed, Converter={StaticResource BoolOpposite}}"/>
                <TextBlock x:Name="Check" Text="{Binding Path=Completed, Mode=OneWay}" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

  构建主页面

  •  在MainPage.xaml文件,<Phone>标签中,增加View的命名空间

  

xmlns:views="clr-namespace:MVVMTestApp.View"
  •  替换ContentPanel GRID内容如下:

  

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <StackPanel>
        <TextBlock Text="Items Collected" Foreground="{StaticResource PhoneAccentBrush}" Style="{StaticResource PhoneTextLargeStyle}" />
        <views:ItemView x:Name="ItemViewOnPage" Height="200"/>

        <TextBlock Text="Levels Completed" Foreground="{StaticResource PhoneAccentBrush}" Style="{StaticResource PhoneTextLargeStyle}" />
        <views:LevelView x:Name="LevelViewOnPage" Height="200"/>
    </StackPanel>
</Grid>

  这样就把ItemView和LevelView两个视图跟应用的页面关联起来了。

  •  视图有了,接下来就是要给视图填充数据了, 把MainPage.xaml.cs修改如下:

  

using System;
using System.Linq;
using System.Windows;
using Microsoft.Phone.Controls;
using MVVMTestApp.ViewModelNamespace;

namespace MVVMTestApp
{
    public partial class MainPage : PhoneApplicationPage
    {
        private ViewModel vm;

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            vm = new ViewModel();
        }


        protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
        {
            base.OnNavigatedTo(e);

            // Later, you will replace this next line with something better.
            vm.GetAccomplishments();


            // There are two different views, but only one view model.
            // So, use LINQ queries to populate the views.

            // Set the data context for the Item view.
            ItemViewOnPage.DataContext = from Accomplishment in vm.Accomplishments where Accomplishment.Type == "Item" select Accomplishment;

            // Set the data context for the Level view.
            LevelViewOnPage.DataContext = from Accomplishment in vm.Accomplishments where Accomplishment.Type == "Level" select Accomplishment;

            // If there is only one view, you could use the following code
            // to populate the view.
            //AccomplishmentViewOnPage.DataContext = vm.Accomplishments;
        }
    }
}

  至此运行一下效果,内容已经填充到了主页面了

  

To be continue...

    

原文地址:https://www.cnblogs.com/shaddock2013/p/3267863.html