Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.12.21;
Скачать: CL | DM;

Вниз

Реализация интерфейса-мастера.   Найти похожие ветки 

 
Kolan ©   (2008-02-29 17:02) [0]

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

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

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

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

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

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

То есть что-то вроде:
if Sender.StepNumber = 2 then
 <сохранить даные>
if Sender.StepNumber = 4 then
 ShowMessge(<сохраненные данные>)


Мне ненравится такой подход из за if"а&#133

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


 
Игорь Шевченко ©   (2008-03-01 00:05) [1]


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


Конечный автомат.

type
 TAutomataState = (asStartState, asState1, asasState2, asCompleteState);

И анализировать этот State при действиях


 
Kolan ©   (2008-03-01 10:37) [2]

> И анализировать этот State при действиях

И сделать большой кейс? Ну я тоже самое и предложил, только в моем случае эти стейты хроняться в самих окнах как бы, и передаются по событию&#133

В стейтах плохо то, что трудно модифицировать такой автомат, я так пробовал.

Например бы такой:

case Stae of:
 asStartState:
 begin
   DoSmth;
   State := asState2;
 end;
 asasState2:
 begin
  State := CompleteState;
 end;
 asCompleteState:
 begin
   
 end;


Теперь если надо вставить asState1 между asStartState и asasState2, то надо менять ссылки &#151; неудобно.
Кроме того, нафига состояния если состояние однозначно связано с показываемым экраном, так почему бы не использовать сам экран(то есть форму у меня) в качестве состояния.

Если бы надо было бы только показывать экраны, то я бы сделал, наверно, такой файлик:

У окон есть номера.

[0.0]
Next=0.1
[0.1]
Next=0.2
Back=0.1

То есть некие мета данные&#133 Загружал бы его и при возникновении события &laquo;Back&raquo; в окне &laquo;0.1&raquo; я бы знал, что надо перейти в окно 0.1&#133
Но вот как быть с выполнением действий &#151; хз.

Плюс ко всму окон много и кейс будет здоровый&#133

Мож есть еще идеии?


 
b z   (2008-03-01 12:42) [3]


> Мож есть еще идеии?

Может взять компонент аля wizard?


 
Kolan ©   (2008-03-01 14:07) [4]

> Может взять компонент аля wizard?

А как он работает?

Визард (собссно есть свой) &#151; это страницы (на подобии PageControl) которые лежат на форме, то есть. Мне бы хотелось не создавать ненужные окна.


 
Kolan ©   (2008-03-03 10:09) [5]

Мож есть у кого еще идеи?


 
clickmaker ©   (2008-03-03 13:15) [6]


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

не вижу логики.
Чем if-то плох?


 
Kolan ©   (2008-03-03 13:35) [7]

Плохо тем, что неудобно добавлять или удалять экраны. Кроме того экранов много и иф будет большой &#151; неудобно. А еще есть как-бы разные ветки, несвязаные друг с другом, и все это будет в одном месте&#133


 
Семеныч   (2008-03-03 14:03) [8]

> Kolan ©   (03.03.08 13:35) [7]

Вместо if можно вызывать некий метод (или процедуру), но не напрямую, а по ссылке, которая хранится в некоей переменной и устанавливается конечным автоматом в зависимости от его текущего состояния.


 
Kolan ©   (2008-03-03 14:19) [9]

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

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


 
clickmaker ©   (2008-03-03 14:23) [10]


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

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


 
KSergey ©   (2008-03-03 14:27) [11]

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

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

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


 
Kolan ©   (2008-03-03 14:37) [12]

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

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

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


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

Сомневаюсь.


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

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

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

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

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

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

Оцените, плз&#133


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


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


Keep It Simple Stupid


 
oxffff ©   (2008-03-03 15:50) [14]


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


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

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.


 
Kolan ©   (2008-03-03 18:23) [15]

> Keep It Simple Stupid

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


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

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


 
oxffff ©   (2008-03-03 22:18) [16]


> Вопрос что писать в контроллере? При возникновении  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 22:58) [17]

> procedureTStepController.back(node:TStepNode);

Так это для одного &laquo;нода&raquo; а теперь представь что  их 25 и у всех по некст(бэк не беру тут проще) надо делать разные действия.
Так вот как ты это напишешь? Вот сама суть того, что я хз как сделать, чтобы попроще было&#133


 
oxffff ©   (2008-03-03 23:11) [18]


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


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

procedureTStepController.back(node:TStepNode);

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

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

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


 
Kolan ©   (2008-03-03 23:31) [19]

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

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

Вот как это делать ты прелагаешь &#151; непонятно.


 
oxffff ©   (2008-03-04 08:38) [20]


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


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-04 08:40) [21]

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


 
Kolan ©   (2008-03-04 08:42) [22]

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


 
KSergey ©   (2008-03-04 08:46) [23]

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

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


 
Kolan ©   (2008-03-04 09:02) [24]

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

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


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

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


 
oxffff ©   (2008-03-04 09:02) [25]


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


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

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


 
oxffff ©   (2008-03-04 09:07) [26]


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


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


 
Kolan ©   (2008-03-04 09:10) [27]

> Либо и т.д.

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

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


 
Kolan ©   (2008-03-04 10:38) [28]

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

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

 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;


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

Хоть и кейс но все в одном месте вроде&#133


 
Kolan ©   (2008-03-04 10:45) [29]

К когду контроллера прилагается State Chart&#133


 
KSergey ©   (2008-03-04 11:30) [30]

Зачем люди переносят заморочки, требуемые в С++ в дельфийский код? В дельфи можно и без всякие рукописных прибабахнутых фабрик объекты по указанному классу создавать, все для этого в компиляторе уже есть. Зачем отсебятину придумывать? Да и все сознаддые формы в Screen.Forms уже есть. Хотя, конечно, иногда бывает проще свой список поддерживать, только для требуемых форм, но тоже вопрос еще.

А использовать по тексту строковые константы - ваще капец. Описка в букве - и превед отладчег. Можно ведь и просто константами все описать, причем - перчислением, как и было сказано. Они - integer, их проверять быстрее у процессора выходит, да и описки - компилятором все отловятся.

В общем на мой вкус - много лишних сущностей. Но это наверняка субъективно.


 
Kolan ©   (2008-03-04 11:56) [31]

> Зачем люди переносят заморочки, требуемые в С++

Я его с трудм читаю&#133

> указанному классу создавать

А кто укажет его?


> А использовать по тексту строковые константы &#151; ваще капец.

Ладно, а где их хранить, приведи пример&#133


 
KSergey ©   (2008-03-04 12:17) [32]

> Kolan ©   (04.03.08 11:56) [31]
> > указанному классу создавать
> А кто укажет его?

Тот же контроллер, но сразу нужный класс формы шага. И все. Никаких рукописных фабрик классов не надо.

> Kolan ©   (04.03.08 11:56) [31]
> Ладно, а где их хранить, приведи пример…

А чем [1] не нравится?


 
Kolan ©   (2008-03-04 12:37) [33]

> А чем [1] не нравится?

Я не поняимаю как конкретно рпелагается это решнение использовать, как инстанцировать и убивать формы&#133


 
KSergey ©   (2008-03-04 12:44) [34]

> Kolan ©   (04.03.08 12:37) [33]
>  как инстанцировать и убивать формы…

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=342
раздел "Лень, как источник вдохновения."


 
Kolan ©   (2008-03-04 12:50) [35]

> [34] KSergey ©   (04.03.08 12:44)

Ты че издеваешься что ли? Ты думаешь я незнаю как создать форму что ли? Ты покажи напримере как ты сделаешь с состояниями, я же мысли не читаю.

&laquo;Используй перечисление&raquo; &#151; это что, то что их как-то можно использовать я понимаю и так? Тут же вопрос именно как все организовать&#133


 
Kolan ©   (2008-03-04 12:54) [36]

А делать такую хрень:
ListClass : TListClass  = (TEdit , TButton , TCheckBox , TLabel ) ;

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

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


А вот создание:
function TWindowsFactory.CreateWindow(AWindowCode: string;
 AControllerInterface: IControllerInterface;
 AOtherInterfacesObjetc: TObject): TCustomTouchForm;
var
 FormClass: TCustomTouchFormClass;
begin
 Result := nil;
 FormClass := FFormsClasses.FindFormClass(AWindowCode);
 if FormClass <> nil then
   Result := FormClass.Create(AWindowCode, AControllerInterface, AOtherInterfacesObjetc);
end;


 
oxffff ©   (2008-03-04 14:05) [37]


> Kolan ©   (04.03.08 12:54) [36]
> А делать такую хрень:
> ListClass : TListClass  = (TEdit , TButton , TCheckBox ,
>  TLabel ) ;


А у тебя тоже все статично. Захочешь добавить будешь править свой

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;


 
Kolan ©   (2008-03-04 14:17) [38]

> А у тебя тоже все статично. Захочешь добавить будешь править
> свой

Ну а как без этого, хоть ListClass не править. Этим мне и ненравится кейс&#133 О чем собссно и вся ветка с самого начала&#133


 
oxffff ©   (2008-03-04 14:48) [39]


> Kolan ©   (04.03.08 14:17) [38]


Перечитай сначала. :)


 
Kolan ©   (2008-03-04 14:53) [40]

> А у тебя тоже все статично.

Ну а как без этого, хоть ListClass не править.


> Захочешь добавить будешь править свой

Этим мне и ненравится кейс… О чем собссно и вся ветка с самого начала…

Имел это ввиду&#133



Страницы: 1 2 3 вся ветка

Текущий архив: 2008.12.21;
Скачать: CL | DM;

Наверх




Память: 0.6 MB
Время: 0.019 c
2-1226472613
Alex7
2008-11-12 09:50
2008.12.21
TShiftState из какого модуля?


15-1224771691
Василий Жогарев
2008-10-23 18:21
2008.12.21
Проблемы с кодировкой двух баз...


2-1226656814
Serega87
2008-11-14 13:00
2008.12.21
Количество веток в ветке


2-1226181426
Scary
2008-11-09 00:57
2008.12.21
StringGrid и полоса прокрутки


15-1224652490
Риппер
2008-10-22 09:14
2008.12.21
The instruction at "0x77f5d61b" referenced memory at "0x00000010"