【问题标题】:Components without VCL Forms in DelphiDelphi 中没有 VCL 表单的组件
【发布时间】:2011-05-20 05:20:29
【问题描述】:

可能是关于现实世界中不需要的东西的问题 .但我想知道我们能做到吗(只是为了知识)

我可以显示没有 VCL 形式的 vcl 组件吗?我们可以创建和使用非可视 vcl,但我们可以在可视 vcl 中创建并使其在屏幕上可见。

或者至少我们可以在使用 windows API 创建的表单中放置一个 VCL,

【问题讨论】:

  • 是的,这可能是可能的,但可能不是很方便。
  • 这是一个很棒的评论,因为您说是!但是如何
  • 为什么在评论里问怎么做?在 Q 你说你不需要这样做。我希望它可以做到。就我个人而言,我会将它托管在 TForm 后代中,并使用 ParentWnd 属性作为父级。

标签: delphi winapi vcl


【解决方案1】:

这不仅仅是我自己的代码,而是你所有对我有用的代码的混合器

只需为 volvox 的答案添加这些代码(我已接受并在顶部

uses
  Windows,
  Messages,buttons,stdctrls;


var
abtn:TButton ;


begin
//  after all declaration
 abtn := TButton.Create(nil) ;
 abtn.ParentWindow := hEdit ; // use window handle but i use hedit because my button     will be hidden by another button in example
end

【讨论】:

  • 更简单的是 abtn := TButton.CreateParented(hEdit)
【解决方案2】:

不,VCL 控件(可视组件)不能存在于 VCL 表单之外。但是,您可以将 VCL 控件放置在 VCL 表单中,然后将 VCL 表单托管在某种其他类型的窗口中。 Delphi 对创建 ActiveX 控件的支持就是这样做的——VCL 表单(我认为是 TCustomActiveForm)提供了分发给 ActiveX 的窗口句柄。您的 VCL 组件位于该 TCustomActiveForm 或该类的后代中,并且永远不知道它们之间的区别。

如果您愿意进行一些挖掘和提升,您也许可以避免 ActiveX 的开销。首先,检查 TCustomForm 上的 ParentHwnd 属性或 CreateParented 方法。这可能就是你所需要的——我想不起来了,而且我已经好几年没看到代码了。

如果没有,您可以覆盖 TCustomForm 后代中的 CreateParams 方法,并将非 VCL 窗口句柄分配给 Params.WndParent 字段。在调用继承的 CreateParams 后执行此操作,以便您获得其他所有内容的所有正常设置。您可能还需要调整样式和 exstyle 标志以删除表单的边框、标题等。

在此自定义表单中构建您的 VCL 控件,并将其调整大小以占据整个表单区域。

您可以使用比 TCustomForm 更低级别的控件(例如 TCustomWinControl)来执行此操作,但您可能会丢失一些按键/加速器窗口消息处理。我知道它可以在 TCustomForm 级别完成。

【讨论】:

  • @dthorpe 你不能只使用 ParentWindow 或 CreateParented 吗?编辑:我现在看到我在质疑 Danny Thorpe 对 Delphi 的了解,所以我希望你是对的!
  • CreateParented 可能会这样做。我以为我们已经实现了一些以更自动化的方式执行此操作,但我手边没有任何 VCL 源,在线文档仍有很多不足之处。
  • @dthorpe “在线文档仍有很多不足之处” 'nuff 说! CreateParented 接受一个 HWND,它在 Params.WndParent 中结束。我不确定您需要对样式标志做很多事情,我认为您可以设置 Form.Align := alClient。我这样做是为了将 VCL 表单放入其他 VCL 表单中。你确定你不想回到德尔福产品开发?!!你和巴里会组成一个非常强大的团队!!!
  • @David:再次使用 Delphi 代码将是一种享受,但是一旦你离开了家,你就永远无法真正回来。
  • @dthorpe:您当然是对的,但我们用户只能做梦!如果你留下来,我敢肯定我们现在已经有了 64 位的 Delphi,这是我的一个特别的 bug。至少听起来,该领域终于取得了良好的进展。
【解决方案3】:

控件可以在所有者和父容器中查找和访问属性,将它们作为 VCL 类进行访问。如果不是,VCL 控件很有可能无法正常工作。

避免这种情况的一种方法是将它们包装为 ActiveX 控件。然后,您可以在任何支持 ActiveX 控件的语言中使用 then。

【讨论】:

    【解决方案4】:

    对于 VCL components,答案显然是肯定的。 组件可以在表单上下文之外使用,无需任何麻烦,只需通过指定 nil 绕过所有权机制并在完成后手动销毁实例。

    NoLongerDependsOnForm := TSomeComponent.Create(nil);
    NoLongerDependsOnForm.This;
    NoLongerDependsOnForm.That;
    FreeAndNil(NoLongerDependsOnForm);
    

    使用 VCL controls 事情要复杂得多,因为任何 child 窗口都需要父窗口。因此,在通用窗口中重用 TWinControl 仅在理论上是可能的(必须编写 VCL 存根才能使其工作)。所以,答案是否定的。

    我求求你,请不要混淆术语 formwindow。 (延伸阅读:顶层和子窗口)

    【讨论】:

    • 公平地说,微软让他们很容易混淆,因为操作系统被称为 Windows,99.99% 的人将窗体称为窗口!
    【解决方案5】:

    是的 - 如果我能很好地理解你的问题。我已经完成了没有表单、可视和非可视 vcl 和 api 的完整安装程序。当然,要使其可视化(没有控制台),您需要一个窗口。

    示例代码 - 无格式(打开以下 .dpr 文件并编译):

    program Window;
    
    {
       This is an example of making an application
       without using the Forms unit. Forms.pas is the
       Delphi unit that makes your programs so damn
       huge! Forms.pas has all the code for translating
       the forms you create with Delphi w/components
       into windows. 
    }
    
    
    uses Windows, Messages;
    
    {$R *.RES}
    
    var
    wClass:   TWndClass;  // class struct for main window
    hFont,                // handle of font
    hInst,                // handle of program (hinstance)
    Handle,               // handle of main window
    hEncrypt,             // handle of encrypt button
    hDecrypt,             // handle of decrypt button
    hEdit,                // handle of main edit
    hLabel,               // handle of password label
    hPW:      HWND;       // handle of password edit
    Msg:      TMSG;       // message struct
    dEncrypt,
    dDecrypt: Pointer;    // default button procedures
    
    (*------------------------------------------------*)
    
    // This lines everything up
    procedure Resize;
    var
    RCT:TRect;
    begin
      GetWindowRect(Handle,RCT);
      MoveWindow(hPW,230,5,RCT.Right-RCT.Left-245,24,True);
      MoveWindow(hEdit,5,34,RCT.Right-RCT.Left-20,RCT.Bottom-RCT.Top-66,True);
    end;
    
    (*------------------------------------------------*)
    
    // This is to cleanup and stop the program
    procedure ShutDown;
    begin
      DeleteObject(hFont);
      UnRegisterClass('Sample Class',hInst);
      ExitProcess(hInst); //end program
    end;
    
    (*------------------------------------------------*)
    
    // Decrypts the text in hEdit with the text in hPW
    procedure Decrypt;
    var
    x,i,                // count variables
    sText,sPW: Integer; // size of Text, PW
    Text,PW:   PChar;   // buffer for Text, PW
    begin
      sText:=GetWindowTextLength(hEdit)+1;
      sPW:=GetWindowTextLength(hPW)+1;
      GetMem(Text,sText);
      GetMem(PW,sPW);
      GetWindowText(hEdit,Text,sText);
      GetWindowText(hPW,PW,sPW);
      x:=0; // initialize count
      for i:=0 to sText-2 do
      begin
        Text[i]:=Chr(Ord(Text[i])-Ord(PW[x]));
        Inc(x);
        if x=(sPW-1)then x:=0;
      end;
      SetWindowText(hEdit,Text);
      FreeMem(Text);
      FreeMem(PW);
    end;
    
    (*------------------------------------------------*)
    
    // Encrypts the text in hEdit with the text in hPW
    procedure Encrypt;
    var
    x,i,                // count variables
    sText,sPW: Integer; // size of Text, PW
    Text,PW:   PChar;   // buffer for Text, PW
    begin
      sText:=GetWindowTextLength(hEdit)+1;
      sPW:=GetWindowTextLength(hPW)+1;
      GetMem(Text,sText);
      GetMem(PW,sPW);
      GetWindowText(hEdit,Text,sText);
      GetWindowText(hPW,PW,sPW);
      x:=0; // initialize count
      for i:=0 to sText-2 do
      begin
        Text[i]:=Chr(Ord(Text[i])+Ord(PW[x]));
        Inc(x);
        if x=(sPW-1)then x:=0;
      end;
      SetWindowText(hEdit,Text);
      FreeMem(Text);
      FreeMem(PW);
    end;
    
    (*------------------------------------------------*)
    
    // This function processes every message sent to the Encrypt Button
    function EncryptProc(hWnd,Msg,wParam,lParam:Longint):Longint; stdcall;
    var
    i: Integer;
    begin
      // Always pass the message to the Default procedure
      Result:=CallWindowProc(dEncrypt,hWnd,Msg,wParam,lParam);
    
      case Msg of
      // If the user presses TAB we're gunna switch the
      // focus over to the Decrypt button.
      WM_KEYDOWN: if wParam=9 then SetFocus(hDecrypt);
      end;
    
    end;
    
    (*------------------------------------------------*)
    
    // This function processes every message sent to the Decrypt Button
    function DecryptProc(hWnd,Msg,wParam,lParam:Longint):Longint; stdcall;
    begin
      // Always pass the message to the Default procedure
      Result:=CallWindowProc(dEncrypt,hWnd,Msg,wParam,lParam);
    
      case Msg of
      // if the user presses TAB we're gunna switch
      // the focus back to the Encrypt button.
      WM_KEYDOWN: if wParam=9 then SetFocus(hEncrypt);
      end;
    
    end;
    
    (*------------------------------------------------*)
    
    // This function processes every message sent to our Main window
    function WindowProc(hWnd,Msg,wParam,lParam:Longint):Longint; stdcall;
    begin
      // Always pass the message to the Default procedure
      Result:=DefWindowProc(hWnd,Msg,wParam,lParam);
    
      case Msg of
      WM_SIZE:    Resize;
      // When buttons are clicked the message is passed to
      // the parent window, so we handle it here.
      WM_COMMAND: if      lParam=hEncrypt then Encrypt
                  else if lParam=hDecrypt then Decrypt;
      WM_DESTROY: ShutDown;
      end;
    
    end;
    
    (*------------------------------------------------*)
    
    // This is the main program function (WinMain)
    begin
    
      hInst:=GetModuleHandle(nil); // get the application instance
                                   // hInstance returns zilch
    
      with wClass do
      begin
        Style:=         CS_PARENTDC;
        hIcon:=         LoadIcon(hInst,'MAINICON');
        lpfnWndProc:=   @WindowProc;
        hInstance:=     hInst;
        hbrBackground:= COLOR_BTNFACE+1;
        lpszClassName:= 'Sample Class';
        hCursor:=       LoadCursor(0,IDC_ARROW);
      end;
    
      // Once our class is registered we
      // can start making windows with it
      RegisterClass(wClass);
    
      // Create our main window
      Handle:=CreateWindow(
        'Sample Class',          // Registered Class Name
        'Encrypter', // Title of Window
        WS_OVERLAPPEDWINDOW or   // Basic Window Style
        WS_VISIBLE,              // Make it Visible
        10,                      // Left
        10,                      // Top
        400,                     // Width
        300,                     // Height
        0,                       // Parent Window Handle
        0,                       // Handle of Menu
        hInst,                   // Application Instance
        nil);                    // Structure for Creation Data
    
      // Create the Encrypt button
      hEncrypt:=CreateWindow(
        'Button',
        'Encrypt',
        WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT,
        5,5,65,24,Handle,0,hInst,nil);
    
      // Create the Decrypt button
      hDecrypt:=CreateWindow(
        'Button',
        'Decrypt',
        WS_VISIBLE or WS_CHILD or BS_PUSHLIKE or BS_TEXT,
        75,5,65,24,Handle,0,hInst,nil);
    
      // Create the main Edit
      hEdit:=CreateWindowEx(
        WS_EX_CLIENTEDGE, // this EX style is for the beveled edge
        'Edit',
        '',
        WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or ES_WANTRETURN or ES_AUTOVSCROLL or WS_VSCROLL,
        5,34,380,234,Handle,0,hInst,nil);
    
      // Create the password Edit
      hPW:=CreateWindowEx(
        WS_EX_CLIENTEDGE,
        'Edit',
        '',
        WS_VISIBLE or WS_CHILD or ES_LEFT or ES_AUTOHSCROLL or ES_PASSWORD,
        230,5,155,24,Handle,0,hInst,nil);
    
      hLabel:=CreateWindow(
        'Static',
        'Password:',
        WS_VISIBLE or WS_CHILD or SS_LEFT,
        160,10,70,20,Handle,0,hInst,nil);
    
      // Create a custom font for our window otherwise
      // everything would use the system font (blech!)
      hFont:=CreateFont(
        -12,                           // Height
        0,                             // Width
        0,                             // Angle of Rotation
        0,                             // Orientation
        0,                             // Weight
        0,                             // Italic
        0,                             // Underline
        0,                             // Strike Out
        DEFAULT_CHARSET,               // Char Set
        OUT_DEFAULT_PRECIS,            // Precision
        CLIP_DEFAULT_PRECIS,           // Clipping
        DEFAULT_QUALITY,               // Render Quality
        DEFAULT_PITCH or FF_DONTCARE,  // Pitch & Family
        'MS Sans Serif');              // Font Name
    
      // Set the fonts for all our controls
      SendMessage(hEncrypt,WM_SETFONT,hFont,0);
      SendMessage(hDecrypt,WM_SETFONT,hFont,0);
      SendMessage(hEdit,WM_SETFONT,hFont,0);
      SendMessage(hPW,WM_SETFONT,hFont,0);
      SendMessage(hLabel,WM_SETFONT,hFont,0);
    
      // Subclass Encrypt Button (assign it a custom WindowProc)
      dEncrypt:=Pointer(GetWindowLong(hEncrypt,GWL_WNDPROC));
      SetWindowLong(hEncrypt,GWL_WNDPROC,Longint(@EncryptProc));
    
      // Subclass Decrypt Button
      dDecrypt:=Pointer(GetWindowLong(hDecrypt,GWL_WNDPROC));
      SetWindowLong(hDecrypt,GWL_WNDPROC,Longint(@DecryptProc));
    
      // The reason I don't subclass the Edit controls here
      // is because they don't do anything custom. If I wanted
      // them to Beep or something whenever you typed a "G" then
      // I would subclass them.
    
      // Focus on first control (otherwise people with no mouse are screwed)
      SetFocus(hEncrypt);
    
      // Now we loop GetMessage to process each Message in
      // our main window's message list. Every time the main
      // window recieves a message its added to the list, so
      // this loop here will eventually process it.
    
      while(GetMessage(Msg,Handle,0,0))do
      begin
        TranslateMessage(Msg);             // Translate any keyboard Msg's
        DispatchMessage(Msg);              // Send it to our WindowProc
      end;                                 // for processing.
    
    end. 
    

    【讨论】:

    • 您误解了 OP。他有一个他希望使用的自定义控件TMyCustomControl
    • 但我的问题是我可以在 win api 表单上显示 vcl(非 vcl 表单)
    • 如果您在窗口程序中注册所有需要的 vcl 类、父类等(可能包装了其中一些),为什么不呢?我来宾使用复杂的 vcl 类将不是一件容易的事。
    • 您接受的答案不能回答您的问题!奇怪。我在这里根本看不到任何 VCL。它仅使用 Windows 和消息。这是一个经典的 Petzold 风格的 Windows 程序。
    • @David Heffernan 是的,但我在自己的答案中为这个答案添加了一些代码
    猜你喜欢
    • 2011-01-20
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 2013-04-11
    • 2012-03-25
    • 2014-11-06
    相关资源
    最近更新 更多