如何控制其他程序窗体上的窗口控件
- 格式:doc
- 大小:36.00 KB
- 文档页数:6
weifenluo控件窗口停靠的原理-回复标题:【WeifenLuo控件:窗口停靠的原理详解】一、引言WeifenLuo.WinFormsUI.Docking,简称DockPanel Suite,是由C#编写的.NET开源库,为Windows窗体应用程序提供了一套强大的停靠功能。
本文将深入剖析WeifenLuo控件中窗口停靠的原理,一步步揭示其背后的设计逻辑与实现机制。
二、基本概念与组件在WeifenLuo控件库中,核心组件是DockPanel和DockControlPane。
DockPanel是所有停靠窗体的容器,而DockControlPane则是窗体实际停靠的位置,可以理解为停靠区域或者停靠面板。
1. DockPanel:作为顶级容器,DockPanel负责管理所有的DockControlPane和停靠窗体,控制它们的布局和行为。
它允许窗体在它的边缘(上、下、左、右)以及内部(填充)进行停靠,并且支持浮动窗口和自动隐藏的功能。
2. DockControlPane:DockControlPane是窗体停靠的具体位置,每个DockControlPane内只能有一个窗体。
当窗体被拖动到DockPanel的不同位置时,会动态创建或调整DockControlPane来适应新的布局需求。
三、窗口停靠过程解析1. 初始化停靠:首先,窗体需要设置CanDock属性为true以启用停靠功能,然后通过DockPanel.DockStyle属性将其添加到DockPanel中并指定初始停靠位置。
例如,如果希望窗体停靠在主界面底部,则可设置.DockStyle为DockBottom。
2. 拖动停靠:在运行时,用户可以通过鼠标拖拽窗体标题栏改变其停靠位置。
当窗体被拖动至DockPanel边界时,DockPanel会检测到这一动作并创建或调整相应的DockControlPane,同时更新窗体的DockControl.DockStyle属性。
Qt Creator 窗体控件自适应窗口大小布局常见的软件窗口大小改变(最大化、手动改变时)需要窗口的部件能够自适应布局,而在Qt的应用程序界面设计中,对于像我一样的初学者如何实现窗口自适应调整还是要绕点弯路的。
网上百度了很多,多数说的很含糊,还有很多是用程序实现的,既然已经有Qt Creator那么高集成度的工具了,我还是倾向于直接在Qt Creator中通过可视化配置的方式完成,一是所见即所得,而是效率要高不少。
Qt中如果想实现窗体内空间随着窗体大小调整,必须使用布局管理,常用的布局管理有QHBoxLayout、QVBoxLayout、QGridLayout,空的地方使用spacer 控件进行填充,因此首先将窗体空间使用布局管理典型应用如下图所示。
我这里使用QGridLayout,按住Ctrl多选需要布局的窗体控件,右键-布局-栅格化局,根据需要进行调整。
要想是控件根据窗体进行调整,最为重要的一点就是设置窗口部件的大小策略,各控件均有这一项设置,如下图所示。
这部分具体的参数解释摘录如下:结合控件的SizePolicy属性,来控制布局管理中的控件的尺寸自适应方式。
控件的sizePolicy说明控件在布局管理中的缩放方式。
Qt提供的控件都有一个合理的缺省sizePolicy,但是这个缺省值有时不能适合所有的布局,开发人员经常需要改变窗体上的某些控件的sizePolicy。
一个QSizePolicy的所有变量对水平方向和垂直方向都适用。
下面列举了一些最长用的值:A. Fixed:控件不能放大或者缩小,控件的大小就是它的sizeHint。
B. Minimum:控件的sizeHint为控件的最小尺寸。
控件不能小于这个sizeHint,但是可以放大。
C. Maximum:控件的sizeHint为控件的最大尺寸,控件不能放大,但是可以缩小到它的最小的允许尺寸。
D. Preferred:控件的sizeHint是它的sizeHint,但是可以放大或者缩小E. Expandint:控件可以自行增大或者缩小注:sizeHint(布局管理中的控件默认尺寸,如果控件不在布局管理中就为无效的值)所以对于需要根据窗口大小对应改变的部件我这里就设置为Expandint。
delphi中在自己的窗体上嵌入其它程序基于Delphi的融合DLL中的窗体博客分类:•delphiDelphiWindows编程基于Delphi的融合DLL中的窗口摘要:提出了一种简单的方法将DLL中的窗口融合(嵌入)到其他应用程序或DLL的窗口中,使用本方法可以简便地实现具有强扩展性和升级能力的软件系统。
1 引言在开发一个大型通用控制系统时曾遇到这么一个问题:该系统软件包由若干个可执行文件和动态链接库组成,因为扩展性和兼容性的要求,需要将系统划分为若干个可执行文件和动态链接库,并且在大部分DLL中封装各自的操作界面,在调用DLL时将其中包含的部分界面嵌入地显示在主界面的某个区域或某个窗口内,与主界面的其他部分浑然一体。
这样主程序与DLL在功能操作上各司其职,在外部界面上又彼此交融,使用户可以通过增加和修改DLL来实现对系统内部、外部的扩展和升级;同时因为DLL的跨语言特性,内部包含操作界面的DLL可以更为方便地在以后的不同工作、不同语言环境中更好地重复使用。
这一问题的应用较为广泛,但没有充分的资料来帮助解决,经过不断的试验,笔者将初步体会总结出来,用以抛砖引玉。
本文中涉及的主程序和DLL都是在Delphi5.0下实现的,但因为其中所依赖的基础还是Windows本身的窗口机制,所以对于其他的语言平台也有实际意义。
在Delphi中如何创建DLL及输出DLL中的函数有较多资料进行过介绍,在本文中不再赘述,本文只针对DLL中的窗口部分做重点介绍。
2 DLL中自带窗口的创建和显示DLL和普通EXE一样,可以自带窗口,用Delphi设计包含窗口的DLL较其他语言更为方便。
在Delphi的DLL工程中,窗口的生成和编程与普通的EXE工程基本相同,但与EXE文件不同的是:在Delphi的EXE工程中所包含的窗口是自动创建的,而DLL工程中所包含的窗口需要显示创建。
在通常的应用中,DLL将所包含的窗口的创建和显示函数(或过程)输出,由宿主程序根据情况调用将DLL中的窗口显示出来(如点击宿主程序中的某个按钮时显示DLL中的窗口),其窗口创建和显示的过程如下:首先,创建一个DLL工程,并新建一个名为DllForm的Form,可以在该Form上放置任何控件。
menustrip控件用法什么是menustrip控件menustrip控件是一个用于创建菜单栏的工具。
菜单栏是指在应用程序窗口的顶部显示的水平导航栏,通常用于显示应用程序的各种功能和选项。
menustrip控件可以包含多个菜单项,每个菜单项都可以包含子菜单项或者是与之关联的操作。
通过点击菜单项,用户可以选择执行相应的操作或打开子菜单。
menustrip控件通常用于创建简洁明了的用户界面,使用户可以方便地访问和操作应用程序的功能。
menustrip控件的基本用法下面介绍menustrip控件的基本用法。
添加menustrip控件在Windows窗体应用程序中,要使用menustrip控件,首先需要将它添加到窗体中。
1.打开Visual Studio,创建一个新的Windows窗体应用程序项目。
2.在窗体设计器中,找到工具箱中的”MenuStrip”控件,并将其拖放到窗体上。
添加菜单项menustrip控件中可以添加多个菜单项,每个菜单项可以包含子菜单项或执行操作。
1.在menustrip控件上,右键单击并选择”Add Menu Item”。
2.输入菜单项的文本,例如”File”,然后按下回车键。
3.再次右键单击menustrip控件,并选择”Add Menu Item”。
4.输入另一个菜单项的文本,例如”Edit”,然后按下回车键。
添加子菜单项对于每个菜单项,我们可以添加子菜单项以创建多层菜单结构。
1.选中一个菜单项,例如”File”。
2.右键单击该菜单项,并选择”Add Menu Item”。
3.输入该子菜单项的文本,例如”New”,然后按下回车键。
关联操作菜单项可以关联一个操作,当用户选择该菜单项时,执行相应的操作。
操作可以是打开一个窗体、执行一个方法等。
1.选中一个菜单项,例如”New”。
2.在属性窗口中,找到”Click”事件,并双击该事件。
3.在事件处理程序中,编写执行的操作代码。
以下是一个示例代码:private void newToolStripMenuItem_Click(object sender, EventArgs e){// 打开一个新窗体或执行其他操作}menustrip控件的高级用法除了基本的菜单创建和操作关联,menustrip控件还支持许多高级用法。
如何控制其他程序窗体上的窗口控件回调函数写出来不是自己的程序去调用的,反而是让其他的东西去调用,比如windows 操作系统,比如其他的程序等等之类的。
但是什么时候被调用却不知道了。
回调函数一般是按照调用者的要求定义好参数和返回值的类型,你向调用者提供你的回调函数的入口地址,然后调用者有什么事件发生的时候就可以随时按照你提供的地址调用这个函数通知你,并按照预先规定好的形式传递参数。
所以很多人打比方,说回调函数还真有点像您随身带的BP 机:告诉别人号码,在它有事情时Call您!所以一个回调函数写出来之后,一定有个注册的动作,就是告诉调用者,你怎么样找到我写的函数。
某些Windows API 函数会要求以回调函数地址作为其参数之一,例如SetTimer 、LineDDA 、EnumObjects,以及我们下面要用到的EnumWindows。
在Delphi里声明一个回调函数的格式很简单,例如:function EnumWindowsProc(AhWnd:LongInt;lParam:LongInt):boolean;stdcall;首先是函数名称可以随便乱取,但函数参数的类型一般不得乱来,其顺序,数据类型等都有规定的,因为这些都是让其他程序调用的,他们已经规定好了的,但参数名称可以随便乱叫。
注意后面一定要带上“stdcall”,stdcall是标准调用,也就是说采用标准windows参数传递方式来调用函数。
编写函数体就很简单了,利用传递过来的参数就可以了,只要记住,这些参数是别人送给你的,你只要知道这些参数代表了什么意思。
再看个向调用者注册回调函数入口地址的函数。
function EnumWindows(lpEnumFunc: TFNWndEnumProc; lParam: LPARAM): BOOL; stdcall;TFNWndEnumProc其实就是指针类型。
其中的lpEnumFunc就是回调函数的入口地址了。
下面是调用EnumWindows的格式:EnumWindows(@EnumWindowsProc,0);通过向系统注册回调函数的入口地址,系统就能在需要的时候,调用回调函数,传递参数给它,也许这些参数就是我们想要的。
EnumWindows函数的功能是:枚举屏幕上所有程序中的顶层窗口,将窗口句柄以参数的形式传递给回调函数。
找到一个窗口,就调用一次回调函数。
枚举结束的条件是:要么枚举完所有的窗口,要么回调函数返回False。
lParam: LPARAM参数是程序定义的值,这个值被传递到回调函数。
回过头来再看一下EnumWindowsProc:function EnumWindowsProc(AhWnd:LongInt;lParam:LongInt):boolean;stdcall;当系统找到了一个窗口后,就开始调用这个回调函数,将窗口的句柄作为第一个参数传递过来,将在EnumWindows中lParam: LPARAM这个程序定义的值作为第二个参数传递过来。
所以我们可以在EnumWindowsProc函数中利用传递过来的两个参数来做某些处理了。
下面我们新建一个程序列举系统中所有程序的顶层窗口,我们要得到窗口的标题,要得到窗口类名称。
得到窗口标题用:function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer; stdcall;该函数功能是将窗口句柄为hWnd的窗口的标题拷入到一个缓冲区lpString。
nMaxCount是拷入缓冲区内的最大的字符数。
要得到窗口标题还可以发送消息:WM_GETTEXT,其实GetWindowText就是发送WM_GETTEXT消息的。
要得到窗口类名称用:function GetClassName(hWnd: HWND; lpClassName: PChar; nMaxCount: Integer): Integer; stdcall;其参数意义和上面的函数差不多。
不详细解释了。
我们先编写回调函数:EnumWindowsProc。
现在告诉自己,我们已经有了两个参数的值了。
这两个参数是系统给我们的.为了显示窗口标题和类名,我们用一个TMemo控件。
先在interface部分声明函数。
function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;stdcall;注意我将第二个参数改了,不要紧,到时候调用的时候注意看。
然后在implementation部分定义函数:function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;varlpszClassName,lpszWindowText:array[0..254] of char; //定义两个缓冲区。
beginGetWindowText(AhWnd,lpszWindowText,254); //得到窗口标题GetClassName(AhWnd,lpszClassName,254); //得到窗口类名。
Aform.memo1.lines.add(StrPas(lpszWindowText));Aform.memo1.lines.add(StrPas(lpszClassName));Aform.memo1.lines.add('--------------------');Result:=True;end;接着需要做的就是调用EnumWindows函数,注册回调函数入口地址,让系统调用回调函数,列举窗口了。
所以再添加一个TButton: btn_listwindowprocedure TForm1.btn_listwindowClick(Sender: TObject);beginEnumWindows(@EnumWindowsProc,LongInt(self));end;程序清单如下:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Memo1: TMemo;btn_listwindow: TButton;procedure btn_listwindowClick(Sender: TObject);private{ Private declarations }public{ Public declarations }end;varForm1: TForm1;function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;stdcall;implementation{$R *.dfm}function EnumWindowsProc(AhWnd:LongInt;AForm:TForm1):boolean;varlpszClassName,lpszWindowText:array[0..254] of char;beginGetWindowText(AhWnd,lpszWindowText,254);GetClassName(AhWnd,lpszClassName,254);Aform.memo1.lines.add(StrPas(lpszWindowText));Aform.memo1.lines.add(StrPas(lpszClassName));Aform.memo1.lines.add('--------------------');Result:=True;end;procedure TForm1.btn_listwindowClick(Sender: TObject);beginEnumWindows(@EnumWindowsProc,LongInt(self));end;end.F9,运行,看看结果。
最好是F7单步跟踪调试一下,看看回调函数是怎么被调用的。
有了回调函数的概念及上面的例子,我们可以继续了。
其实想要找到一个标题已知的窗口句柄,用一个API函数就可以了:FindWindow.其函数原形是:function FindWindow(lpClassName, lpWindowName: PChar): HWND; stdcall;lpClassName:窗口类名.如果只知道标题,可以为空.窗口类名可以用很多工具获得.如winsignt32.lpWindowName:窗口标题.调用方式举例:var wndhwnd:HWND;wndhwnd:=FindWindow(nil,'某窗口标题');if wndhwnd<>0 then file://找到此窗口句柄.beginxxxxxendelse beginMessageBox(self.handle,'没找到该窗口句柄','提示',0);end;有了这个窗口句柄,就离我们的初始目的不远了:控制其他窗体上的窗口控件.同样,首先要得到其他窗体上窗口控件的句柄.我们用这个API函数:EnumChildWindows.其函数原形是:function EnumChildWindows(hWndParent: HWND; lpEnumFunc: TFNWndEnumProc;lParam: LPARAM): BOOL; stdcall;这个函数和EnumWindow函数很有些想象.其作用也很相似.它的功能就是列举窗口句柄为hWndParent的窗体上所有的窗口控件的句柄.同样也是以回调函数参数的形式给出的.我们再举一个实际的例子,来说明这个函数的用法.程序的功能是让用户输入一个窗口标题,然后调用FindWindow函数找到此窗口句柄.通过这个句柄,我们在一个Memo里显示该窗口上所有的窗口控件.同样先编写回调函数.function EnumChildWndProc(AhWnd:LongInt;AlParam:lParam):boolean;stdcall;varWndClassName: array[0..254] of Char;WndCaption: array[0..254] of Char;beginGetClassName(AhWnd,wndClassName,254);GetWindowText(aHwnd,WndCaption,254);with form1.memo1 dobeginlines.add( string(wndClassName));lines.add( string(wndCaption));lines.add('-------');end;result:=true;end;然后在一事件里调用EnumChildWindows函数.procedure TForm1.Button1Click(Sender: TObject);varhWnd:LongInt;beginmemo1.Lines.Clear;Memo1.Lines.Add(Edit1.Text+' 有如下控件类名称');hWnd:=FindWindow(nil,pchar(Edit1.Text));if hWnd<>0 thenbeginEnumChildWindows(hWnd,@EnumChildWndProc,0);endelse MessageBox(self.handle,'没找到该窗口句柄','提示',0);end;程序清单如下:unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls;typeTForm1 = class(TForm)Memo1: TMemo; file://用来显示找到的控件Label1: TLabel;Edit1: TEdit;file://输入标题.Button1: TButton;procedure Button1Click(Sender: TObject);private{ Private declarations }public{ Public declarations }end;varForm1: TForm1;function EnumChildWndProc(AhWnd:LongInt; AlParam:lParam):boolean;stdcall;implementation{$R *.dfm}function EnumChildWndProc(AhWnd:LongInt; AlParam:lParam):boolean;stdcall;varWndClassName: array[0..254] of Char; WndCaption: array[0..254] of Char;beginGetClassName(AhWnd,wndClassName,254); GetWindowText(aHwnd,WndCaption,254);with form1.memo1 dobeginlines.add( string(wndClassName));lines.add( string(wndCaption));lines.add('-------');end;result:=true;end;procedure TForm1.Button1Click(Sender: TObject); varhWnd:LongInt;beginmemo1.Lines.Clear;Memo1.Lines.Add(Edit1.Text+' 有如下控件类名称'); hWnd:=FindWindow(nil,pchar(Edit1.Text));if hWnd<>0 thenbeginEnumChildWindows(hWnd,@EnumChildWndProc,0);endelse MessageBox(self.handle,'没找到该窗口句柄','提示',0);end;end.有了控件句柄,我们当然就可以随心所欲了.比如:SendMessage(hWnd,WM_SETTEXT,0,LongInt(Pchar('sdafdsf')));就可以给控件发送文本.其他还可以发送不同的消息可以做很多事情.但是,有很大一个问题:假设一个窗体上有很多相同的控件,并且根本没办法区分他们,即使我们能找到所有的控件句柄,我们又不能区分到底哪个是我们想要的,同样是干着急.我想了很长时间,后来在大富翁里找到了答案,只要用到一个小技巧,就可以解决了.运行要控制的窗口程序。