VisualStates в Silverlight или управление состоянием из ViewModel (MVVM)

WPF, MVVM, Silverlight | создано: 12.12.2010 | опубликовано: 12.12.2010 | обновлено: 13.01.2024 | просмотров: 4662

Не редко, возникает потребность переключить состояние VisualState (что естественно находится во View) какого-либо объекта (например UserControl) программно из ViewModel. В этой статье я хочу показать как просто cделать.

MVVM библиотека для простоты

Статья расчитана на тех, кто уже знаком с Silverlight, а так же имеет какое-либо представление об MVVM. Чтобы было проще, я буду использовать свою библиотеку, которую сам и сделал (не скромно, зато она у меня есть). А вы может использовать, например, MVVMLight. Для начала создадим простой проект Silverlight и добавим сборку в проект.

Реализация хотелки.

Итак, для начала озвучу свою "хотелку". Я хочу управлять VisaulState (визуальное состояние) UserControl из программного кода ViewModel. Коротко и практично. Для начала создадим новый класс и файл MainPageViewModel.cs для главной (MainPage.xaml):

public class MainPageViewModel : ViewModelBase
{
}

Данный класс надо унаследовать от класса ViewModelBase, который находится в сборке Calabonga.Silverlight.Framework. Теперь создадим свойство VisualState в котором будем хранить текущее имя состояния: 

#region свойство VisualState

/// <summary>
/// поле для хранения значений свойства <see cref="VisualState"/>
/// </summary>
private String visualState = "WelcomeVisualState";

/// <summary>
/// наименование поля для свойства <see cref="VisualState"/>
/// </summary>
private const string VisualStatePropertyName = "VisualState";

/// <summary>
/// свойство которое хранит текущее состояние контрола.
/// </summary>
public String VisualState
{
    get
    {
        return visualState;
    }
    set
    {
        visualState = value;
        OnPropertyChanged(VisualStatePropertyName);
    }
}

#endregion свойство VisualState

Сделаю только одно примечание: WelcomeVisualState - это значение ствойства по умолчанию. В нашем примере название VisualState'ов три: "WelcomeVisualState", "LoginingVisualState" и "LoggedInVisualState". Надеюсь из названия всё понятно. Но если нет, то сомнения должны развеять следующий раздел.

Немного картинок

Давайте предположим, что у нас есть форма входа, которая принимает три состояния взависимости от того:

1) пользователь только что открыл программу:

2) пользователь нажал кнопку [Вход]:

  

3) пользователь ввел имя и пароль и вход упешно произведен, форма скрывается:

Немного кнопок

Добавим реализацию ICommand в наш ViewModel. Нам потребуется три комагды: WelcomeCommand, LoginCommand, CancelCommand. Приведу код одной из команды, например, LoginCommand:

#region команда LoginCommand

/// <summary>
/// Команда LoginCommand
/// </summary>
public DelegateCommand LoginCommand
{
    get
    {
        return new DelegateCommand(() => this.LoginCommandExecute());
    }
}

/// <summary>
/// Процедура выполняет команды LoginCommand
/// </summary>
private void LoginCommandExecute()
{
    // выполнение команды Login
    // подразумеваем, что проверка имени и пароля пользователя
    // выполнена успешно и просто переключаем состояние.
    this.VisualState = "LoggedInVisualState";
}

#endregion // end команда LoginCommand  

Для создания команд я использую snippet, поэтому это не требует много времени. Тем более, что логика выполнения команд сведена к минимуму. Мы просто переключаем состояние объекта.

Всё дело в разметке (про XAML)

Пришло время поговорить про самое главное. Чтобы получить возможность управлять состоянием, надо каким-либо способом "опустить" это свойство на уровень ViewModel. Я воспользуюсь Attached Dependency Property. Это свойство я назвал VisualStates, и оно реализовано в подключенной сборке. Подключим его. Для начала подключим namespace:

xmlns:clb=http://schemas.calabonga.com/

теперь тут же его используем:

clb:VisualStates.CurrentState="{Binding VisualState}"

И теперь, чтобы понятно было приведу почти весь XAML:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    x:Class="MvvmViewState.MainPage"
    xmlns:clb="http://schemas.calabonga.com/"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    clb:VisualStates.CurrentState="{Binding VisualState}">

    <Grid Background="#FFE4E4E4">
       <!-- скрыть не важный код -->
    </Grid>
</UserControl>

Приведу пример свзяывания команд с кнопками, может быть это еще кто-то не знает:

<Button
    Content="Вход"
    Width="75"
    HorizontalAlignment="Center"
    Margin="5,0"
    Style="{StaticResource ButtonStyler}"
    Height="Auto"
    Command="{Binding LoginCommand}" />

Ключевые моменты отмечены жирным шрифтом.

Про MEF я чуть не забыл

Пока писал статью решил приплести еще и про MEF что-нибудь. Идельным вариантом будет использовать MEF для того, чтобы MainPageViewModel подключить к View. И пусть некоторые скажут, что из-за одного импорта подключать к проекту порядка 270 Кб дополнительных библиотек. Суть и правила статей этого сайта показать и научить как надо и как можно пользоваться технологимями. И пусть данный пример использования MEF - "стрельба по воробьям из пушки". Те, которым поможет данная статья хоть чем-нибудь, меня поймут.

Подключим те самые необходимые сборки для MEF:

Calabonga.Silverlight.Framework.dll
System.ComponentModel.Composition.dll

Пометим аттрибутами MainPageViewModel

[Export]
public class MainPageViewModel : ViewModelBase

А в файле MainPage.xaml.cs (Code Behind) получим импорт и свяжем с DataContext

[Import]
public MainPageViewModel ViewModel
{
    set
    {
        this.DataContext = value;
    }
}

Заключение

Кажется все точки на "ё" уже расставлены, можно запускать приложение. В следующей статье будет продолжение.