2015-12-04 7 views
0

У меня есть проект (Delphi 10 Seattle, win32) с множеством меню и множеством элементов в этих меню. Некоторые элементы меню создаются во время разработки, некоторые из них во время выполнения.Logging MenuItem OnClick Event

Что я хочу сделать, это зарегистрировать некоторую информацию о TMenuItem, такую ​​как имя/подпись, метка времени и т. Д., Когда запускается событие OnClick.

Я мог бы просто добавить вызов процедуры к началу каждой функции, которая назначена событию TMenuItem OnClick, но мне было интересно, было ли более элегантное решение.

Кроме того, я пробовал Embarcadero's AppAnalytics, но я нашел, что он не дал мне информации или гибкости, которую я хотел, и был довольно дорогой.

Редактировать: Я добавлю дополнительную информацию о том, какие варианты я рассмотрел (что я, вероятно, должен был сделать для начала).

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

procedure TSomeForm.SomeMenuItem1Click(Sender: TObject); 
var 
    item : TMenuItem; 
begin 
    item := Sender as TMenuItem; 
    LogMenuItem(item); // Simple log function added to the start of each menuitem click 
end; 

К «более элегантное решение» я имею в виду это было бы возможно, чтобы добавить «крюк» так, что все события TMENUITEM OnClick вызвали другую процедуру (которая будет делать запись в журнал) перед вызовом процедуры, назначенный OnClick Event ,

Или другой вариант, который я рассматривал, заключается в создании класса, унаследованного от TMenuItem, который будет отменять TMenuItem.Click и выполнять регистрацию перед генерированием события OnClick. Но тогда я не знал, как это будет работать для пунктов меню дизайна без большой работы, переделывая меню.

+0

Небольшой совет: прекратите использовать события onlick и вместо этого используйте Actions. – whosrdaddy

+0

воронка всех кликов на одно и то же событие, используйте Sender для оценки и регистрации каждого из них, а затем маршрутизируйте их в свое собственное событие. –

+0

@whosrdaddy вы могли бы очертить преимущества использования действий над OnClick –

ответ

2

Это намного легче достичь с помощью действий. Это принесет выгоду, что вы подберете действия, вызываемые элементами пользовательского интерфейса, отличными от меню, например панелей инструментов, кнопок и т. Д.

Используйте action list или action manager, как вам удобно. Например, в списке действий объект списка действий имеет событие OnExecute, которое срабатывает, когда выполняется какое-либо действие. Вы можете прослушивать это событие и регистрировать сведения о выполняемом действии.

+0

Спасибо @David, использование события OnExecute списка действий - это именно то, что я искал, просто, легко и не требует много работы, чтобы изменить код, чтобы использовать –

1

Я абсолютно согласен с тем, что действия - это путь, но для полноты и тех случаев, когда вы быстро хотите отлаживать приложение, используя меню старого стиля, вот единица, которую вы можете использовать с элементами меню. Это будет даже работать, если элементы меню имеют связанные с ними действия, но не будут работать для каких-либо других элементов управления с действиями, такими как TActionMainMenuBar. Весь код отладки находится в этом устройстве, чтобы ваш обычный код беспорядок. Просто добавьте блок в предложение uses и вызовите StartMenuLogging с любым применимым компонентом, например. компонент меню, компонент формы или даже Application! Любые элементы меню в дереве под ним будут подключены. Таким образом, вы можете отлаживать все клики по меню во всех формах только с этими двумя строками в вашем производственном коде. Вы можете использовать StopMenuLogging для остановки, но это необязательно. Предупреждение: Данное устройство не было протестировано должным образом - я взял старый блок отладки, который я написал, и очистил его для этой цели, и просто проверил его поверхностно.

unit LogMenuClicks; 


interface 

uses 
    Classes; 

procedure StartMenuLogging(AComponent: TComponent); 
procedure StopMenuLogging(AComponent: TComponent); 
procedure StopAllMenuLogging; 


implementation 

uses 
    SysUtils, 
    Menus; 


type 
    PLoggedItem = ^TLoggedItem; 
    TLoggedItem = record 
    Item: TMenuItem; 
    OldClickEvent: TNotifyEvent; 
    end; 

    TLogManager = class(TComponent) 
    private 
    FList: TList; 
    FLog: TFileStream; 

    procedure Delete(Index: Integer); 
    function FindControl(AItem: TMenuItem): Integer; 
    procedure LogClick(Sender: TObject); 
    protected 
    procedure Notification(AComponent: TComponent; Operation: TOperation); override; 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 

    procedure AddControl(AItem: TMenuItem); 
    procedure RemoveControl(AItem: TMenuItem); 
    end; 

    var 
    LogMan: TLogManager = nil; 

{ TLogManager } 

constructor TLogManager.Create(AOwner: TComponent); 
begin 
    inherited; 

    FLog := TFileStream.Create(ChangeFileExt(ParamStr(0), '.log'), fmCreate or fmShareDenyWrite); 
    FList := TList.Create; 
end; 

destructor TLogManager.Destroy; 
var 
    i: Integer; 
begin 
    i := FList.Count - 1; 
    while i >= 0 do 
    Delete(i); 
    FList.Free; 

    FLog.Free; 

    inherited; 
end; 

procedure TLogManager.Notification(AComponent: TComponent; Operation: TOperation); 
begin 
    if Operation = opRemove then 
    RemoveControl(TMenuItem(AComponent)); 

    inherited; 
end; 

procedure TLogManager.Delete(Index: Integer); 
var 
    li: PLoggedItem; 
begin 
    li := FList[Index]; 

    with li^ do 
    begin 
    Item.RemoveFreeNotification(Self); 
    Item.OnClick := OldClickEvent; 
    end; 

    Dispose(li); 
    FList.Delete(Index); 
end; 

function TLogManager.FindControl(AItem: TMenuItem): Integer; 
begin 
    Result := FList.Count - 1; 
    while (Result >= 0) and (PLoggedItem(FList[Result]).Item <> AItem) do 
    Dec(Result); 
end; 

procedure TLogManager.AddControl(AItem: TMenuItem); 
var 
    li: PLoggedItem; 
begin 
    if not Assigned(AItem) then 
    Exit; 

    if FindControl(AItem) >= 0 then 
    Exit; 

    New(li); 
    li.Item := AItem; 
    li.OldClickEvent := AItem.OnClick; 
    AItem.OnClick := LogClick; 
    FList.Add(li); 

    AItem.FreeNotification(Self); 
end; 

procedure TLogManager.RemoveControl(AItem: TMenuItem); 
var 
    i: Integer; 
begin 
    if Assigned(AItem) then 
    begin 
    i := FindControl(AItem); 
    if i >= 0 then 
     Delete(i); 
    end; 
end; 

procedure TLogManager.LogClick(Sender: TObject); 
var 
    s: string; 
begin 
    s := Format('%s: %s' + sLineBreak, [TComponent(Sender).Name, FormatDateTime('', Now)]); 
    FLog.WriteBuffer(s[1], Length(s)); 
    PLoggedItem(FList[FindControl(TMenuItem(Sender))]).OldClickEvent(Sender); 
end; 


procedure StartMenuLogging(AComponent: TComponent); 

    procedure CheckControls(Comp: TComponent); 
    var 
    i: Integer; 
    begin 
    if Comp is TMenuItem then 
     LogMan.AddControl(TMenuItem(Comp)) 
    else 
     for i := 0 to Comp.ComponentCount - 1 do 
     CheckControls(Comp.Components[i]); 
    end; 

begin 
    if not Assigned(LogMan) then 
    LogMan := TLogManager.Create(nil); 

    CheckControls(AComponent); 
end; 

procedure StopMenuLogging(AComponent: TComponent); 

    procedure CheckControls(Comp: TComponent); 
    var 
    i: Integer; 
    begin 
    if Comp is TMenuItem then 
     LogMan.RemoveControl(TMenuItem(Comp)) 
    else 
     for i := 0 to Comp.ComponentCount - 1 do 
     CheckControls(Comp.Components[i]); 
    end; 

begin 
    if Assigned(LogMan) then 
    CheckControls(AComponent); 
end; 

procedure StopAllMenuLogging; 
begin 
    LogMan.Free; 
end; 


initialization 

finalization 
    if Assigned(LogMan) then 
    LogMan.Free; 

end. 
+0

Спасибо Jannie, это определенно позволило бы мне делать то, что я хотел, не изменяя все элементы меню, чтобы использовать действия, но я собираюсь потратить время и изменить их все, чтобы использовать действия, поскольку это общее мнение о том, что действия лучше –

+0

@Clayton, да, делайте действие, если это возможно, и держите мое подразделение в аварийных ситуациях, когда у вас нет времени, чтобы изменить большой проект, когда вы охотитесь за ошибкой! –