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; } }
Заключение
Кажется все точки на "ё" уже расставлены, можно запускать приложение. В следующей статье будет продолжение.