VisualStates в Silverlight или управление состоянием из ViewModel (MVVM)
WPF, MVVM, Silverlight | создано: 12.12.2010 | опубликовано: 12.12.2010 | обновлено: 13.01.2024 | просмотров: 4946
Не редко, возникает потребность переключить состояние 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;
}
}
Заключение
Кажется все точки на "ё" уже расставлены, можно запускать приложение. В следующей статье будет продолжение.