Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Реализация интерфейса-мастера.


Страницы:   1 2 3 4 5

Kolan ©   (2008-02-29 17:02:00) 

Здравствуйте,
 Разрабатываю программу для тач скрина. Интерфейс (все представляют терминал оплаты) состоит из нескольких экранов, которые работают как мастер.

Вопрос как это реализовать?

Как показывать окна я придумал, имхо, надо использовать некие мета данные, где описаны какие окна должны открываться.

А вот как быть с остальными (кроме показа окон) действиями?

Допустим есть 4 шага. На шаге 2 пользователь вводит данные, а на шаге 4 их надо обработать как-то.

У меня есть идея, но она мне ненравится из за условного выражения.
Чтобы перейти на сл экран пользователь жмет «далее». Соотв при нажатии далее возникает событие «Далее(Sender)», где сендер — это окно где нажали далее. Какое следующее окно паказать написанно в мета данных, а что сделать определяется по Sender.

То есть что-то вроде:
if Sender.StepNumber = 2 then
 
if Sender.StepNumber = 4 then
 ShowMessge()


Мне ненравится такой подход из за if'а…

А что вы посоветуете?


Kolan   (2008-03-04 10:38:00) [61]

Вроде получилось неплохо:

Есделал такого предка:

 TCustomTouchForm = class(TForm)
 private
   FWindowCode: string;
   FControllerInterface: IControllerInterface;
   FOtherInterfacesObjetc: TObject;
 protected
   function GetControllerInterface: IControllerInterface;
 public
   {С помощью этой операции можно узнать поддерживает или нет форма код.
    Таких кодов может быть несколько.}
   class function SupportsWindowCode(AWindowCode: string): Boolean; virtual; abstract;

   {При создании формы ей указывают конкретный её код.
    Если поддерживать форма может несколько кодов, то создается с конкретным.
    А с каким именно знает тот, кто её создает.}
   constructor Create(AWindowCode: string;
     AControllerInterface: IControllerInterface;
     AOtherInterfacesObjetc: TObject); reintroduce;
   property WindowCode: string read FWindowCode write FWindowCode;
 end;

 TCustomTouchFormClass = class of TCustomTouchForm;


Контроллер такой:

procedure TSystemController.Act(ASenderWindowCode,
 APerformedActionName: string);
begin
 if ASenderWindowCode = 'Start' then
 begin
   ShowWindow('0.0');
 end;                

 if ASenderWindowCode = '0.0' then
 begin
   if APerformedActionName = 'Settings' then
     ShowWindow('7.0');
 end;

 if ASenderWindowCode = '7.0' then
 begin
   if APerformedActionName = 'Back' then
     ShowWindow('0.0');    
 end;
 
end;


Вот показ формы:
procedure TSystemController.ShowWindow(AWindowCode: string);
var
 TouchForm: TCustomTouchForm;
begin
 TouchForm := GetWindowsFactory.GetWindow(AWindowCode, Self, nil);
 if Assigned(TouchForm) then
 begin
   TouchForm.Show;
   TouchForm.FormStyle := fsStayOnTop;
 end
 else
   raise EFormNotFoundException.Create(AWindowCode);
end;


Каждая форма регистрирует совй класс в фабрике:
initialization
 GetWindowsFactory.AttachTouchFormClass(TSettingsForm);


Фабрика создает окна так:

function TWindowsFactory.GetWindow(AWindowCode: string;
 AControllerInterface: IControllerInterface;
 AOtherInterfacesObject: TObject): TCustomTouchForm;
begin
 Result := nil;
 {Найи форму в кэше.}
 Result := FFormsList.FindForm(AWindowCode);
 if Assigned(Result) then
 begin
   FFormsList.DeleteAllFormsAfterGiven(Result);
 end
 else
 begin
   {Если форма не найдена, то создать её.}
   Result := CreateWindow(AWindowCode, AControllerInterface, AOtherInterfacesObject);
   if Assigned(Result) then
     FFormsList.Add(Result);
 end;
end;


А еще на каждой форме лежит экше лист в OnExecute которого написано:
procedure TSettingsForm.MainActionListExecute(Action: TBasicAction;
 var Handled: Boolean);
begin
 GetControllerInterface.Act(WindowCode, Action.Name);
end;


Как бы сделать так, чтобы у предка всех форм это экшен лист сразу бы лежал на форме?

Хоть и кейс но все в одном месте вроде…


Kolan   (2008-03-04 09:10:00) [62]

> Либо и т.д.

Либо, либо, вариантов масса… а конкретного примера тен.

Ладно шас выложу свой вариант, будите ругать :).


oxffff   (2008-03-04 09:07:00) [63]


> моему, именно так стоит на это все смотреть. Т.е. экраны
> шагов - друг про друга пусть не знают, однако меняют состояние
> одного общего объекта (опять же не буквально в терминах
> ООП). Может и не экраны меняют его состояние, а из них просто
> информация вычитывается. Тут смотреть надо как связей меньше.
>


Это и есть контракт. Но это все разновидность [25].


oxffff   (2008-03-04 09:02:00) [64]


> Kolan ©   (04.03.08 08:42) [22]
> И вообще непонятно как в этом случае ты на шаге 2 что-то
> запомнишь, а на шаге 4 что-то с этим сделаешь, если формы
> ничего не знают про друг друга…


А что мешает тебе определить шаги в одном модуле например и использовать shared переменные.
Либо при создании их ссылаться друг на друга.
Либо и т.д.

Если шаги взаимодействуют они это делают согласно контракта.
Какой контракт это выбор за тобой.


Kolan   (2008-03-04 09:02:00) [65]

> как обычно — сценарий меняется по ходу пьессы.

«Допустим есть 4 шага. На шаге 2 пользователь вводит данные, а на шаге 4 их надо обработать как-то.» © [0] Kolan


> Может и не экраны меняют его состояние, а из них просто
> информация вычитывается.

Вот тут я и прошу конкретный пример, бо всплывают проблеммы… Все написанное я понимаю.


KSergey   (2008-03-04 08:46:00) [66]

как обычно - сценарий меняется по ходу пьессы.
Это уже не интересно становится. Неужели нельзя все сразу было рассказать?

По поводу "переменных на разных шагах" - ну так это для пользователя эти шаги - разные. Но что мешает все состояние хранить в одном месте, в общей куче от всех "шагов"? По сути ведь есть некий объект (здесь обьек - условность, не в смысле ООП), управление которым исключительно для удобства (не более!) разнесено на несколько "шагов" вместо одного огромного экрана.
По-моему, именно так стоит на это все смотреть. Т.е. экраны шагов - друг про друга пусть не знают, однако меняют состояние одного общего объекта (опять же не буквально в терминах ООП). Может и не экраны меняют его состояние, а из них просто информация вычитывается. Тут смотреть надо как связей меньше.


Kolan   (2008-03-04 08:42:00) [67]

И вообще непонятно как в этом случае ты на шаге 2 что-то запомнишь, а на шаге 4 что-то с этим сделаешь, если формы ничего не знают про друг друга…


Kolan   (2008-03-04 08:40:00) [68]

То есть ты предлагаешь логику работы из контроллера распылить по всем окнам, так?


oxffff   (2008-03-04 08:38:00) [69]


> Вот как это делать ты прелагаешь — непонятно.


function TStepNode.Back:boolean;
begin
BeforeMoveBack;
result:=controller.back(self);
AfterMoveBack(result);
end;

procedureTStepController.back(node:TStepNode);
begin
if AllowBack and node.AllowBack  then     // paranoiac AllowBack :)
 begin
  Node.Detach;   // on Node
 setStepNode(previous); // <- где previous может быть не предыдущий,
                                 а тот например который Skipped, либо другое    поведение на твое усмотре
  previous.Attach;
 end;
end;

Что еще не понятно?


Kolan   (2008-03-03 23:31:00) [70]

Да переход фигня, кароме него работу еще надо сделать.

На 2 шаге запомнить в переменную на 4 что-то с ней сделать, на 7 запустить поток и туда её засунуть…

Вот как это делать ты прелагаешь — непонятно.


oxffff   (2008-03-03 23:11:00) [71]


> Kolan ©   (03.03.08 22:58) [17]


У конктроллера есть связный список переходов на одном уровне.
Его уведомляет элемент о Back переходе, вызовом

procedureTStepController.back(node:TStepNode);

У контролер зафиксировано предудущее состояние (либо оно вычисляется). Он совершает на него переход.

Динамическая структура, без статической привязки.

Что тебе не нравится?


Kolan   (2008-03-03 22:58:00) [72]

> procedureTStepController.back(node:TStepNode);

Так это для одного «нода» а теперь представь что  их 25 и у всех по некст(бэк не беру тут проще) надо делать разные действия.
Так вот как ты это напишешь? Вот сама суть того, что я хз как сделать, чтобы попроще было…


oxffff   (2008-03-03 22:18:00) [73]


> Вопрос что писать в контроллере? При возникновении  Back
> и Forward?


function TStepNode.Back:boolean;
begin
result:=controller.back(self);
end;

procedureTStepController.back(node:TStepNode);
begin
if AllowBack and node.AllowBack  then     // paranoiac AllowBack :)
  begin
  Node.Detach;   // on Node
  setStepNode(previous); // <- где previous может быть не предыдущий,
                                  а тот например который Skipped, либо другое    поведение на твое усмотре
  previous.Attach;
  end;
end;

end;


Kolan   (2008-03-03 18:23:00) [74]

> Keep It Simple Stupid

Вроде просто, а вы предлогаете что? Не [1] я читал, как конкретно предлагается реализовать? Где хранить состояния? Как узнавать что следующим показывать?


> А чем тебе двунаправленный список не угодил?
> Объект в элементе списка поддерживает интерфейс.

Так и хотел сделать примерно, только операций много не только «Назад» «Далее», поэтому интерфейс несколько другой.
Вопрос что писать в контроллере? При возникновении  Back и Forward?


oxffff   (2008-03-03 15:50:00) [75]


> А что вы посоветуете?


А чем тебе двунаправленный список не угодил?
Объект в элементе списка поддерживает интерфейс.

TStepNode=class
function IsDataValid:boolean;Virtual;abstract; вызывается controlleroм для
    {
   
    }

function Back:integer;Virtual;abstract; делегирует вызов контролер .передавая себя в параметре.
function Forward:integer;Virtual;abstract; делегирует вызов контролеру.
передавая себя в параметре.

function SkipSteps(stepCount:integer):Boolean делегирует вызов контролеру который проверяет уж каждого пропущенного IsDataValid. И в случае успеха устанавливает текущий шаг.

Рассуждая так далее можно сделать вложенные шаги и т.д.

Игорь Шевченко ©   (03.03.08 15:07) [13]

Keep It smart and simple.


Игорь Шевченко   (2008-03-03 15:07:00) [76]


> Оцените, плз…


Keep It Simple Stupid


Kolan   (2008-03-03 14:37:00) [77]

> и часто они будут добавляться/удаляться?
> все равно же одним добавлением не ограничится? Нужно же
> еще какой-то код писать, так ведь?

Опыт показал что может 1-2 добавится/исчезнуть.

Угу, ессно код писать надо, если бы ненадо было, то я, как говорил, использовал бы мета данные и редактировал бы только их.
В том и дело, что код писать надо.


> элементарно вставлять новые шаги в середину визарда;

Сомневаюсь.


> если визард тупо-глупо линейный

Визард восе не тупой.

Для варианта с кэйсом я уже придумал все:

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

Тот класс, у кого и будет всеь этот кейс будет по полученному коду и операции делать что-то, а потом брать инфу из мета данных, как в [2], и показывать следующиее окно.

Валидация будет внутри форм.

Оцените, плз…


KSergey   (2008-03-03 14:27:00) [78]

> Kolan ©   (01.03.08 10:37) [2]
> Теперь если надо вставить asState1 между asStartState и
> asasState2, то надо менять ссылки — неудобно.

Вот именно поэтому Игорь и говорит про конечный автомат.
Весь этот "большой case", которого вы так опасаетесь в итоге просто вынесется в однельный управляющий unit, который будет это самый case содержать и создавать требуемые формы при надобности в зависимости от кнопок, нажатых пользователем (анализировать результат ShowModal).
Такой подход позволит:
а) элементарно вставлять новые шаги в середину визарда;
б) легко реализовать кнопку "пошли все на!" на k., шаге;
в) в дополнение к пункту а - построить в одном месте логику "Шаг X из Y" (при условии поддержки в формах)
г) главная засада взардов - иногда возникающая необходимость пропускать некоторые шаги в зависимости от пользовательского ввода. Здесь этот обычно довольно некрасивый и развесистый код реализуется хотя бы в одном точно месте.

ну и главное: если визард тупо-глупо линейный, т.е. только с кнопочками "Back" / "Next" / "Cancel" - то можно и вовсе прописать классы форм в перечислителе - тогда и вовсе никаких Case не понадобится.


clickmaker   (2008-03-03 14:23:00) [79]


> Плохо тем, что неудобно добавлять или удалять экраны.

и часто они будут добавляться/удаляться?
все равно же одним добавлением не ограничится? Нужно же еще какой-то код писать, так ведь?


Kolan   (2008-03-03 14:19:00) [80]

> которая хранится в некоей переменной

Лучьше уж стратегию использовать, только как. Хотелось бы более конкретный пример.


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо