Опубликован: 02.08.2013 | Доступ: свободный | Студентов: 464 / 17 | Длительность: 18:38:00
Специальности: Программист
Лекция 15:

Основы MVVM

Шаблон Приложение Windows Phone с привязкой к данным

Приложение, построенное с использованием данного шаблона, реализует основные принципы, характерные для шаблона MVVM. Нельзя сказать, что такое приложение в полной мере соответствует принципам MVVM. Например, модель и модель представления разделены недостаточно чётко (хотя, в данном случае можно говорить о том, что файл ItemViewModel, представляющий собой класс, определяющий структуру данных, которая используется в приложении, ближе к понятию "Model", чем к понятию "ViewModel", которое присутствует в его названии). В файлах программного кода страниц, которые формируют пользовательский интерфейс, производятся некоторые действия. Однако, есть в этом шаблоне и реализация основополагающих идей MVVM. В частности, он предусматривает использование данных периода проектирования, связь между исходными данными и представлением реализована с помощью системы привязки данных. В интерфейсе используются шаблоны, что закладывает основу для работы со списками данных переменной длины.

Рассмотрим приложение, созданное по этому шаблону, подробнее. На рис. 21.1. приведено окно Visual Studio, в котором развёрнута структура проекта, в визуальном конструкторе открыта страница MainPage.xaml.

Приложение, созданное по шаблону Приложение Windows Phone с привязкой к данным

увеличить изображение
Рис. 21.1. Приложение, созданное по шаблону Приложение Windows Phone с привязкой к данным

Начнём рассмотрение проекта с папки SampleData. Здесь находится XAML-файл (Листинг 21.1), MainViewModelSampleData, который содержит данные периода проектирования. Структура данных, которые описаны в файле определяется классом ItemViewModel. В заголовке XAML-файла описано пространство имен vm, соответствующее пространству имен класса ItemViewModel. В файле содержится, кроме того, описание одиночного свойства из класса MainViewModel.

<vm:MainViewModel
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:L5_1.ViewModels"
    SampleProperty="Пример значения свойства текста">

    <vm:MainViewModel.Items>
        <vm:ItemViewModel ID="0" LineOne="первая схема" 
                             LineTwo="Maecenas praesent accumsan bibendum" 
                 LineThree="Maecenas praesent accumsan bibendum 
                            dictumst eleifend facilisi faucibus 
                      habitant inceptos interdum lobortis 
                      nascetur"/>
        <vm:ItemViewModel ID="1" LineOne="вторая схема" 
                             LineTwo="Dictumst eleifend facilisi faucibus" 
                 LineThree="Pharetra placerat pulvinar sagittis 
                            senectus sociosqu suscipit torquent 
                      ultrices vehicula volutpat maecenas 
                      praesent"/>

    </vm:MainViewModel.Items>
</vm:MainViewModel>
Листинг 21.1. Фрагмент файла MainViewModelSampleData.xaml

Этот файл и представляет собой тот контекст данных, который назначен страницам приложения. При этом в режиме проектирования контекст данных для страницы MainPage (Листинг 21.2) соответствует общему списку данных, на этой странице выводится список элементов. А на странице DetailsPage (Листинг 21.3) выводится подробная информации об элементе с главной страницы, которого, при работе приложения, коснулся пользователь. В режиме проектирования контекст данных страницы так же установлен на вышеупомянутый файл, но контекст данных сетки LayoutRoot уточнён до элемента с индексом [0].

<phone:PhoneApplicationPage
    x:Class="L5_1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait"  Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--Для контекста данных установлен верхний пример данных, и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

       <!--TitlePanel содержит имя приложения и заголовок страницы-->
        <StackPanel Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock Text="имя страницы" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel содержит LongListSelector и LongListSelector ItemTemplate. Поместите здесь дополнительное содержимое-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" 
            SelectionChanged="MainLongListSelector_SelectionChanged">
                <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>
        </Grid>

    </Grid>

</phone:PhoneApplicationPage>
Листинг 21.2. Фрагмент файла MainPage.xaml

Класс ItemViewModel, по сути, ближе всего к понятию "модели", принятом в MVVM. Он описывает базовую структуру данных, применяемых в программе. Эти данные применяются, во-первых, при заполнении страницы MainPage – на ней выводится список элементов, во-вторых – при заполнении страницы DetailsPage, которая содержит подробное описание одного элемента. Как уже было сказано, в режиме проектирования данные, выводимые на страницах, определены в файле MainViewModelSampleData.xaml. При запуске приложения контекст данных страницы MainPage (Листинг 21.4) устанавливается на объект типа MainViewModel, созданный в классе App (Листинг 21.5). Похожая процедура выполняется и при вызове страницы DetailsPage (Листинг 21.6). В XAML контекст данных установлен на один из элементов, описанных в MainViewModelSampleData.xaml, в режиме исполнения он программно устанавливается на один из элементов, присутствующих в объекте типа MainViewModel, который создаётся в классе App.

<phone:PhoneApplicationPage
    x:Class="L5_1.DetailsPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DataContext="{d:DesignData SampleData/MainViewModelSampleData.xaml}"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait"  Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

    <!--Для контекста данных установлен верхний пример данных и первый элемент из коллекции примеров данных ниже,
     и LayoutRoot содержит корневую сетку, где размещается все содержимое страницы-->
    <Grid x:Name="LayoutRoot" Background="Transparent" d:DataContext="{Binding Items[0]}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!--TitlePanel содержит имя приложения и заголовок страницы-->
        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock Text="МОЕ ПРИЛОЖЕНИЕ" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <!--ContentPanel содержит текст сведений. Поместите здесь дополнительное содержимое-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <TextBlock Text="{Binding LineThree}" TextWrapping="Wrap" Style="{StaticResource PhoneTextNormalStyle}"/>
        </Grid>
    </Grid>

</phone:PhoneApplicationPage>
Листинг 21.3. Фрагмент файла DetailPage.xaml
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using L5_1.Resources;
using L5_1.ViewModels;

namespace L5_1
{
    public partial class MainPage : PhoneApplicationPage
    {
        // Конструктор
        public MainPage()
        {
            InitializeComponent();

            // Задайте для контекста данных элемента управления LongListSelector пример данных
            DataContext = App.ViewModel;

        }

        // Загрузка данных для элементов ViewModel
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (!App.ViewModel.IsDataLoaded)
            {
                App.ViewModel.LoadData();
            }
        }

        // Обработка выбранных элементов, измененных в LongListSelector
        private void MainLongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Если выбранный элемент равен NULL (ничего не выбрано), никакие действия не требуются
            if (MainLongListSelector.SelectedItem == null)
                return;

            // Переход на новую страницу
            NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" +
             (MainLongListSelector.SelectedItem as ItemViewModel).ID, UriKind.Relative));

            // Сброс выбранного элемента в NULL (ничего не выбрано)
            MainLongListSelector.SelectedItem = null;
        }
  
    }
}
Листинг 21.4. Фрагмент файла MainPage.xaml.cs