Delphi中没有VCL表单的组件

4

这可能是一个关于现实世界中不需要的问题,但我想知道我们是否可以做到(仅供了解)。

我们可以创建和使用非可视化vcl组件,但是能否在可视化vcl中实现并使其在屏幕上可见呢?

或者至少我们能否将vcl放置在使用Windows API创建的窗体内部?


1
是的,这可能是可行的,但可能不太方便。 - Andreas Rejbrand
这是一个很好的评论,因为你说了“是”!但是“怎么做”呢? - VibeeshanRC
你为什么在注释里问如何做?在问题中你说你不需要这样做。我认为它是可以实现的。个人而言,我会将其托管在 TForm 的子类中,并使用 ParentWnd 属性作为其父级。 - David Heffernan
5个回答

6
不,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的了解,所以我认为你是正确的!! - David Heffernan
1
@dthorpe "在线文档仍有很大的提升空间" 话说得好!CreateParented函数需要一个HWND参数,该参数最终会出现在Params.WndParent中。我不确定您是否需要对样式标志进行太多操作,我认为您只需设置Form.Align := alClient即可。我用这种方法将VCL表单放置在其他VCL表单中。你确定你不想回到Delphi产品开发吗?!!你和Barry会是一个非常强大的团队!!! - David Heffernan
1
@David:再次使用Delphi代码将是一种享受,但一旦你离开了家,你就永远无法回到过去。 - dthorpe
@dthorpe:您当然是正确的,但我们用户只能梦想!如果您留下来,我相信现在我们已经有了64位Delphi,这是我特别关注的一个问题。至少听起来在这个领域取得了很好的进展。 - David Heffernan
@David:这不是适合此类对话的场所。如果你想的话,可以通过电子邮件或推特联系我。 - dthorpe
显示剩余3条评论

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. 

3
你误解了原帖作者的意思。他有一个名为 TMyCustomControl 的自定义控件,希望能够使用它。 - Andreas Rejbrand
但我的问题是,我能否在Win API表单(非VCL表单)上显示VCL? - VibeeshanRC
为什么不在窗口程序中注册所有所需的VCL类、父类等(可能需要包装其中一些)?我猜使用复杂的VCL类不是一件容易的事情。 - volvox
3
您已接受一个与您问题无关的答案!奇怪。我根本看不到 VCL,它只使用了 Windows 和 Messages。这是一个经典的 Petzold 风格的 Windows 程序。 - David Heffernan
@ David Heffernan 是的,但我在我的答案中添加了一些代码。 - VibeeshanRC

3
对于VCL组件,答案显然是肯定的。组件可以在不需要表单上下文的情况下使用,只需通过指定nil绕过所有权机制,并在完成后手动销毁实例即可,非常方便。
NoLongerDependsOnForm := TSomeComponent.Create(nil);
NoLongerDependsOnForm.This;
NoLongerDependsOnForm.That;
FreeAndNil(NoLongerDependsOnForm);

使用VCL控件会更加复杂,因为任何子窗口都需要父窗口。因此,在通用窗口中重用TWinControl只是理论上可能的(必须编写VCL存根才能使其正常工作)。因此,答案基本上是否定的。
我恳求您,请不要混淆术语“表单”和“窗口”。(进一步阅读:顶级和子窗口)

公平地说,微软让它们混淆起来很容易,因为操作系统被称为Windows,99.99%的人口会将窗体称为Window! - David Heffernan

0

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

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


0

这不仅仅是我的代码,而是所有你们的代码的混合物,对我很有效。

只需添加这些代码到 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

2
更简单的写法是 abtn := TButton.CreateParented(hEdit)。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接