Конференция "Основная" » Как дисэйблить кнопки если операция не поддерживается… ?
 
  • Kolan © (25.01.08 19:36) [20]
    > [16] offff   (25.01.08 16:42)

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


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

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


    > ADDITEM.VISIBLE:=TRUE;

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

    Нихрена не понял короче…
  • oxffff © (26.01.08 01:24) [21]

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


    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;
  • oxffff © (26.01.08 01:26) [22]

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


    Есть такая команда. :)
  • Kolan © (26.01.08 09:21) [23]
    > [21] oxffff ©   (26.01.08 01:24)

    Смотрю…
  • Kolan © (26.01.08 12:05) [24]
    Понял, таки это решение с командами. А хотелось бы от них избавится.

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

    :)
  • Игорь Шевченко © (26.01.08 22:44) [25]

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


    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 © (27.01.08 09:52) [26]
    > получилось следующее:

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


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

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


    > Keep It Simple, Stupid

    В смысле Short and Simple ? :)
  • Kolan © (27.01.08 09:53) [27]
    > actFind.Enabled := ctFind in Supported;
    > actGoto.Enabled := ctFind in Supported;

    Вот это только плохой кусок, тут придется добавлять вручную…
  • oxffff © (27.01.08 10:50) [28]

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


    Рассмотри другой способ.
    Команда сама предоставляет callback процедуру и/или иконку.
    Твой Menu опрашивает команды на наличие этой функциональности.
    И в случае отсутствия ставит произвольную иконку.
    Так ты сможешь избавиться  от статической привязки построителя меню к набору обрабатываемых им конанд.
    + обеспечишь подрузку внешних комманд.
  • Kolan © (27.01.08 11:04) [29]
    Надо подумать…
  • Игорь Шевченко © (27.01.08 12:44) [30]
    Kolan ©   (27.01.08 09:52) [26]


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


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

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


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


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

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


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


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

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

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

    (с) Эрик Рэймонд, Искусство программирования для Unix
  • Kolan © (27.01.08 12:56) [31]
    > честно бы их опрашивал

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


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

    Медитирую…
  • Игорь Шевченко © (27.01.08 13:27) [32]
    Kolan ©   (27.01.08 12:56) [31]


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


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

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

    как-то так.
  • Kolan © (27.01.08 13:33) [33]
    > Это значит,

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

    Понял.

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

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

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



    Вообщем понятно. Много вариантов получил. Благодарю за обсуждение.
  • Игорь Шевченко © (27.01.08 13:45) [34]

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


    Тогда уж:

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

    Небольшая разница, но код становится яснее.
  • Kolan © (27.01.08 13:51) [35]
    > Небольшая разница, но код становится яснее.

    Согласен.

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

    Благодарю. :)
 
Конференция "Основная" » Как дисэйблить кнопки если операция не поддерживается… ?
Есть новые Нет новых   [134482   +35][b:0][p:0.001]