Delphi Dll封闭Form 嵌入TabSheet中,失去焦点

lyhoo163 2018-11-18 11:22:26
各位码友,我在解决Delphi Dll封闭Form 嵌入TabSheet中,失去焦点问题上。未寻到好的解决办法。
所谓焦点,就是鼠标不能激活DBGrid的Cell,和一些常用的Tab键等,推动功能。

Delphi Dll封闭Form 常见的办法:
1、Delphi Dll封闭Form作为MDI了窗口,是有焦点的。
2、Form 设置 fsNormal,作为普通的窗口,也是有焦点的。

但是,使用TabSheet1,嵌入TabSheet的Page中,就出现焦点问题。寻求各位,有何解决办法。
或第三方控件,有类似的功能,请推荐。谢谢!
...全文
558 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
th9999 2019-06-13
  • 打赏
  • 举报
回复
lyhoo163,你的DLL焦点问题解决没有?
lyhoo163 2018-11-27
  • 打赏
  • 举报
回复
感谢 doloopcn、 wdonghai 和“早打大打打核战争” ,提供解决建议和代码。
lyhoo163 2018-11-27
  • 打赏
  • 举报
回复
问题解决了。非常感谢:doloopcn、早打大打打核战争年代的思路。特别是doloopcn消息处理 的帮助。

解决的办法,从消息处理上,一直调试不出来。最后,还是从嵌入的方式上。让form在Tab中。

doloopcn 2018-11-26
  • 打赏
  • 举报
回复
发现问题:使用下条语句
MyForm.ManualDock(MyTabSheet); // 嵌入PageControl1.Pages
DllForm可以嵌入PageControl1中,但无焦点。此语句,是出现无焦点的关键。

这就是一个开始说的问题了,你要用API,那你跟着的开发最好都是用API实现的,尽量不要用DELPHI的进行这种操作,因为有些结果是不可预科的

SendMessage没有反应,你得查看是哪个部位把消息给拦截了,这些都得用API

你先用我上的代码进行测试,我在D7+Win7x64上调试是能过的,然后再回到项目测试一下,才可能找到问题

简单例子:你有MainForm=FORM1、非MDI子窗口=FORM2=嵌入TABSHEET的父窗口,
在DBGrid上按下任意键,如果消息是在FORM2拦截的,那么MSG.HWND应该是DBGrid的HANDLE;
如果消息是在FORM1拦截的,那么MSG.HWND应该是FORM1,这个时候你得用MSG.pt参数看一下事件是不是发生在DBGrid上:Windows.WindowFromPoint(MSG.pt)=dyFormDBGridHWND;

别外HWND是INTEGER形式,你用工具查询到的HWND一般都是十六进制,你调试的时候应该用'$'+IntToHex(MSG.HWND,8),以免发生误别
lyhoo163 2018-11-26
  • 打赏
  • 举报
回复
引用 24 楼 doloopcn 的回复:
用SendMessage的方法,没有你说的情况

主调用窗口是MainForm还是MDI FORM?
如果是MDI FORM的话,你得调试Application.OnMessage在哪个位置触发
如果Application.OnMessage是由Main FORM 触发的话,那是有问题的.

解决办法 是:无论在哪里触发,你都应该在TabSheet的Form中SendMessage给DBGrid,而不能跨域SendMessage

1、主调用窗口是MainForm
2、不使用MDI方式,
3、
procedure TForm1.ShareEvents(var MSG: tagMSG; var Handled: Boolean);
begin
if MSG.hwnd=dyFormDBGridHWND then
begin
showMessage(Inttostr(dyFormDBGridHWND)); // 调试:1、发现Hamdle不一样 2、当有显示时,句柄相同时,下列消息无效果
Handled:=True; //这句必须有,作用是优先处理下面的SendMessage
//一定要用SendMessage,不能用PostMessage
//也就是说一定要执行完才返回,不然消息会发生冲突
SendMessage(MSG.hwnd,MSG.message,MSG.wParam,MSG.lParam);
Handled:=False;//这句必须有,作用是让当前Application默认方式去处理消息
end
else begin
Handled:=False;
end;
end;

4、“解决办法 是:无论在哪里触发,你都应该在TabSheet的Form中SendMessage给DBGrid,而不能跨域SendMessage”具体
操作?
lyhoo163 2018-11-26
  • 打赏
  • 举报
回复
发现问题:使用下条语句
MyForm.ManualDock(MyTabSheet); // 嵌入PageControl1.Pages
DllForm可以嵌入PageControl1中,但无焦点。此语句,是出现无焦点的关键。

我试着不用此语句,有焦点了。正在........
doloopcn 2018-11-25
  • 打赏
  • 举报
回复
用SendMessage的方法,没有你说的情况

主调用窗口是MainForm还是MDI FORM?
如果是MDI FORM的话,你得调试Application.OnMessage在哪个位置触发
如果Application.OnMessage是由Main FORM 触发的话,那是有问题的.

解决办法 是:无论在哪里触发,你都应该在TabSheet的Form中SendMessage给DBGrid,而不能跨域SendMessage
lyhoo163 2018-11-25
  • 打赏
  • 举报
回复
补充:是动态调用Dll。
lyhoo163 2018-11-25
  • 打赏
  • 举报
回复
搞了一天,未成功。
发现一个问题Dll中的DBGrid 的Handle,创建时和嵌入Tabsheel中的句柄不同。
Dllf封闭Form作为普通的窗口,一切正常。嵌入到TabSheel中,就是不行。
但是,一次偶然机会,让DBGrid的OnChick继承代码时,调试成功。但修改过程中,不知何原因。又还原了。
doloopcn 2018-11-23
  • 打赏
  • 举报
回复
主要的机制问题已经明白了:调用主窗口的Application将消息优先处理,导致DLLForm.DBGrid1收不到消息

解决办法:
1.截获主窗口的消息处理事件
2.优先处理DBGrid1的消息事件
3.返加处理主窗口消息事件


待优化功能:
有的消息可能会发生两次,有待你在事件中做更详细的处理。如:按下向左键、向右键等

主要代码贴在下面:
MainForm主要代码
###################
unit TestDLLFORM;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, ComCtrls;

type
TForm1 = class(TForm)
PageControl1: TPageControl;
TabSheet1: TTabSheet;
Panel1: TPanel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
procedure ShareEvents(var MSG: tagMSG; var Handled: Boolean);
public
{ Public declarations }
end;

var
Form1: TForm1;
dyForm: TForm;
dyFormDBGridHWND:HWND;

function ShowDLLForm(AParent:THandle;var DBGHWND:HWND):TForm;stdcall;external 'DLLFRM.DLL';

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
//记得把DBGrid1的HWND返回来,其实感觉返回TForm没有多大用处,DLL或外部程序的调用一般都得直接用Handle去处理问题
dyForm:=ShowDLLForm(SELF.Handle,dyFormDBGridHWND);
end;

procedure TForm1.ShareEvents(var MSG: tagMSG; var Handled: Boolean);
begin
if MSG.hwnd=dyFormDBGridHWND then
begin
Handled:=True; //这句必须有,作用是优先处理下面的SendMessage
//一定要用SendMessage,不能用PostMessage
//也就是说一定要执行完才返回,不然消息会发生冲突
SendMessage(MSG.hwnd,MSG.message,MSG.wParam,MSG.lParam);
Handled:=False;//这句必须有,作用是让当前Application默认方式去处理消息
end
else begin
Handled:=False;
end;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
//截获当前Application的消息处理事件
Application.OnMessage:=ShareEvents;
end;

end.

#############################
DLLFORM代码
unit uDLLFORM;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, ADODB, StdCtrls;

type
TDLLFORM = class(TForm)
Memo1: TMemo;
qry1: TADOQuery;
ds1: TDataSource;
DBGrid1: TDBGrid;
procedure FormShow(Sender: TObject);
procedure qry1AfterOpen(DataSet: TDataSet);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;

var
DLLFORM: TDLLFORM;

function ShowDLLForm(AParent:THandle;var DBGHWND:HWND):TForm;stdcall;

implementation

{$R *.dfm}

function ShowDLLForm(AParent:THandle;var DBGHWND:HWND):TForm;stdcall;

begin
DLLForm:=TDLLForm.Create(nil);
DLLForm.BorderStyle:=bsNone;
DLLForm.Align:=alClient;
Windows.SetParent(DLLForm.Handle,AParent);
DLLForm.Show;
DBGHWND:=DLLForm.DBGrid1.Handle;
Result:=DLLForm;
end;
procedure TDLLFORM.FormShow(Sender: TObject);
begin
qry1.Open;
end;

procedure TDLLFORM.qry1AfterOpen(DataSet: TDataSet);
var
i:Integer;
begin
for i:=0 to DBGrid1.Columns.Count-1 do
DBGrid1.Columns[i].Width:=80;
end;

procedure TDLLFORM.FormClose(Sender: TObject; var Action: TCloseAction);
begin
qry1.Close;
Action:=caFree;
end;

end.
  • 打赏
  • 举报
回复
就是新建frame替代form,整个UI都放在frame上,file->new->other->Delphi Files,选FireMonkey Frame或者VCL Frame,早期版本可能位置有所不同。用TFrame的一个重要优点是可以保存到组件面板上,对于复用性高的UI设计,以后直接拖一个到窗体中就可以了。
lyhoo163 2018-11-22
  • 打赏
  • 举报
回复
@早打大打打核战争
好的建议,我试试看。有可以参考的 TFrame类似代码吗,可以提供参考学习了。谢谢!
lyhoo163 2018-11-22
  • 打赏
  • 举报
回复
@doloopcn 没有错误,显示正常。就是不能对DBGrid进行编辑,但DBGrd中的bit字段,鼠标可以点击选择,但其它数据不能进入编辑,就象ReadOnly了一样,如果使用DBEdit关联字段,也可以编辑,就是DBGrid不能,无焦点进入编辑。
lyhoo163 2018-11-22
  • 打赏
  • 举报
回复
再补充一句,目前,Dll封闭的Form ,使用MDI子窗口或非MDI普通窗口,创建正常。就是插入到PageControl1不正常。
lyhoo163 2018-11-22
  • 打赏
  • 举报
回复
补充说明:
1、软件使用DataSnap C/S方式 ,
2、Form封闭进Dll中,
3、MainFrm中的PageControl1,TabShee中创建Dll封闭的Form
4、Dll封闭的Form成功在 TabShee中创建,就是DBGrid不能录入,即无焦点。

之前,使用动态Form,TabShee中创建动态Form不存在此类问题。
请大家,提出宝贵建议和意见。

lyhoo163 2018-11-22
  • 打赏
  • 举报
回复
谢谢 doloopcn 我努力试试,
能提供代源码,少走弯路。
谢谢!回复 1yhoo163@163.com
doloopcn 2018-11-22
  • 打赏
  • 举报
回复
引用 2 楼 lyhoo163 的回复:
[quote=引用 1 楼 doloopcn 的回复:]
近期经常涉及控制第三方控件的案例,感觉上,嵌入在程序中的外部程序不通过API控制起来也是问题多多

个人认为,在封装成DLL之前,将你的SetFoucus、TAB之类的方法都以函数形式导出,主程序以函数的形式直接调用

如要控制DBGrid的Cell,写一函数function dbgridcell(row,col,value):Boolean;stdcall;

然后在主程序这边执行dbgridcell就好了


能提供参考代码吗?[/quote]

不知道是否符合你的需求,希望可以给到你灵感
doloopcn 2018-11-22
  • 打赏
  • 举报
回复
有点不能准确理解楼主的意图,给一个回调的例子,你可以把DLLFORM中的事件、消息返回主程序的方法:
主程序代码:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Tabs, ComCtrls, Grids, DBGrids;

type
TCallBackProc_DBGrid_MouseMove=procedure (Sender: TObject; Shift: TShiftState; X,Y: Integer) of object;
TCallBackProc_DBGrid_CellClick=procedure (Column: TColumn) of object;
TForm1 = class(TForm)
Button1: TButton;
PageControl1: TPageControl;
TabSheet1: TTabSheet;
TabSheet2: TTabSheet;
Button2: TButton;
StatusBar1: TStatusBar;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);

procedure DBGrid_CellClick(Column: TColumn);
procedure DBGrid_MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);

private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

function ShowDLLForm(AParent:THandle;
CallBackProc_DBGrid_MouseMove:TCallBackProc_DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:TCallBackProc_DBGrid_CellClick):TForm;stdcall;external 'DLLFrm.dll';

procedure TForm1.DBGrid_CellClick(Column: TColumn);
begin
Application.MessageBox(PChar('DLL FORM 中的DBGrid_CellClick,Col:'+IntToStr(Column.Index)),'系统提示');
end;

procedure TForm1.DBGrid_MouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
StatusBar1.Panels[0].Text:='MouseMove';
StatusBar1.Panels[1].Text:=Format('X: %d ',[X]);
StatusBar1.Panels[2].Text:=Format('Y: %d ',[Y]);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
CallBackProc_DBGrid_MouseMove:TCallBackProc_DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:TCallBackProc_DBGrid_CellClick;
begin
CallBackProc_DBGrid_MouseMove:=DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:=DBGrid_CellClick;
ShowDLLForm(TabSheet1.Handle,CallBackProc_DBGrid_MouseMove,CallBackProc_DBGrid_CellClick);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
CallBackProc_DBGrid_MouseMove:TCallBackProc_DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:TCallBackProc_DBGrid_CellClick;
begin
CallBackProc_DBGrid_MouseMove:=DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:=DBGrid_CellClick;
ShowDLLForm(TabSheet2.Handle,CallBackProc_DBGrid_MouseMove,CallBackProc_DBGrid_CellClick);
end;

end.
##############################################
DLL入口代码:
library DLLFrm;

{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }

uses
SysUtils,
Classes,
uDLLFrm in 'uDLLFrm.pas' {DLLFORM};

{$R *.res}
//这些地方一般都不要包含代码
exports
ShowDLLForm;
begin
end.
####################################
DLLFORM代码:
unit uDLLFrm;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Grids, DBGrids;

type
TCallBackProc_DBGrid_MouseMove=procedure (Sender: TObject; Shift: TShiftState; X,Y: Integer) of object;
TCallBackProc_DBGrid_CellClick=procedure (Column: TColumn) of object;
TDLLFORM = class(TForm)
Memo1: TMemo;
DBGrid1: TDBGrid;
private
{ Private declarations }
public
{ Public declarations }
end;

var
DLLFORM: TDLLFORM;

function ShowDLLForm(AParent:THandle;
CallBackProc_DBGrid_MouseMove:TCallBackProc_DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:TCallBackProc_DBGrid_CellClick):TForm;stdcall;

implementation

{$R *.dfm}

function ShowDLLForm(AParent:THandle;
CallBackProc_DBGrid_MouseMove:TCallBackProc_DBGrid_MouseMove;
CallBackProc_DBGrid_CellClick:TCallBackProc_DBGrid_CellClick):TForm;stdcall;

begin
DLLForm:=TDLLForm.Create(nil);
DLLForm.BorderStyle:=bsNone;
DLLForm.Align:=alClient;
Windows.SetParent(DLLForm.Handle,AParent);
DLLForm.Show;
DLLForm.DBGrid1.OnMouseMove:=CallBackProc_DBGrid_MouseMove;
DLLForm.DBGrid1.OnCellClick:=CallBackProc_DBGrid_CellClick;
result:=DLLForm;
end;
end.
#########################################
运行结果:


  • 打赏
  • 举报
回复
应该用TFrame,不要用TForm,DLL中实例化一个Frame传给主程序,主程序将其嵌入到Form中

doloopcn 2018-11-22
  • 打赏
  • 举报
回复
引用 15 楼 lyhoo163 的回复:
再补充一句,目前,Dll封闭的Form ,使用MDI子窗口或非MDI普通窗口,创建正常。就是插入到PageControl1不正常。


建议把你的错误界面发我看一下doloopcn@163.com
加载更多回复(9)

5,378

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧