Форум: "Основная";
Текущий архив: 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"а…
А что вы посоветуете?
← →
Игорь Шевченко © (2008-03-01 00:05) [1]
> А что вы посоветуете?
Конечный автомат.
type
TAutomataState = (asStartState, asState1, asasState2, asCompleteState);
И анализировать этот State при действиях
← →
Kolan © (2008-03-01 10:37) [2]> И анализировать этот State при действиях
И сделать большой кейс? Ну я тоже самое и предложил, только в моем случае эти стейты хроняться в самих окнах как бы, и передаются по событию…
В стейтах плохо то, что трудно модифицировать такой автомат, я так пробовал.
Например бы такой:
case Stae of:
asStartState:
begin
DoSmth;
State := asState2;
end;
asasState2:
begin
State := CompleteState;
end;
asCompleteState:
begin
end;
Теперь если надо вставить asState1 между asStartState и asasState2, то надо менять ссылки — неудобно.
Кроме того, нафига состояния если состояние однозначно связано с показываемым экраном, так почему бы не использовать сам экран(то есть форму у меня) в качестве состояния.
Если бы надо было бы только показывать экраны, то я бы сделал, наверно, такой файлик:
У окон есть номера.[0.0]
Next=0.1
[0.1]
Next=0.2
Back=0.1
То есть некие мета данные… Загружал бы его и при возникновении события «Back» в окне «0.1» я бы знал, что надо перейти в окно 0.1…
Но вот как быть с выполнением действий — хз.
Плюс ко всму окон много и кейс будет здоровый…
Мож есть еще идеии?
← →
b z (2008-03-01 12:42) [3]
> Мож есть еще идеии?
Может взять компонент аля wizard?
← →
Kolan © (2008-03-01 14:07) [4]> Может взять компонент аля wizard?
А как он работает?
Визард (собссно есть свой) — это страницы (на подобии PageControl) которые лежат на форме, то есть. Мне бы хотелось не создавать ненужные окна.
← →
Kolan © (2008-03-03 10:09) [5]Мож есть у кого еще идеи?
← →
clickmaker © (2008-03-03 13:15) [6]
> Мне ненравится такой подход из за if"а
не вижу логики.
Чем if-то плох?
← →
Kolan © (2008-03-03 13:35) [7]Плохо тем, что неудобно добавлять или удалять экраны. Кроме того экранов много и иф будет большой — неудобно. А еще есть как-бы разные ветки, несвязаные друг с другом, и все это будет в одном месте…
← →
Семеныч (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], и показывать следующиее окно.
Валидация будет внутри форм.
Оцените, плз…
← →
Игорь Шевченко © (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] я читал, как конкретно предлагается реализовать? Где хранить состояния? Как узнавать что следующим показывать?
> А чем тебе двунаправленный список не угодил?
> Объект в элементе списка поддерживает интерфейс.
Так и хотел сделать примерно, только операций много не только «Назад» «Далее», поэтому интерфейс несколько другой.
Вопрос что писать в контроллере? При возникновении 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);
Так это для одного «нода» а теперь представь что их 25 и у всех по некст(бэк не беру тут проще) надо делать разные действия.
Так вот как ты это напишешь? Вот сама суть того, что я хз как сделать, чтобы попроще было…
← →
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 запустить поток и туда её засунуть…
Вот как это делать ты прелагаешь — непонятно.
← →
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 что-то с этим сделаешь, если формы ничего не знают про друг друга…
← →
KSergey © (2008-03-04 08:46) [23]как обычно - сценарий меняется по ходу пьессы.
Это уже не интересно становится. Неужели нельзя все сразу было рассказать?
По поводу "переменных на разных шагах" - ну так это для пользователя эти шаги - разные. Но что мешает все состояние хранить в одном месте, в общей куче от всех "шагов"? По сути ведь есть некий объект (здесь обьек - условность, не в смысле ООП), управление которым исключительно для удобства (не более!) разнесено на несколько "шагов" вместо одного огромного экрана.
По-моему, именно так стоит на это все смотреть. Т.е. экраны шагов - друг про друга пусть не знают, однако меняют состояние одного общего объекта (опять же не буквально в терминах ООП). Может и не экраны меняют его состояние, а из них просто информация вычитывается. Тут смотреть надо как связей меньше.
← →
Kolan © (2008-03-04 09:02) [24]> как обычно — сценарий меняется по ходу пьессы.
«Допустим есть 4 шага. На шаге 2 пользователь вводит данные, а на шаге 4 их надо обработать как-то.» © [0] Kolan
> Может и не экраны меняют его состояние, а из них просто
> информация вычитывается.
Вот тут я и прошу конкретный пример, бо всплывают проблеммы… Все написанное я понимаю.
← →
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]> Либо и т.д.
Либо, либо, вариантов масса… а конкретного примера тен.
Ладно шас выложу свой вариант, будите ругать :).
← →
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;
Как бы сделать так, чтобы у предка всех форм это экшен лист сразу бы лежал на форме?
Хоть и кейс но все в одном месте вроде…
← →
Kolan © (2008-03-04 10:45) [29]К когду контроллера прилагается State Chart…
← →
KSergey © (2008-03-04 11:30) [30]Зачем люди переносят заморочки, требуемые в С++ в дельфийский код? В дельфи можно и без всякие рукописных прибабахнутых фабрик объекты по указанному классу создавать, все для этого в компиляторе уже есть. Зачем отсебятину придумывать? Да и все сознаддые формы в Screen.Forms уже есть. Хотя, конечно, иногда бывает проще свой список поддерживать, только для требуемых форм, но тоже вопрос еще.
А использовать по тексту строковые константы - ваще капец. Описка в букве - и превед отладчег. Можно ведь и просто константами все описать, причем - перчислением, как и было сказано. Они - integer, их проверять быстрее у процессора выходит, да и описки - компилятором все отловятся.
В общем на мой вкус - много лишних сущностей. Но это наверняка субъективно.
← →
Kolan © (2008-03-04 11:56) [31]> Зачем люди переносят заморочки, требуемые в С++
Я его с трудм читаю…
> указанному классу создавать
А кто укажет его?
> А использовать по тексту строковые константы — ваще капец.
Ладно, а где их хранить, приведи пример…
← →
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] не нравится?
Я не поняимаю как конкретно рпелагается это решнение использовать, как инстанцировать и убивать формы…
← →
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)
Ты че издеваешься что ли? Ты думаешь я незнаю как создать форму что ли? Ты покажи напримере как ты сделаешь с состояниями, я же мысли не читаю.
«Используй перечисление» — это что, то что их как-то можно использовать я понимаю и так? Тут же вопрос именно как все организовать…
← →
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 не править. Этим мне и ненравится кейс… О чем собссно и вся ветка с самого начала…
← →
oxffff © (2008-03-04 14:48) [39]
> Kolan © (04.03.08 14:17) [38]
Перечитай сначала. :)
← →
Kolan © (2008-03-04 14:53) [40]> А у тебя тоже все статично.
Ну а как без этого, хоть ListClass не править.
> Захочешь добавить будешь править свой
Этим мне и ненравится кейс… О чем собссно и вся ветка с самого начала…
Имел это ввиду…
Страницы: 1 2 3 вся ветка
Форум: "Основная";
Текущий архив: 2008.12.21;
Скачать: [xml.tar.bz2];
Память: 0.59 MB
Время: 0.05 c