Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2008.12.21;
Скачать: [xml.tar.bz2];

Вниз

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

 
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


 
KSergey ©   (2008-03-04 15:58) [41]

Я не знаю что еще можно добавлять
Я вроде все написал.

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

Про перечисления - я лишь говорю о том, что не надо тут использовать строковые константы. Просто не по делу тут строки.

Короче принципиально архитектурно я не вижу как это все можно изменить так, чтобы при добавлении/изменении ничего еще и не править :)


 
Kolan ©   (2008-03-04 16:00) [42]

> ничего

Не ничего, а мак. просто. :) Строковые мож и прям не поделу&#133


 
KSergey ©   (2008-03-04 16:12) [43]

> Kolan ©   (04.03.08 16:00) [42]
> Не ничего, а мак. просто. :)

Так опять же - это от ситуации зависит.
Если переход межту отдельными шагами весьма не тривиален - то от case - не отвертеться. Ну либо от каких-либо метаданных, которые по сути будут этот case описывать. Усложняя "ядро".

Если же логика простейшая - то [11].


 
oxffff ©   (2008-03-04 19:40) [44]


> Усложняя "ядро".


Да нет здесь никакого усложнения вся логика перехода может быть посредоточена NodeStep, который запрашивает у контролера нужный переход по строке, по классовому типу и т.д.
Причем переход может быть статичен,
либо динамический передача параметра перехода (следующего шага) в конструкторе. Задача контролера выполнить действие по запросу нода, предоставить экземпляр типа по строке или классовому типу. И все. Причем действительно контроллер отвязан от логики (можно не перекомпилировать)
А логика задается в Шагах.


 
oxffff ©   (2008-03-04 19:47) [45]


> oxffff ©   (04.03.08 19:40) [44]


А шаги задаются на лету (DLL).
Например шаг
  Выбор директории (класс TStepDirectorySelection(TAbstractStepClass)), где
TAbstractStepClass=class of TAbstractStepClass;

Controler.Add(TStepDirectorySelection.create(TStepConfirm)    <-параметр следующий шаг.
Причем не обязательно определять порядок создания.
Шаги можно доинициализировать в порядке добавления других шагов (естественно предусмотрев это в виде контракта).


 
oxffff ©   (2008-03-04 19:48) [46]


> oxffff ©   (04.03.08 19:47) [45]

Sorry
(TAbstractStepClass)) =  (TAbstractStep))
TAbstractStepClass=class of TAbstractStep;


 
Игорь Шевченко ©   (2008-03-04 21:42) [47]

Автор, а у тебя сколько шагов и сколько различных форм вызывается ?


 
Kolan ©   (2008-03-05 08:59) [48]

> Автор, а у тебя сколько шагов и сколько различных форм вызывается
> ?

Где-то 25 ±5.
Из них 1-2 используются в нескольких местах.


 
Игорь Шевченко ©   (2008-03-05 10:21) [49]

Kolan ©   (05.03.08 08:59) [48]

Это как понимать ? Некоторое действие будет выполняться за 25-30 шагов ?

Никому плохо не станет ?


 
Kolan ©   (2008-03-05 10:22) [50]

Нет, это всего возможных. Самое доинное &#151; 5 шагов.


 
Игорь Шевченко ©   (2008-03-05 11:44) [51]


> Самое доинное — 5 шагов.


И чего ты маешься ? Классы-фигасы. Сделал case и радуешься. Код вполне сопровождаемый получится.

Keep It Simple Stupid


 
TStas ©   (2008-03-05 16:12) [52]

Простите чайника, может, я чего-то не понимаю, но почему нельзя PageControl использовать? И, конечно, Sender, а не case? Это же, как я понял, получится что-то вроде телефонной платилки? Просто я когда-то задавал аналогичный вопрос и мне ответили, что это и есть общепринятый способ. TabVisible := False и все. И тут хоть 100 шагов можно вставить, если пользователь не уйдет. И последовательность как угодно менять. и обращаться к ним хоть через Sender, хоть через ActivePageIndex


 
Kolan ©   (2008-03-05 16:22) [53]

> PageControl использовать

Нехочется держать все в памяти. Аппарат гораздо менее мощный чем платилка.


 
KSergey ©   (2008-03-05 17:37) [54]

> Kolan ©   (05.03.08 16:22) [53]
> Нехочется держать все в памяти.

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


 
TStas ©   (2008-03-05 19:01) [55]

Но все равно. PageControl - по сути, те же формы. 25 - это очень даже пристойное число и сразу их создать, если прога никогда не выключается - вполне разумное решение. Я верю, что мощнее, но принцип-то тот же: нажмите кнопочку 1, посмотрите, сколько вы ошибок наделали, вернитесь к шагу 1 и т. д. Это тот же принцип, что и мастера установки чего-нибудь.
>если в память влезут Так ведь сама форма (экземпляр) не очень много занимает в памяти, а класс формы там так и так висит. А потом ну не байты же вы считаете.


 
Kolan ©   (2008-03-05 20:33) [56]

> что круглые сутки отображать одну из этих 25 форм

Не это некй измеритель. То есть формы сменяют друг друга. Программа то раьботает с БД, то с ком портом&#133


> Сделал case и радуешься.

Я его и сделал, только вот строки использовал в кач состояни(наверно переделаю). А как предлагается?


> Но все равно. PageControl


У меня есть даже самописный компонент такой. Там вся логика получается в событиях OnExecute (так работают большиство визардов) и.т.д. Опыт паказал, что код такой трудно сопроваждать.
Кроме того в визаде только далее и назад, ну отмена, а тут у меня хз сколько событий будет.


 
Германн ©   (2008-03-06 03:31) [57]


> Kolan ©   (05.03.08 20:33) [56]
>
> > что круглые сутки отображать одну из этих 25 форм
>
> Не это некй измеритель. То есть формы сменяют друг друга.
>  Программа то раьботает с БД, то с ком портом…
>
>

Ну и пусть формы меняются. Тем более KSergey ©   (05.03.08 17:37) [54].

А при чём тут БД или Сом-порт?

>
> У меня есть даже самописный компонент такой. Там вся логика
> получается в событиях OnExecute (так работают большиство
> визардов) и.т.д. Опыт паказал, что код такой трудно сопроваждать.
>
>

No comments!


 
Kolan ©   (2008-03-06 09:24) [58]

> No comments!


Почему?


 
KSergey ©   (2008-03-06 09:33) [59]

> Kolan ©   (06.03.08 09:24) [58]
> > No comments!
> Почему?

Сам же написал: "такой трудно сопроваждать". И это понятно, т.к. вся логика разбросана по разным местам.


 
Kolan ©   (2008-03-06 09:42) [60]

> Сам же написал: &laquo;такой трудно сопроваждать&raquo;. И это понятно,
> т.к. вся логика разбросана по разным местам.

А, я думал он наоборот считает что это нормально&#133

Я честно так и не понял что конкретно вы предлагаете.

Давайте на простом примере.

Есть 3 формы.

TForm1, TForm2, TForm3


Вы говорите используй состояния &#151; ок. Есть 3 состояния:

TStates =
(
 sState1,
 sState2,
 sState3
);


Как предлагается все это увязать? Пусть из формы 1 надо вытащить и сохранить переменную S и передать её TForm3.

Варик с сразу созданными формами еще плох тем, что придется делать очищение их.


> Keep It Simple Stupid

Вот клянусь именно этого и хочу, но не получается :(


 
oxffff ©   (2008-03-06 10:36) [61]

NO COMMENTS. :)


 
Семеныч   (2008-03-06 10:49) [62]

> Kolan ©   (06.03.08 09:42) [60]

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

Не мудри. Из пушки по воробьям стреляешь. Это не та задача, где требуется Гради Буча сначала проштудировать.


 
clickmaker ©   (2008-03-06 11:06) [63]


> Пусть из формы 1 надо вытащить и сохранить переменную S
> и передать её TForm3.

а какие проблемы?
массив параметров, индекс = шаг
для пущей универсальности в структуре, которая хранит параметры отдельно взятого шага, можно юзать Variant или что-то типа Name = Value


 
clickmaker ©   (2008-03-06 11:22) [64]


> [63] clickmaker ©   (06.03.08 11:06)
>
> > Пусть из формы 1 надо вытащить и сохранить переменную
> S
> > и передать её TForm3.

кстати, о птичках.
Почему бы не сделать список данных форм, ключом в котором будет класс формы (или имя класса)?
Форма при по нажатию на Next просто добавляет/обновляет данные в этот список.
Тогда на любом шаге можно обратится к данным одной из предыдущих форм вызовом Data := GetFormData(Form1).
Где data - наследник абстрактного класса наподобие TFormData. Из экземпляров наследников этого класса и состоит список.
форма 3 знает, что ей надо получить именно TForm1Data:
var
 Data: TForm1data;

Data := GetFormData("Form1");


 
KSergey ©   (2008-03-06 13:23) [65]

> Семеныч   (06.03.08 10:49) [62]
> Ветка держится неделю. Ей-богу, за это время можно было
> уже весь визард написать.

Вот все о том же пытаются сказать.
Блин, давно бы уже написал хоть как-нибудь - и было бы видно: хорошо это или нет.
Бесполезно так вот на расстоянии получить ответ, который стопудово будет самым правильным.
Хотя я помнимаю это желание, сам им часто страдаю.


 
KSergey ©   (2008-03-06 13:30) [66]

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


 
KSergey ©   (2008-03-06 13:52) [67]

> Kolan ©   (06.03.08 09:42) [60]
> Есть 3 формы.
>
> TForm1, TForm2, TForm3
>
> Есть 3 состояния:
>
> TStates = ( sState1,  sState2,  sState3 );
> Пусть из формы 1 надо вытащить и сохранить переменную S и передать её TForm3.

Я не понимаю что тут может быть непонятно. Лучже бы ваш пример глянуть.
Вот мое видение, очень схематично. Предполагаю (для краткости), что все формы созданы ранее. Конструкции пишу очень на память, в хелп за синтаксисом лазить лень. Так что формально может и не компилируемый код.
mrNext, mrBack, mrCancel - просто доопретелил бы свои, отсутствующие.

Расширим TStates:

TStates = ( sState1,  sState2,  sState3 , sStateCanceled, sStateEnded );

Ну и код некоей процедуры, запускаемой при старте программы:

CurState: TStates;

CurState := sState1;

while ((CurState <> sStateCanceled) OR (CurState <> sStateEnded))
begin
  case CurState
    sState1:
       begin
          Res := Form1.ShowModal;
          if Res <> mrCanceled then
            DataModule.ParameterS = Form1.S;
          if Res = mrNext then CurState := State2;
       end;
    sState2:
       begin
          Res := Form2.ShowModal;
          if Res = mrNext then CurState := State3
          else if Res = mrBack then CurState := State1;
       end;
    sState3:
       begin
          Form3.S := DataModule.ParameterS;
          Res := Form3.ShowModal;
          if Res = mrBack then CurState := State2;
       end;
end;

// Здесь в CurState имеем sStateCanceled или sStateEnded
// Делаем что нам надо после завершения визарда, если его завершение вообще предполагается.


Этот вариант для случая, когда нужны непоследовательные варианты перехода между шагами, зависящие от введенных данных (здесь просто не показано для простоты). Добавляется в логику if Res = ... then CurState := ...

Если же все перемещения только тупо вперед-назад - то проще массив ссылок на формы (или классы) - и тупо индекс гонять. Или если перечисление (классов, например) - то к переменной типа перечисление применимы Inc/Dec (если не ошибаюсь) и возможность определения граничный это элемент или нет.


 
KSergey ©   (2008-03-06 13:54) [68]

Да, не факт, что sStateCanceled, sStateEnded  есть смысл добавлять в общую кучу состояний
Может удобнее отдельный флажек завести.


 
Kolan ©   (2008-03-06 15:05) [69]

> По поводу передаваемых данных &laquo;из формы в форму&raquo; &#151;

Из формы в ворму я ниче не буду передавать. Буду использовать посетителя, то есть форма для работы будет требовать интерфейсы. Это метод проверен, и он оч. удобен.


> Блин, давно бы уже написал хоть как-нибудь

Я пока другие места прорабатывал&#133


> [67] KSergey ©   (06.03.08 13:52)

Вот, пример которого я хотел :)


if Res <> mrCanceled then
           DataModule.ParameterS = Form1.S;
         if Res = mrNext then CurState := State2;

В вот этих результатов (mrCanceled, mrNext&#133) может быть еще несколько, на каждой форме разное кол-во. На одной &laquo;Добавить&raquo;, на другой &laquo;Настройки&raquo; . Придется при возникновении нового дополнять перечисление.

В итоге вроде у меня получается почти тоже самое, только строки типа "0.1" надо заменить на константы. А вот с возврящаемыми значениями у меня лучьше имхо, они прям из экшенов беруться&#133

Вообще пример я понял. Благодарю, пригодится.


 
Kolan ©   (2008-03-06 15:10) [70]

Все, я все понял, сделаю как KSergey, только с возвращ. занчениями сделаю как у меня&#133
Я понял в чем тут плюс, формами легко управлять. Благодарю еще раз :)


 
Kolan ©   (2008-03-06 16:21) [71]

sState2:
      begin
         Res := Form2.ShowModal;
         if Res = mrNext then CurState := State3
         else if Res = mrBack then CurState := State1;
      end;
   sState3:
      begin
         Form3.S := DataModule.ParameterS;
         Res := Form3.ShowModal;
         if Res = mrBack then CurState := State2;
      end;


Интересно, а как при возврашении назад из шага 3 в шаг 2 показать форму? Она же уже показана, причем модально&#133


 
clickmaker ©   (2008-03-06 16:23) [72]


> как при возврашении назад из шага 3 в шаг 2 показать форму?
> Она же уже показана, причем модально

для каждой страницы - форма модально?
почему не одна форма с меняющимися фреймами?


 
Kolan ©   (2008-03-06 16:25) [73]

> [72] clickmaker ©   (06.03.08 16:23)
> почему не одна форма с меняющимися фреймами?

Потому, что ты понимаешь как это сделать, а я нет. Приведи пример&#133


 
clickmaker ©   (2008-03-06 16:33) [74]


> Приведи пример…

dfm сюда выложить? Злой ты ))
понаделай фреймов, а потом по нажатию кнопок на форме убирай один, создавай и показывай другой
Parent := Form, ну и тыпы

впрочем, непонятно как [71] сочетается с [4] - "Визард (собссно есть свой) — это страницы (на подобии PageControl) которые лежат на форме"


 
Kolan ©   (2008-03-06 16:42) [75]

Вопрос снимается, она же по ShowModal закрывается&#133


 
Kolan ©   (2008-03-06 16:47) [76]

> понаделай фреймов, а потом по нажатию кнопок на форме убирай
> один, создавай и показывай другой

Понимаешь, когда ты так говориш, то я в принципе понимаю. Но в принципе я и так могу понять. А точо понять что ты предлагаешь можно только на мини примере. Типа [67] KSergey.


> впрочем, непонятно

Суть в предыдущих 70 постах. :)


 
clickmaker ©   (2008-03-06 16:59) [77]


> только на мини примере

type
 TWizardFrame = class(TFrame)
 ...

 TStep1Frame = class(TWizardFrame)
 ...
 TStep25Frame = class(TWizardFrame)
 ...
 TWizardFrameClass = class of TWizardFrame;

var
 FCurrentFrame: TFrame;

procedure TWizardForm.Next()
begin
  FcurrentFrame.SaveData();
  FCurrentFrame.Free;
  CurrFrameClass := GetNextFrameClass(Step); // TWizardFrameClass
  FCurrentFrame :=  CurrFrameClass.Create(Self);
  FCurrentFrame.Parent := Self;
  FCurrentFrame.Visible := true;
end;

как-то так


 
b z   (2008-03-06 17:07) [78]


> Вопрос снимается, она же по ShowModal закрывается…

формы, патерны, классы, "фигасы" ... :(


> FCurrentFrame.Free;

Можно (или нужно) "захэшировать", с учетом - Back (Previous) и т.д.


 
KSergey ©   (2008-03-06 17:13) [79]

> b z   (06.03.08 17:07) [78]
> Можно (или нужно) "захэшировать", с учетом - Back (Previous)  и т.д.

Автору хочется напрягать менеджер кучи и ядро винды для создания окон и выделения/уничтожения для них GDI-объектов по полной программе.
Флаг ему в руки.


 
Kolan ©   (2008-03-06 17:15) [80]


> как-то так

Тут непонятно:
1. Как и куда сохранять данные фрейма.
2. Как и когда их восстанавливать.
3. Куда помещать логику связей. То есть как запомнить на шаге 2 и показать на шаге 4.
4. Что делать с тем пунктом, что событий типа TWizardForm.Next() разное кол-во на разных формах...


 
Kolan ©   (2008-03-06 17:18) [81]

> [79] KSergey ©   (06.03.08 17:13)

А если как ты предлагаешь сразу все создать, то надо будет очишать, сохранять и востанавливать состояния окон&#133


 
KSergey ©   (2008-03-06 17:19) [82]

> Kolan ©   (06.03.08 17:15) [80]
> Тут непонятно:

Мне вот любопытно: а у самого мысли есть по этим вопросам?
не ну правда. Сначала выкладыватся жутко навороченный код, а потом вдруг начинают задаваться элементарные вопросы.. Я вот никак не могу в голове своей это срастить...


 
clickmaker ©   (2008-03-06 17:20) [83]


> событий типа TWizardForm.Next() разное кол-во на разных
> формах

это как?

> есть как запомнить на шаге 2 и показать на шаге 4

опять двадцать пять. Список кто мешает сделать с данными?


 
Kolan ©   (2008-03-06 17:28) [84]


> не ну правда.

Есть ессно, только толку от них мало, я же хочу узнать как еще можно.
1. На счет сохранения я думаю, что это приличный геморой, бо формы разные, как их запоминать ..

2. Раз у тебя есть событие Next, значит будут и другие. Логика будет по ним распыляться. А еще часть её будет в GetNextFrameClass и GetPriorFrameClass - тоже распыление.

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

Разница между "И анализировать этот State при действиях" и KSergey ©   (06.03.08 13:52) [67] огромна (для меня), хотя вроде одно и тоже.


 
Kolan ©   (2008-03-06 17:31) [85]

> опять двадцать пять. Список кто мешает сделать с данными?

А опять 25 из-за того, что я реально невижу где ты будешь сохранять данные в GetNextFrameClass что ли?


 
clickmaker ©   (2008-03-06 17:41) [86]


> где ты будешь сохранять данные в GetNextFrameClass что ли?

в SaveData у конкретного фрейма. Который знает, что ему нужно сохранить.
Для любителей примеров:

TWizardData = class
public
  UserName: string;
  Email: string;
  Address1: string;
  Address2: string;
  Phone: string;
end;

var
 WizardData: TWizardData; // глобальная

procedure TWizardFrame1.SaveData;
begin
  WizardData.UserName := edName.Text;
  WizardData.Email := edEmail.Text;
end;

procedure TWizardFrame2.SaveData;
begin
  WizardData.Address1:=
  ...
end;

можно структуру разнести по нескольким, по числу страниц. Тогда будет глобальный список вместо переменной


 
Kolan ©   (2008-03-06 17:58) [87]

Понял. Много лишнего делать придется для каждого окна. Я же хочу его не закрывать пока оно еще может понадобится в том же виде.

ЗЫ
 Вообще пошел я делать, всех благодарю. :)


 
KSergey ©   (2008-03-06 18:02) [88]

> clickmaker ©   (06.03.08 17:41) [86]
> procedure TWizardFrame1.SaveData;
> procedure TWizardFrame2.SaveData;

Вот я тоже, к стати, думал, что так будет лучше и правильнее.
Но надеялся, что автор сам сможет подобный перенос сделать, потому писать не стал :)



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

Форум: "Основная";
Текущий архив: 2008.12.21;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.74 MB
Время: 0.043 c
1-1204908815
Воронтсов
2008-03-07 19:53
2008.12.21
работа с TOpenDialog


15-1224564830
Slider007
2008-10-21 08:53
2008.12.21
С днем рождения ! 21 октября 2008 вторник


2-1226751519
TRSteep
2008-11-15 15:18
2008.12.21
DirectX


2-1226438665
АлексейН
2008-11-12 00:24
2008.12.21
чтение из столбцов


15-1224472794
Slider007
2008-10-20 07:19
2008.12.21
С днем рождения ! 20 октября 2008 понедельник





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский