Опубликован: 13.12.2011 | Уровень: для всех | Доступ: платный
Самостоятельная работа 5:

Реализация WPF проекта с помощью MVVM toolkit’а

< Лекция 9 || Самостоятельная работа 5: 12 || Лекция 10 >

Шаг 4. Редактирование

Сделаем так, что для выделенной в списке книги будет открываться редактор. Изменим XAML-разметку формы: MainView.xaml

<Window x:Class="SampleMVVM.Views.MainView"
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:c="clr-namespace:SampleMVVM.Commands"
    Title="Main Window" Height="400" Width="350">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
               <ListView ItemsSource="{Binding BooksList}" 
                         IsSynchronizedWithCurrentItem="True">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Bisque" BorderThickness="1" 
                            Margin="10">
                        <StackPanel Margin="10">
                            <TextBlock Text="{Binding Title}" 
                                       FontWeight="Bold"/>
                            <TextBlock Text="{Binding Author}" />
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="Осталось:" />
                                <TextBlock Text="{Binding Count}"
                                           FontWeight="Bold" Margin="10,0"/>
                                <TextBlock Text="шт" />
                            </StackPanel>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
       
        <ContentControl Grid.Column="1" Content="{Binding BooksList}">
            <ContentControl.ContentTemplate>
                <DataTemplate>
                    <Border BorderBrush="Bisque" BorderThickness="1" 
                            Margin="10">
                        <StackPanel Margin="10">
                            <TextBlock Text="Название:"/>
                            <TextBox Text="{Binding Title, 
                                        UpdateSourceTrigger=PropertyChanged}"
                                     Margin="0,0,0,10"/>
                           
                            <TextBlock Text="Автор:"/>
                            <TextBox Text="{Binding Author, 
                                        UpdateSourceTrigger=PropertyChanged}"
                                      Margin="0,0,0,10"/>
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </ContentControl.ContentTemplate>
        </ContentControl>
    </Grid>
</Window>

Стоит обратить внимание на конструкцию UpdateSourceTrigger=PropertyChanged в строке биндинга. Это значит, что любое изменение, производимое в данном поле, будет немедленно отражаться на источнике. Это легко увидеть:

Результат конструкции UpdateSourceTrigger=PropertyChanged

Рис. 14.4. Результат конструкции UpdateSourceTrigger=PropertyChanged

Если этого не написать, источник будет обновляться только по окончании редактирования (т.е. когда контрол будет терять фокус). Это может привести к следующей ошибке интерфейса: когда жмешь "Сохранить", сохраняется все, кроме только что измененного поля.

Шаг 5. Команды

Добавим в приложение функциональности. Пусть некие читатели берут книги и возвращают. Соответственно, сделаем две кнопки — "Выдать" и "Забрать", меняющие количество имеющихся в наличии книг. Если книг не осталось (Count = 0), кнопка "Выдать" должна дизаблиться.

В MVVM не пишутся обработчики событий. Функции, которые нужно выполнять контролам, пишутся во ViewModel и биндятся к контролам точно так же, как поля. Только используется механизм команд.

Команда должна представлять из себя экземпляр класса, реализующего интерфейс ICommand. К счастью, MVVM Toolkit снова нам помог и сгенерил целых два таких класса - DelegateCommand для реализации команды без параметров и DelegateCommand<T> - для реализации команды с параметром типа T.

Мы параметры передавать не будем. Код во ViewModel будет таков: BookViewModel.cs

#region Забрать

private DelegateCommand getItemCommand;

public ICommand GetItemCommand
{
    get
    {
        if (getItemCommand == null)
        {
            getItemCommand = new DelegateCommand(GetItem);
        }
        return getItemCommand;
    }
}

private void GetItem()
{
    Count++;
}

#endregion

#region Выдать

private DelegateCommand giveItemCommand;

public ICommand GiveItemCommand
{
    get
    {
        if (giveItemCommand == null)
        {
            giveItemCommand = new DelegateCommand(GiveItem, CanGiveItem);
        }
        return giveItemCommand;
    }
}

private void GiveItem()
{
    Count--;
}

private bool CanGiveItem()
{
    return Count > 0;
}

#endregion

Обратите внимание, что этот код добавляется в BookViewModel, а не в MainViewModel. Дело в том, что мы будем добавлять кнопки в ContentControl, DataContext-ом которого является именно BookViewModel.

С первой командой все ясно. Создали команду, и назначили ей в качестве действия метод GetItem, который и будет вызываться при ее активации. Со второй немного интереснее, но тоже просто. Помимо того, что она выполняет некоторое действие, она еще и может проверять с помощью метода CanGiveItem(), может она выполняться или нет.

В XAML-разметку нашей формы добавим следующее

<ContentControl Grid.Column="1" Content="{Binding BooksList}">
    <ContentControl.ContentTemplate>
        <DataTemplate>
            <Border BorderBrush="Bisque" BorderThickness="1" Margin="10">
                <StackPanel Margin="10">
                    <TextBlock Text="Название:"/>
                    <TextBox Text="{Binding Title, 
                                      UpdateSourceTrigger=PropertyChanged}"
                             Margin="0,0,0,10"/>

                    <TextBlock Text="Автор:"/>
                    <TextBox Text="{Binding Author, 
                                      UpdateSourceTrigger=PropertyChanged}"
                             Margin="0,0,0,10"/>

                    <StackPanel Orientation="Horizontal">
                        <Button Content="Выдать" Command="{Binding 
                                GiveItemCommand}" Margin="10,0" />
                        <Button Content="Забрать" Command="{Binding 
                                GetItemCommand}" Margin="10,0" />
                    </StackPanel>
                </StackPanel>
            </Border>
        </DataTemplate>
    </ContentControl.ContentTemplate>
</ContentControl>

Вот и все. Мы получили требуемую функциональность. Количество экземпляров книги увеличивается и уменьшается, а когда их становится 0, кнопка "Выдать" дизаблится (благодаря упомянутому CanGiveItem).

Краткие итоги

В приведенном приложении все данные и реализация логики вынесены в отдельное место. В фоновом коде формы мы не добавили ни строчки. XAML понятен. Благодаря паттерну MVVM в коде легко разобраться и его легко сопровождать. Стоит добавить, что рассмотренный MVVM Toolkit содержит весьма полезный набор классов, особенно хорошо подходящий для небольших задач, не требующих модульности или необходимости использовать IoC. Для сложных приложений, можно рекомендовать более "тяжелые" Toolkit’ы, один из них мы будем рассматривать в последующих лекциях.

< Лекция 9 || Самостоятельная работа 5: 12 || Лекция 10 >
Анисимов Михаил
Анисимов Михаил
Украина
Наталия Шаститко
Наталия Шаститко
Украина, Днепропетровск, Днепропетровский Гуманитарный Университет, 2014