среда, 20 мая 2009 г.

Пример создания простого конечного автомата

Для создания конечного автомата можно воспользоваться уже существующим шаблоном Visual Studio - "Консольное приложение рабочих процессов конечного автомата".
В результате будет создано консольное приложение включающее конечный автомат и класс содержащий метод Main в который уже добавлен весь код необходимый для запуска конечного автомата.
Основным элементом для построения конечных автоматов является компонент: StateActivity, который и задает состояния конечного автомата.
В качестве примера давайте рассмотри пример конечного автомата, эмитирующего работу двери с кодовым замком:
Состояниями указанного автомата будут являться:
1. Начальное состояние
2. Дверь закрыта, замок защелкнут
3. Замок проверяет код
4. Дверь закрыта, замок открыт
5. Дверь открыта
События, которые должен принимать автомат, будут следующие:
1. Набор кода
2. Код введен правильно
3. Код введен не правильно
4. Открывание двери
5. Закрывание двери
Схематически данный автомат можно изобразить следующим образом:


Добавив 4 состояния в конечный автомата (начальное состояние добавлено автоматически), получим следующий автомат:

Обычно состояние конечного автомата состоит из трех программных компонентов:
1. StateInitializationActivity – выполняет операции при переходе автомата в состояние.
2. Один или несколько EventDrivenActivity – отвечают за перехват сообщений из внешнего мира
3. StateFinalizationActivity – выполняет операции перед тем как автомат покинет текущее состояние.
Ни один из указанных компонентов не является обязательным.
Для начала поместим в Workflow1InitialState два компонента: StateInitializationActivity и StateFinalizationActivity (с компонентом EventDrivenActivity мы познакомимся в следующий раз).
В результате получим:

Двойной клик на любой Activity добавленной в состояние открывает линейный workflow в котором задаются действия выполняемые при передачи управления данному Activity.

Добавим компонент CodeActivity, который позволяет выполнить закрепленный за ним обработчик. Добавим в него код вида:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Дверь создана и готова к работе");
}


* This source code was highlighted with Source Code Highlighter.


Для перехода в другое состояние используется SetStateActivity, состояние в которое необходимо перейти в данном Activity задается свойством: TargetStateName. В нашем случае его необходимо задать в CloseClose:


Добавим в состояние CloseClose StateInitializationActivity аналогичный Workflow1InitialState указав в качестве выполняемого кода следующий метод (в класс Workflow1 также добавляем строковое поле класса string):
string code = "";

private void codeActivity2_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Введите код:");
code = Console.ReadLine();
}


* This source code was highlighted with Source Code Highlighter.
У SetStateAtivity укажем в качестве TargetStateName состояние TestKey.
В состоянии TestKey в StateInitializationActivity помести проверку ключа на равенство «111». Для этого воспользуемся компонентом IfElseActivity. Все ветви кроме одной данного Activity должны иметь проинициализированным свойство Conditon. В неашем примере установим значение этого свойства в «Declarative Rule Condition» (т.е. само Activity проверяет некое декларативное правило задаваемое при помощи мастера доступного в свойстве ConditionName. Для провекри на соответствие введенного кода введм следующее правило: this.code == "111".
После чего в левую ветвь поместим SetStateActivity с переходом на CloseOpen, а в правую с переходом на CloseClose.

Я думаю, остальные состояния трудности при написании не вызовут. Попробуйте написать их самостоятельно. В результате должно получится что то похожее:

Запускаем и проверяем работу конечного автомата.
Для ленивых работоспособный проект можно скачать здесь.

вторник, 19 мая 2009 г.

Разработка выпадающего меню: примеры и лучшая практика

http://blog.yed-prior.com/archives/47

Многопоточный доступ в WPF

Одной из проблем при работе в WPF с визуальными компонентами заключается в том, что доступ к ним из любого потока отличного от их породившего приводит к исключению.

Для примера возьмем простое приложение, со следующим интерфейсом:

При нажатии на кнопку "Старт" запускается длительная операция выводящая состояние процесса в ProgressBar.


Код метода длительной операции для простоты возьмем вот такой:

private int LongOperation()
{
for (int i = 0; i < 100; i++)
{
// Имитация полезной работы
Thread.Sleep(100);
// Сообщаем в визуальную часть, что часть работы выполнена
SetProgressBarValue(i + 1);
}
return 0;
}


* This source code was highlighted with Source Code Highlighter.


Обработчик клика на кнопке:


private void startButton_Click(object sender, RoutedEventArgs e)
{
Func<int> operation = LongOperation;
operation.BeginInvoke(null, null);
}


* This source code was highlighted with Source Code Highlighter.

При попытке реализовать метод SetProgressBarValue в лоб:


private void SetProgressBarValue(int newValue)
{
workProgress.Value++;
}


* This source code was highlighted with Source Code Highlighter.

мы получим InvalidOperationException

Для решения этой проблемы необходимо воспользоваться замечательным классом Dispatcher, переписав с его помощью код, мы получим:


// Делегат для перевызова метода SetProgressBarValue через диспетчер
private delegate void SetProgressBarValueHandler(int newValue);

private void SetProgressBarValue(int newValue)
{
// Проверяем совпадает ли поток диспетчера с потоком вызвавшим метод
if (Dispatcher.Thread == Thread.CurrentThread)
{
// Все замечательно :) меняем значение прогресбара
workProgress.Value = newValue;
}
else
{
// Нет :( все плохо :( перезапускаем метод в потоке диспетчера
Dispatcher.Invoke(new SetProgressBarValueHandler(SetProgressBarValue), new object[] { newValue });
}
}


* This source code was highlighted with Source Code Highlighter.

Вуаля, все заработало :)

Проект можно скачать здесь: MultithreadingWPF.rar