![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Реализация интерфейса-мастера.
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]> которая хранится в некоей переменной
Лучьше уж стратегию использовать, только как. Хотелось бы более конкретный пример.