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

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

Как дисэйблить кнопки если операция не поддерживается&#133 ?


Страницы:   1 2

Kolan ©   (2008-01-25 15:32:00) 

Здравствуйте,
 Допустим у нас есть два вида объектов А и Б.

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

Понятно, что удобно узать стратегии. Тогда надо определить общий интерфейс, и сделать его конкретный реализации для А и Б.

Пример:
IItemsOperationsController = interface
   ['{3574BBB8-1413-4508-AD48-F4BEFFC88A28}']
   procedure AddItem;
   procedure DeleteCurrentItem;
   procedure SetCurrentItem(AnObject: TObject);
   procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
     IDeviceOperationPerformer);
 end;

 TCustomItemsOperationsController = class(TInterfacedObject, IItemsOperationsController)
 public
   procedure AddItem; virtual; abstract;
   procedure DeleteCurrentItem; virtual; abstract;
   procedure SetCurrentItem(AnObject: TObject); virtual; abstract;
   procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
     IDeviceOperationPerformer); virtual; abstract;
 end;

 TNetItemsOperationsController = class(TCustomItemsOperationsController)
 protected
   function QueryNetName(var AName: string): Boolean;
 public
   procedure AddItem; override;
   procedure DeleteCurrentItem; override;
   procedure SetCurrentItem(AnObject: TObject); override;
   procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
     IDeviceOperationPerformer); override;
 end;

 TDeviceItemsOperationsController = class(TCustomItemsOperationsController)
 protected
   function QueryDeviceAddress(var AnAddress: Byte): Boolean;
 public
   procedure AddItem; override;
   procedure DeleteCurrentItem; override;
   procedure SetCurrentItem(AnObject: TObject); override;
   procedure PerformOpertionWithCurretnItem(ADeviceOperationPerformer:
     IDeviceOperationPerformer); override;
 end;


Тогда вызов будет такой(это код экшена):
 procedure TMainForm.DeleteItemExecute(Sender: TObject);
begin
 GetConfigurationUtilityController.DeleteCurrentItem;
end;


Где GetConfigurationUtilityController— это синглетон, который делегирует вызов нужному контроллеру.
procedure TConfigurationUtilityController.DeleteCurrentItem;
begin
 GetItemsOperationsController.DeleteCurrentItem;
end;


Но вот проблемма, объекты типа Б не поддерживают операцию удаления(например). Для них хорошо бы кнопочку(экшен) удалить сделать недоступной.

Вопрос: Как?


Kolan   (2008-01-27 13:51:00) [1]

> Небольшая разница, но код становится яснее.

Согласен.

Имхо ниче так решение особенно в сочетании со стратегиями. Можно сделать мелкие интерфейсы (IFind, IGoto), а стратегии(или MDI Children) будут реализовывать их комбинации.

Благодарю. :)


Игорь Шевченко   (2008-01-27 13:45:00) [2]


> Будет так:
> actFind.Enabled := ActiveMDIChild.GetInterface(IFind, TheInt);
>


Тогда уж:

actFind.Enabled := Supports(ActiveMDIChild, IFind);

Небольшая разница, но код становится яснее.


Kolan   (2008-01-27 13:33:00) [3]

> Это значит,

Ааа, не увидел что тут CanFind.

Понял.

Тогда на каждую операцию еще надо добавить Can операцию. Довольно просто.

Но мне кажется, что мой вариант с интерфейсам еще лучьше, бо не надо будет Can операцию добавлять и проверка попроще.

Будет так:
actFind.Enabled := ActiveMDIChild.GetInterface(IFind, TheInt);

Вообщем понятно. Много вариантов получил. Благодарю за обсуждение.


Игорь Шевченко   (2008-01-27 13:27:00) [4]

Kolan ©   (27.01.08 12:56) [31]


> Что это значит? То есть кнопки бы всегда были активны?


Это значит, что в обработчике события ActionListUpdate я бы смотрел, является ли текущее окно экземпляром класса, поддерживающего эти операции и если да, то опрашивал бы эти методы.

actFind.Enabled := (ActiveMDIChild is TMyClass) and TMyClass(ActiveMDIChild).CanFind

как-то так.


Kolan   (2008-01-27 12:56:00) [5]

> честно бы их опрашивал

Что это значит? То есть кнопки бы всегда были активны?


> Напоследок немножно помедитируй над цитатой:

Медитирую…


Игорь Шевченко   (2008-01-27 12:44:00) [6]

Kolan ©   (27.01.08 09:52) [26]


> А как? если не секрет? Оч. интересно…


Унаследовал бы класс формы от предка, в котором были бы методы CanFind, CanGoto (это в моем случае) и честно бы их опрашивал.

Код получился бы короче и яснее.


> В смысле Short and Simple ? :)


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

Задай себе вопрос - что тебе чаще придется делать - читать код или дополнять его функциональность.


> Вот это только плохой кусок, тут придется добавлять вручную…


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

Напоследок немножно помедитируй над цитатой:

"ОО-языки упрощают абстракцию, возможно, даже слишком ее упрощают. Они поддерживают создание структур с большим количеством связующего кода и сложными уровнями.
Это может оказаться полезным в случае, если предметная область является
действительно сложной и требует множества абстракций, и вместе с тем такой подход может обернуться неприятностями, если программисты реализуют простые вещи сложными способами, просто потому что им известны эти способы и они умеют  ими пользоваться.
Все ОО-языки несколько сколнны "втягивать" программистов в ловушку избыточной иерархии. Чрезмерное количество уровней разрушает прозрачность: крайне затрудняется их просмотр и анализ ментальной модели, которую по существу реализует код. Всецело нарушаются правила простоты, ясности и прозрачности, а в результате код наполняется скрытыми ошибкми и создает постоянные проблемы при сопровождении.
Данная тенденция, вероятно, усугубляется тем, что множество курсов по
программированию преподают громоздкую иерархию как способ удовлетворения правила представления. С этой точки зрения множество классов приравнивается к внедрению знаний в данные. Проблема данного подхода заключается в том, что слишком часто "развитые данные" в связующих уровнях фактически не относятся у какому-либо естественному объекту в области действия программы - они предназначены только для связующего уровня.
Одной из причин того, что ОО-языки преуспели в большинстве характерных для них предметных областей (GUI-интерфейсы, моделирование, графические средства), возможно, является то, что в этих областях относительно трудно неправильно определить онтологию типов.
Например, в GUI-интерфейсах и графических средствах присутствует довольно естественное соотвествие между манипулируемыми
визуальными объектами и классами. Если выясняется, что создается большое
количество классов, которые не имеют очевидного соответствия с тем, что
происходит на экране, то, соотвественно, легко заметить, что связующий уровень стал слишком большим."

(с) Эрик Рэймонд, Искусство программирования для Unix


Kolan   (2008-01-27 11:04:00) [7]

Надо подумать…


oxffff   (2008-01-27 10:50:00) [8]


> Kolan ©   (27.01.08 09:53) [27]
> > actFind.Enabled := ctFind in Supported;
> > actGoto.Enabled := ctFind in Supported;
>
> Вот это только плохой кусок, тут придется добавлять вручную…


Рассмотри другой способ.
Команда сама предоставляет callback процедуру и/или иконку.
Твой Menu опрашивает команды на наличие этой функциональности.
И в случае отсутствия ставит произвольную иконку.
Так ты сможешь избавиться  от статической привязки построителя меню к набору обрабатываемых им конанд.
+ обеспечишь подрузку внешних комманд.


Kolan   (2008-01-27 09:53:00) [9]

> actFind.Enabled := ctFind in Supported;
> actGoto.Enabled := ctFind in Supported;

Вот это только плохой кусок, тут придется добавлять вручную…


Kolan   (2008-01-27 09:52:00) [10]

> получилось следующее:

Просто и понятно.


> Теперь бы делал иначе.

А как? если не секрет? Оч. интересно…
Хотелось бы, чтобы дабавлять команды было просто, дисэбл делался автоматически и код был простым…


> Keep It Simple, Stupid

В смысле Short and Simple ? :)


Игорь Шевченко   (2008-01-26 22:44:00) [11]


> Как дисэйблить кнопки если операция не поддерживается… ?
>  


Button.Enabled := false;

Чего гадать-то ?

В свое время тоже маялся подобной заумью, получилось следующее:

Есть MDI-форма с тулбаром, на тулбаре кнопки, на кнопках Actions, в зависимости от допустимости операций с текущим активным MDI-дитем, кнопки должны быть открыты или закрыты.

type
 TCommandType = (ctFind, ctGoto);
 TCommandTypes = set of TCommandType;

 IToolbarCommands = interface
 ['{D97A7812-091F-4E2F-84DA-08A394646BC2}']
   function SupportedCommands : TCommandTypes;
   procedure FindCommand;
   procedure GotoCommand;
 end;

И использование:

procedure TfrmMain.ActionList1Update(Action: TBasicAction;
 var Handled: Boolean);
var
 Supported: TCommandTypes;
 IC: IToolbarCommands;
begin
 if Assigned(ActiveMDIChild) and
    ActiveMDIChild.GetInterface(IToolbarCommands, IC) then
   Supported := IC.SupportedCommands
 else
   Supported := [];
 actFind.Enabled := ctFind in Supported;
 actGoto.Enabled := ctFind in Supported;
end;

Теперь бы делал иначе.


> Понятно, что удобно узать стратегии.


Keep It Simple, Stupid


Kolan   (2008-01-26 12:05:00) [12]

Понял, таки это решение с командами. А хотелось бы от них избавится.

Благодарю за обсужение. Пользу извлек такую — если ты не пддерживаешь интерфейс, то нехрен говорить, что поддреживаешь.

:)


Kolan   (2008-01-26 09:21:00) [13]

> [21] oxffff ©   (26.01.08 01:24)

Смотрю…


oxffff   (2008-01-26 01:26:00) [14]


>      if DynaCmds.TryPerfomOp(CurrentCommand,self) then showmessage('Not
> such command CurrentCommand')


Есть такая команда. :)


oxffff   (2008-01-26 01:24:00) [15]


> Нихрена не понял короче…


AddCommand=class;
DeleteCommand=class;
CurrentCommand=class;

AbstractCommandVisitor=class
procedure VAdd(Command:AddCommand);virtual;abstract;
procedure VCurrent(Command:CurrentCommand);virtual;abstract;
procedure VDelete(Command:DeleteCommand);virtual;abstract;
end;

MenuBarCommandVisitor=class(AbstractCommandVisitor)
procedure VAdd(Command:AddCommand);override;
procedure VDelete(Command:DeleteCommand);override;
procedure VCurrent(Command:CurrentCommand);override;
end;

AbstractCommand=class
function Exec(const param):integer;virtual;
procedure Accept(Visitor:AbstractCommandVisitor);virtual;
end;

TAbstractCommandClass=Class of AbstractCommand;

AddCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;

DeleteCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;

CurrentCommand=class(AbstractCommand)
function Exec(const param):integer;override;
procedure Accept(Visitor:AbstractCommandVisitor);override;
end;

DynaCommands=class
Commands:Tlist;
constructor create;overload;
constructor create(Commands:array of AbstractCommand);overload;
destructor destroy;override;
procedure Accept(Visitor:AbstractCommandVisitor);
function TryPerfomOp(CommandClass:TAbstractCommandClass;const param):boolean;
end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

{ DynaCommands }

constructor DynaCommands.create;
begin
Commands:=TList.Create;
end;

procedure DynaCommands.Accept(Visitor: AbstractCommandVisitor);
var i:integer;
begin
for i:=0 to commands.Count-1 do AbstractCommand(commands.Items[i]).Accept(Visitor);
end;

constructor DynaCommands.create(Commands: array of AbstractCommand);
var i:integer;
begin
create;
for i:=0 to length(commands)-1 do self.Commands.Add(commands[i]);
end;

destructor DynaCommands.destroy;
var i:integer;
begin
for i:=0 to commands.Count-1 do Tobject(commands.Items[i]).Free;
Commands.free;
inherited;
end;

function DynaCommands.TryPerfomOp(CommandClass:TAbstractCommandClass;const param):boolean;
var i:integer;
begin
for i:=0 to commands.count-1 do
  if (AbstractCommand(commands.Items[i]) is CommandClass) then
         begin
         AbstractCommand(commands.Items[i]).Exec(param);
         result:=true;
         exit;
         end;
result:=false;
end;

{ AbstractCommand }

procedure AbstractCommand.Accept(Visitor: AbstractCommandVisitor);
begin
//
end;

function AbstractCommand.Exec(const param): integer;
begin
//
end;

{ AddCommand }

procedure AddCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VAdd(self);
end;

function AddCommand.Exec(const param): integer;
begin
//
end;

{ DeleteCommand }

procedure DeleteCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VDelete(self);
end;

function DeleteCommand.Exec(const param): integer;
begin
//
end;

{ MenuBarCommandVisitor }

procedure MenuBarCommandVisitor.VAdd(Command: AddCommand);
begin
Showmessage('Enable ADD Command');
end;

procedure MenuBarCommandVisitor.VCurrent(Command: CurrentCommand);
begin
Showmessage('Enable Current Command');
end;

procedure MenuBarCommandVisitor.VDelete(Command: DeleteCommand);
begin
Showmessage('Enable Delete Command');
end;

{ CurrentCommand }

procedure CurrentCommand.Accept(Visitor: AbstractCommandVisitor);
begin
Visitor.VCurrent(self);
end;

function CurrentCommand.Exec(const param): integer;
begin
//
end;

procedure TForm1.Button1Click(Sender: TObject);
var DynaCmds:DynaCommands;
   MenuBar:MenuBarCommandVisitor;
begin
try
DynaCmds:=DynaCommands.create([AddCommand.Create,CurrentCommand.Create]);
 try
//Disable menu item
     MenuBar:=MenuBarCommandVisitor.Create;
     try
//Menu init
     DynaCmds.Accept(MenuBar);
     if not DynaCmds.TryPerfomOp(DeleteCommand,self) then showmessage('Not such command DeleteCommand');
     if DynaCmds.TryPerfomOp(CurrentCommand,self) then showmessage('Not such command CurrentCommand')
     finally
     MenuBar.free;
     end;
 finally
 DynaCmds.free;
 end;
except
end;
end;


Kolan   (2008-01-25 19:36:00) [16]

> [16] offff   (25.01.08 16:42)

Не, не понял… Как устроен MenuManager непонял.


> Реализатор IItemsOperationsController пробегает по всем
> его коммандам и делает вызов

Что за команды?


> ADDITEM.VISIBLE:=TRUE;

Какое-то жесткое присвоение…

Нихрена не понял короче…


oxffff   (2008-01-25 16:49:00) [17]


> offff   (25.01.08 16:42) [17]
> Удалено модератором
> Примечание: да что'ж ты дублями то :)


Sorry снова. Сам не знаю что за дела. Под oxffff пользователем порядок.
:)


oxffff   (2008-01-25 16:47:00) [18]

Что то задублилось у меня. Sorry.

Что касаемо реализации то я уже тут предлагал улучшенную реализацию GOF паттерна VISITOR без привязки к VMT.
Кстати напишу в своем блоге об этом.

:)


offff   (2008-01-25 16:42:00) [19]

Удалено модератором
Примечание: да что'ж ты дублями то :)


offff   (2008-01-25 16:42:00) [20]


> Kolan ©   (25.01.08 16:37) [15]


Menu Manager - отключает все кнопки.

MenuManager=class(CommandVisitor)
procedure OpAdd
procedure OpRemove
Procedure OpCurrent
end;

Реализатор IItemsOperationsController пробегает по всем его коммандам и делает вызов

AbstractCommand.Accept(visitor:CommandVisitor)  

procedure MenuManager.OpAdd
begin
ADDITEM.VISIBLE:=TRUE;
end;

procedure MenuManager.OpRemove
begin
RemoveITEM.VISIBLE:=TRUE;
end;

Procedure MenuManager.OpCurrent
begin
....
end;


Страницы:   1 2

Наверх

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