【问题标题】:How can I change the caption of a modal form without access to the source?如何在不访问源代码的情况下更改模态表单的标题?
【发布时间】:2012-10-13 20:28:30
【问题描述】:

我有一个显示打印预览表单的第三方组件。我想将预览表单的标题更改为更合适的内容。不幸的是,我没有第三方组件的源代码,并且该组件不提供该功能。

是否有可能在显示模态表单时以某种方式捕获它并在显示之前设置它的属性?

【问题讨论】:

  • 这只是另一个例子,说明如果有可用的选项,您不应该使用您没有源代码的第三方组件。 :-) 您可以指定您正在使用的第三方组件,以便如果有与该组件集相关的已知解决方案,有人可以提供它。
  • @Ken:TeeChart。我可以升级到 'with source' 版本,但很难证明至少 250 美元只是为了更改表单的标题:o)
  • 这样的问题是,如果有什么变化(或者他们停止在下一个版本的 Delphi 中包含 TeeChart),所有使用它的代码都无法移动到下一个版本的编译器。如果你有依赖于第三方组件的代码,你需要有源代码来保护自己。这是您在学习 Delphi 后首先应该学习的东西之一 - 它的开放式架构非常适合第三方扩展,但是当将来事情发生变化并且您无法自己解决问题时,使用它们而不获取源代码会使您瘫痪。
  • 嗨,肯。我完全同意并拥有我使用的所有其他组件的源代码(大约 7 个或更多)。我不想为 TeeChart 源代码分叉的原因是它在整个应用程序中只占很小的一部分,并且可以在需要时轻松替换。

标签: delphi delphi-xe2


【解决方案1】:

模态表单将导致调用表单停用,您可以在显示模态表单之前在处于活动状态的表单上监听WM_ACTIVATE消息。您将在消息处理程序中拥有激活窗口的句柄,您可以测试它是否属于模态表单类型的表单。下面的示例测试类名,您可以使用 Spy++ 之类的东西获得。请注意,在模态表单变得可见后会短暂停用,但我认为不可能注意到不同的标题。

type
  TForm1 = class(TForm)
    ..
  protected
    procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
  end;

procedure TForm1.WMActivate(var Message: TWMActivate);
var
  Form: TWinControl;
begin
  if Message.Active = WA_INACTIVE then begin
    Form := FindControl(Message.ActiveWindow);
    if Form is TCustomForm then begin
      if TCustomForm(Form).ClassName = 'TThirdPartyModalForm' then
        TCustomForm(Form).Caption := 'My caption';
    end;
  end;
  inherited;
end;

【讨论】:

  • 完美运行。谢谢 Sertac。
  • 如何将TScreen.OnActiveFormChange 事件与TScreen.ActiveCustomFormTScreen.ActiveForm 属性一起使用,而不是直接处理WM_ACTIVATE 并搜索活动的TForm
  • @Remy - 在焦点更改期间激活模态表单时稍后会触发它,应该没问题。但是如果这是问题的话 FindControl 是相当快的,实际上它是在 Application.ProcessMessage 期间与每个排队的消息一起调用的。
  • @SertacAkyuz: FindControl() 很快,因为每个TWinControl 对象都将其Self 指针存储在其分配的HWND 句柄中。 FindControl() 只是从提供的HWND 中检索该指针。我只是在想TScreen 方法会更像 VCL,并且可能更跨平台,然后诉诸于处理特定于平台的消息,仅此而已。
  • @Remy - 是的,我看到了这一点,你的回答有我的投票权。 :)
【解决方案2】:

尝试使用TScreen.OnActiveFormChange 事件,使用TScreen.ActiveCustomFormTScreen.ActiveForm 属性来知道哪个TForm 具有焦点:

procedure TMainForm.DoSomething; 
begin 
  Screen.OnActiveFormChange := ActiveFormChanged;
  try
    // do something that triggers the modal form ...
  finally
    Screen.OnActiveFormChange := nil;
  end;
end;

procedure TMainForm.ActiveFormChanged(Sender: TObject);
var
  Form: TCustomForm;
begin
  Form := Screen.ActiveCustomForm; 
  if (Form <> nil) and (Form.ClassName = 'TModalFormClassName') then 
    Form.Caption := 'My caption'; 
end;

【讨论】:

    【解决方案3】:

    您可以使用SetWindowsHookEx 函数安装WH_CBT 挂钩。

    var
     hhk: HHOOK;
    
    
    function CBT_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    const
      ClassNameBufferSize = 1024;
    var
     hTemp  : HWND;
     i      : Integer;
     RetVal : Integer;
     ClassNameBuffer: Array[0..ClassNameBufferSize-1] of Char;
    begin
       case nCode of
         HCBT_ACTIVATE:
         begin
           hTemp := HWND(wParam);
           if (Screen<>nil) and (hTemp>0) then
           begin
              RetVal := GetClassName(wParam, ClassNameBuffer, SizeOf(ClassNameBuffer));
              //check for the class 
              if (RetVal>0) and SameText(ClassNameBuffer,'TForm2') then
              begin
                 Assert(RetVal < ClassNameBufferSize, 'Class name larger than fixed buffer size');
                for i := 0 to Screen.FormCount-1 do
                 if Screen.Forms[i].Handle=hTemp then
                   begin 
                      //set the caption
                      Screen.Forms[i].Caption:='Hello';
                      Break;
                   end;
              end;
           end;
         end;
       end;
      Result := CallNextHookEx(hhk, nCode, wParam, lParam);
    end;
    
    Procedure InitHook();
    var
      dwThreadID : DWORD;
    begin
      dwThreadID := GetCurrentThreadId;
      hhk := SetWindowsHookEx(WH_CBT, @CBT_FUNC, hInstance, dwThreadID);
      if hhk=0 then RaiseLastOSError;
    end;
    
    
    Procedure KillHook();
    begin
      if (hhk <> 0) then
        UnhookWindowsHookEx(hhk);
    end;
    
    
    initialization
      InitHook();
    
    finalization
      KillHook();
    end.
    

    【讨论】:

      【解决方案4】:

      这是您可以尝试的“伪”代码(未经测试):

      const
        MY_PRINT_PREVIEW_MSG = WM_USER + 200;
      
      type
        TForm1 = class(TForm)
          procedure MyPrintPreviewMsg(var Msg: TMessage); message MY_PRINT_PREVIEW_MSG;
          procedure MyPrintPreview;
        end;
      ...
      procedure TForm1.MyPrintPreviewMsg(var Msg: TMessage);
      var
        h: HWND;
      begin
        h := Screen.Forms[0].Handle; // if the modal dialog is VCL dialog (verify it with spy++)
        // h := FindWindow(<class name>, <caption>); // non VCL window
        if (h <> 0) then
        begin
          SetWindowText(h, 'new caption');
        end;
      end;
      
      procedure TForm1.MyPrintPreview;
      begin
        PostMessage(Handle, MY_PRINT_PREVIEW_MSG, 0, 0);
        ThirdPartyPrintPreview;
      end;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-18
        • 1970-01-01
        • 1970-01-01
        • 2020-09-22
        • 1970-01-01
        • 2017-11-20
        • 2017-11-15
        相关资源
        最近更新 更多