DELPHI中的自定义通用对话框处理

更新时间:2024-03-29 11:39:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

DELPHI中的自定义通用对话框处理和消息处理

一、自定义通用对话框

在Delphi的VCL的Dialog包中存在多个通用对话框,比如TopenDialog(文件选择对话框)、TsaveDialog(文件保存对话框)、TopenPictureDialog(图片打开对话框)、TfontDialog(字体选择对话框)、TfindDialog(文件查找对话框)、TreplaceDialog(文件替换对话框)等13个常用、通用的标准对话框,这里的标准对话框采用的是Windows系统内置的标准对话框,对话框的风格和样式和操作系统的风格和样式是一致的,之所以能够保持一致,是因为通用对话框是由通过对话框模版生成的窗体,只不过这些对话框模版由操作系统定义。

建立自定义通用对话框有两种方式: 1、 修改通过对话框模版来实现的; 2、 通过代码在运行时添加控件;

这里首先介绍一下通过对话框模板方式的实现步骤:

1、对话框模板方式

Delphi的通用对话框位于Delphi系统库的VCL.Dialogs单元,Delphi 中所有通用对话框类都继承自 TCommonDialg 抽象类,

以TfindDialog为例,可以看到创建查找兑换的结构信息如下: tagFINDREPLACEW = packed record

lStructSize: DWORD; { size of this struct $20 }

hWndOwner: HWND; { handle to owner's window } hInstance: HINST; { instance handle of.EXE that

contains cust. dlg. template } Flags: DWORD; { one or more of the fr_?? } lpstrFindWhat: PWideChar; { ptr. to search UnicodeString } lpstrReplaceWith: PWideChar; { ptr. to replace UnicodeString } wFindWhatLen: Word; { size of find buffer } wReplaceWithLen: Word; { size of replace buffer } lCustData: LPARAM; { data passed to hook fn. }

lpfnHook: function(Wnd: HWND; Message: UINT; wParam: WPARAM; lParam: LPARAM): UINT stdcall;

{ ptr. to hook fn. or nil } lpTemplateName: PWideChar; { custom template name }

在这个抽象类中定义了PWideChar 类型的Template属性,这个属性就是用来存放模版标识的,通过在IDE中对操作系统标准的对话框模板进行修改,因为lpTemplateName是保护属性,所以需要对通用对话框进行继承,继承的类中定义好TemplateRes属性,将修改好的模板赋给TemplatesRes属性,例如:TemplateRes := Windows.MakeIntResource(131);其中131为资源ID。

该方法比较麻烦,需要修改模板文件,并引入资源,同时对于新加入的控件,需要捕捉windows消息进行处理,所以推荐使用下面的方式。

2、代码在运行时添加控件

在电子病历编辑器控件中,在标准打印的基础上增加了几个特殊选项,比如

“仅打印奇数页”、“仅打印偶数页”、“奇偶页边距互换”三个选项,对应于标准打印对话框是没有的,这里需要在标准对话框的基础上,在空白区域加上我们需要的控件,这里只要继承标准的打印对话框,在对话框的空白区域,算好控件需要放置的位置就可以了,过程如下:

1、 首先创建TprintDialogEx类继承于标准对话框TprintDialog,并命名为TprintDialogEx;

TPrintDialogEx = class(TPrintDialog)

2、 创建一个Tpanel,计算好位置到空白区域,这里打印窗口是Fixed窗口,空白区域

属于绝对位置;

FExtendedPanel := TPanel.Create(self); with fExtendedPanel do begin

Name := 'ExtendedPanel'; Caption := '';

SetBounds(0, 0, 169, 200); // (204, 5, 169, 200); BevelOuter := bvNone; BorderWidth := 6; TabOrder := 1; End;

3、将3个CheckBox对应FextendedPanel的位置进行放置,并挂接事件就OK了;FCheckBoxOdd:=TCheckBox.Create(Self); with FCheckBoxOdd do begin

Name:='CheckBoxOdd'; Checked:=False;

caption:='仅打印奇数页'; SetBounds( 2, 7, 90, 28 ); Parent := FExtendedPanel; end; ….

这种方法最大的好处是方便,不需要再修改模板并且再去捕捉系统Windows消息,直接挂接事件,对属性进行赋值就可以;其中最大的问题是对应的位置要比较固定,但是这些对话框基本上都是Fix固定大小的窗体,对应的位置并不需要过多的考虑。

二、通用对话框的消息处理

通用对话框的基类TcommonDialog的基类是Tcomponent,但是它并不能按照普通的TWinControl那样进行控件操作和事件的处理,查看Tdialog单元源代码,会发现每个通用对话框,会创建一个TredirectorWindow类,代码如下:

FRedirector := TRedirectorWindow.Create(nil); with TRedirectorWindow(FRedirector) do begin

FFindReplaceDialog := Self; end;

TRedirectorWindow(FRedirector).FFormHandle := GetActiveWindow; FFindReplace.hWndOwner := FRedirector.Handle;

从以下代码可以看到FfindReplace.hWndOwner,即FfindReplace的宿主是Fredirector,再来看TredirectorWindow的定义,TRedirectorWindow = class(TWinControl),其中注释描述如下:

{ TRedirectorWindow }

{ A redirector window is used to put the find/replace dialog into the ownership chain of a form, but intercept messages that CommDlg.dll sends exclusively to the find/replace dialog's owner. TRedirectorWindow creates its hidden window handle as owned by the target form, and the find/replace dialog handle is created as owned by the redirector. The redirector wndproc forwards all messages to the find/replace component. }

重定向window是用来创建一个隐藏的窗口句柄,redirector的处理的Wndproc消息都被重定向到通用对话框组件上。

所以,通用对话框的处理都来自于windows系统的CommDlg.dll,TredirectorWindow用来转发Delphi程序中的消息给通用对话框,这里也可以看出Delphi中的通用对话框是对windows标准对话框进行封装后的结果,windows标准对话框中的控件并不能当做delphi中的控件进行直接访问。

在开发电子病历编辑器控件的过程中,出现一个问题,对于Delphi的ActiveX控件在非模态窗体状态,接受输入法输入时,输入内容呈现的是乱码,如果是英文和数字输入则没有问题,而且模态窗体状态没有这种情况,在独立EXE程序中也没有。

通过跟踪对话框消息,发现在非模态窗体状态,接受的windows的WM_CHAR消息,接收到的Wparam参数内容和正常的情况不一样,在模态状态下,接受的Wparam是4位长度,而在非模态状态下的Wparam是6位长度,并且与正常情况下的内容没有规律可循,经过分析对比发现,模态和非模态对话框在DELPHI中的处理的不同在于:

? 非模态对话框

响应一个消息,系统处理一个消息,处理完毕后返回控制权给Windows; ? 模态对话框

在对话框创建后,挂起外部的消息,只是响应对话框内部的消息,而外部消息则全部\过滤\掉了,直到系统接收到WM_DESTROY或WM_CLOSE后,系统返回控制权给模态对话框创建前的线程,继续模态对话框创建前的线程将执行的代码; 在Windows中有两个接受字符的消息,WM_CHAR和WM_IME_CHAR; 1、 WM_CHAR

如果窗口是unicode(IsWindowUnicode),WM_CHAR的wParam就是unicode字符。如果窗口式ANSI,WM_CHAR的wParam是一个单字节的值。如果输入中文,则会得到2个WM_CHAR,把这两个单字节值合到一起就是输入的中文; 2、 WM_IME_CHAR

如果是unicode window,则wParam就是unicode字符,此时和WM_CHAR没有任何区别。

如果是ansi window,则wParam是双字节的mbc编码。 其中IME(Input Method Editor)的消息处理流程:

当用输入法输入时,首先发送WM_IME_COMPOSITION消息,在这个消息处理中,可以用ImmGetCompsitionString得到输入的字符串。如果不处理,则DefWndProc会针对每个字符串重新发送WM_IME_CHAR,如果WM_IME_CHAR也未处理,则 DefWndProc会再次发送WM_CHAR。

所以,我判断之所以会造成乱码,很有可能是OCX在非模态状态下,即接受了WM_CHAR又接收了WM_IME_CHAR消息,造成最后的wParam是三个字节的内容,造成最后的输出是三个字节的乱码内容。

经过实验,捕捉输入控件的WM_IME_CHAR事件,并将WM_IME_CHAR作为WM_CHAR事件转发出去,就可以解决这个问题了!处理代码如下:

case Message.Msg of WM_IME_CHAR: begin

KeyMsg:=TWMKey(Message);

SendMessage(Edit1.handle, WM_CHAR, KeyMsg.CharCode, 0); end;

以上实验是针对普通的Tform中的Tedit控件进行的实验,但我要解决的问题是在通用对话框中对WM_IME_CHAR消息的处理。

在通用查找对话框中的Edit输入控件根本不是标准TWinControl控件,不能像Delphi中标准控件那样直接挂接WndProc过程,所以必须通过遍历通用对话框中的输入控件,然后再挂接消息处理过程;

1、 2、 3、

首先遍历通用对话框中的控件,判断控件的ClassName,这里ClassName通过Spy++工具可以查看的到;

找到CalssName = ‘Edit’的控件,将其消息处理转到自定义的消息处理过程中; 捕捉WM_IME_CHAR消息,并作为WM_CHAR进行转发,并忽略掉默认的WM_IME_CHAR处理。

代码如下:

//遍历通用对话框中的控件,并判断ClassName

function EnumChildWndProc(AhWnd: LongInt; AlParam: lParam) : Boolean; stdcall; var

WndClassName: array [0 .. 254] of Char; WndCaption: array [0 .. 254] of Char; className, Caption: string; begin

GetClassName(AhWnd, WndClassName, 254); GetWindowText(AhWnd, WndCaption, 254); className := string(WndClassName); Caption := string(WndCaption); if className ='Edit' then begin

SetProp(AhWnd, GWL_WNDPROC));

//挂接到FindReplaceWndProc消息处理过程

SetWindowLong(AhWnd, GWL_WNDPROC, LongInt(@FindReplaceWndProc)); end; Result := True; end;

//FindReplaceWndProc中对WM_IME_CHAR的处理 WM_IME_CHAR: begin

SendMessage(Wnd, WM_CHAR, WParam, 0); Result:=-1;

MakeIntAtom(WndProcPtrAtom),

GetWindowLong(AhWnd,

Exit; end; 三、总结

在Delphi实际开发中,有些在独立EXE程序中可以正常的处理,而在ActiveX控件中则会出现一些莫名其妙的问题,究其原因都是因为在OCX控件中消息处理机制在和独立EXE程序中差异造成的,通过以上问题的解决为今后对错误的处理和判断都有很好的借鉴意义。

本文来源:https://www.bwwdw.com/article/vrnr.html

Top