實現一個熱鍵注冊編輯的類
CST(http://blog.csdn.net/mrtechno) 2005-8-19
1 文檔目的... 1
2 熱鍵編程基礎... 1
2.1 API函數... 1
2.2 編程方法... 2
3 實現概述... 4
4 實現細節... 4
4.1 XML配置文檔結構... 4
4.2 熱鍵編輯控件 ThotKeyEdit 5
4.3 熱鍵自定義窗體類TformHotKeyconfig. 6
4.4 主類ThotKeyConfig. 6
4.4.1 解析XML文檔... 7
4.4.2 注冊注銷系統熱鍵... 8
4.4.3 熱鍵編輯窗體... 8
4.4.4 對象唯一性... 8
5 程序源代碼... 9
6 小結... 25
6.1 沒有解決的一些問題... 25
6.2 程序心得... 26
1 文檔目的本文檔介紹了在Delphi 7中注冊、注銷、使用熱鍵(Hot Key)的基本函數和方法,並在此基礎上介紹了一個熱鍵控制的類THotKeyConfig。該類可以從指定的xml配置文檔中讀入熱鍵配置信息,並在程序指定的位置注冊、注銷、修改熱鍵。
本文將不涉及XML文檔的讀寫方法,也不會詳述控件開發的方法。如果您對這些內容不了解,推薦您先略讀一下相關的文章。關于xml文檔的解析,可以到我的blog找到文章。
2 熱鍵編程基礎2.1 API函數在Delphi7中使用熱鍵要用到如下幾個函數:
//注冊熱鍵
BOOL RegisterHotKey(
HWND hWnd, // 接受熱鍵消息的窗口句柄
int id, // 熱鍵ID
UINT fsModifiers, // 按鍵組合整數
UINT vk // 案件虛擬鍵碼VK
);
其中fsModifiers 值如下:
1: M_strDisplay:='Alt + ';
2: M_strDisplay:='Ctrl + ';
3: M_strDisplay:='Ctrl + Alt + ';
4: M_strDisplay:='Shift + ';
5: M_strDisplay:='Shift + Alt + ';
6: M_strDisplay:='Ctrl + Shift + ';
7: M_strDisplay:='Ctrl + Shift + Alt + ';
//注銷熱鍵
BOOL UnregisterHotKey(
HWND hWnd, //接受熱鍵消息的窗口句柄
int id //熱鍵ID
);
注銷時,根據注冊時賦予的熱鍵ID進行注銷,因此在注冊時必須保證ID唯一。
關于這兩個WIN32 API函數的具體說明可以參考WIN32 SDK文檔。
2.2 編程方法基本的熱鍵編程方法是定義2個窗體過程,和一個消息響應函數:
Procedure HotKeyOn;
Procedure HotKeyOff;
procedure HotKeyDown(var Msg: Tmessage); message WM_HOTKEY;
在HotKeyOn過程中調用API函數注冊熱鍵,代碼可以是這樣的:
procedure TfrmMain.HotKeyOn;
begin
HKStep := 1;
HKScreen := 2;
HKComponent := 3;
HKShowMain := 4;
HKShowOI := 5;
if not RegisterHotKey(Handle, HKStep, MOD_CONTROL, Ord('C')) then
showmessage('can not register the hotkey "Ctrl-C"');
if not RegisterHotKey(Handle, HKScreen, MOD_CONTROL, Ord('V')) then
showmessage('can not register the hotkey "Ctrl-V"');
if not RegisterHotKey(Handle, HKComponent, MOD_CONTROL, Ord('B')) then
showmessage('can not register the hotkey "Ctrl-B"');
if not RegisterHotKey(Handle, HKShowMain, MOD_CONTROL, VK_F11) then
showmessage('can not register the hotkey "Ctrl-F11"');
if not RegisterHotKey(Handle, HKShowOI, MOD_CONTROL, VK_F12) then
showmessage('can not register the hotkey "Ctrl-F12"');
end;
在HotKeyOff過程中調用API函數注銷熱鍵,代碼可以是這樣的:
procedure TfrmMain.HotKeyOff;
begin
UnRegisterHotKey(handle, HKStep);
UnRegisterHotKey(handle, HKScreen);
UnRegisterHotKey(handle, HKComponent);
UnRegisterHotKey(handle, HKShowMain);
UnRegisterHotKey(handle, HKShowOI);
end;
在HotKeyDown消息處理函數中判斷系統消息,根據不同的熱鍵組合執行響應的語句,代碼可以是這樣的:
procedure TfrmMain.HotKeyDown(var Msg: Tmessage);
begin
if (Msg.LParamHi = Ord('C')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-C"')
else if (Msg.LParamHi = Ord('V')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-V"')
else if (Msg.LParamHi = Ord('B')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-B"')
else if (Msg.LParamHi = VK_F11) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-11"')
else if (Msg.LParamHi = VK_F12) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-12"')
end;
如果系統熱鍵數量少、穩定不變,則適合使用這種方法。如果系統熱鍵較多,而軟件需求又要求熱鍵可以由用戶設置修改,則需要有一個自動化管理的模塊來實現。因此我在學習了如上的方法後實現了一個熱鍵自動管理的類。
3 實現概述實現這個熱鍵管理類我定義了1個記錄體和3個類。可以將熱鍵配置信息保存在一個獨立的xml文檔中,也作爲子樹加入到應用程序的配置文檔中。
記錄體ThotKeyStatus保存從XML配置樹中讀入的熱鍵記錄,該記錄體的數組變量將被整個單元文件內的對象所共享。
類ThotKeyEdit是一個自定義的控件,用于接受用戶輸入的熱鍵組合,一方面轉化爲系統可以接受的形式,另一方面也給用戶一個即時反饋。
類TformHotkeyConfig是一個窗體類,該窗體類將根據從XML中讀入的熱鍵配置動態創建ThotKeyEdit控件,提供用戶查看和修改熱鍵。
類ThotKeyConfig是我們要使用和直接訪問的類,它提供一個後台操作的功能,用戶在引入該類後可以選擇在程序指定位置讀入XML配置文件、生效熱鍵配置、打開TformHotKeyConfig提供的編輯窗體、保存配置到XML文件。另外該類在編程上爲了控制對象的唯一性,采用了類方法MgetInstance來獲得唯一對象,關于用類方法控制對象唯一性的方法可以參考我blog中的文章。
4 實現細節4.1 XML配置文檔結構XML配置文檔的結構可以如下:
<?xml version="1.0" encoding="UTF-8"?>
<configure>
<hotkeys>
<hotkey>
<caption>添加SCREEN</caption>
<hkid>101</hkid>
<mod>2</mod>
<vk>49</vk>
</hotkey>
<hotkey>
<caption>新建STEP</caption>
<hkid>102</hkid>
<mod>2</mod>
<vk>50</vk>
</hotkey>
</hotkeys>
</configure>
其中,<hotkeys>爲保存熱鍵配置的節點的子樹樹根。每個<hotkey>子樹記錄一個熱鍵配置。
caption爲熱鍵名稱,將顯示在TformHotKeyConfig實例中的ThotKeyEdit對象的標題位置。
Hkid爲熱鍵唯一標識,對應上文API函數中的ID值,該值必須局部唯一。
Mod爲熱鍵模式,對應上文API函數中的fsModifier值。
Vk爲熱鍵虛擬鍵碼,對應上文API函數中的VK值。
4.2 熱鍵編輯控件 ThotKeyEdit該控件的聲明如下:
//---------------------------------------
// 熱鍵編輯控件
//---------------------------------------
THotKeyEdit = class(TLabeledEdit)
private
//當前控件接收到的熱鍵組合是否合法
FKeySetValid:Boolean;
//組合鍵
FModValue:Integer;
//虛擬鍵碼
FVirtualKeyValue:Integer;
//修改合法後顯示的顔色
FValidateColor:TColor;
//用來覆蓋OnExit事件的函數
procedure LostFocusEvent(Sender:TObject);
//用來覆蓋OnKeyDown事件的函數
procedure GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
//將熱鍵數據轉換爲直觀文字
function GetDisplayText:string;
//熱鍵組合合法執行的代碼
procedure ActionOnHotKeyValid;
//熱鍵組合非法執行的代碼
procedure ActionOnHotKeyInvalid;
public
//覆蓋構造函數
constructor Create(AOwner: TComponent); override;
//外部請求將內部數據表現爲直觀文字
procedure DisplayHotKey;
published
property HasValidKeySet:boolean read FKeySetValid;
property VirtualKeyValue:integer read FVirtualKeyValue write FVirtualKeyValue;
property KeyModValue:integer read FModValue write FModValue;
end;
ThotKeyEdit控件繼承自TlabeledEdit控件,包含一個顯示熱鍵名稱的LABEL和一個只讀的EDIT區,在該區將顯示用戶輸入的熱鍵組合。控件的OnKeyDown事件被GetHotKeyDownEvent過程重寫,在該過程中捕捉用戶按下的按鍵組合,先將捕捉到的鍵位信息保存到私有字段中,然後調用GetDisplayText函數判斷私有字段中的鍵位是否合法,並且返回由這些信息轉換得到的熱鍵字串。合法性將保存在一個私有布爾字段FKeySetValid中。
對于用戶提供的熱鍵布局,如果可以接受則控件edit區會變色,如果不能接受則會在失去焦點時清除內容,並恢複默認顔色。
4.3 熱鍵自定義窗體類TformHotKeyconfig該窗體類繼承自Tform類,並需要UnitHotkeyConfigClass.dfm資源文件的支持。
類的聲明代碼如下:
//---------------------------------------
// 熱鍵設定窗體
//---------------------------------------
TFormHotkeyConfig = class(TForm)
GroupBoxLeft: TGroupBox;
PanelRight: TPanel;
ButtonYes: TButton;
ButtonNo: TButton;
procedure ButtonYesClick(Sender: TObject);
procedure ButtonNoClick(Sender: TObject);
private
//用來保存動態創建的THotKeyEdit控件的對象列表
FEditList:TObjectList;
public
constructor Create(AOwner: TComponent); override;
end;
該類重寫了構造方法,並在構造方法中根據從xml中讀入的熱鍵個數動態創建和初始化響應數量的ThotKeyEdit控件,因此需要一個私有成員FeditList來維護控件數組。
4.4 主類ThotKeyConfigThotKeyConfig的聲明如下:
//---------------------------------------
// 主類:提供熱鍵注冊和編輯功能
//---------------------------------------
THotkeyConfig = class (TComponent)
private
//如果用戶自定義配置文件路徑則記錄它
FAssociatedXML : String;
//配置窗體對象
FConfigureForm : TFormHotkeyConfig;
//熱鍵響應窗體引用
FWindow : TWinControl;
//定位到保存熱鍵記錄的XML子樹樹根
function XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
//隱藏的構造函數
constructor Create(AOwner: TComponent);override;
public
//獲得對象
class function MGetInstance(AOwner:TWinControl):THotKeyConfig;
//讀入XML配置文件
function LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
//保存配置
function SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
//注冊所有熱鍵設置
function EnableAllHotkeys:Boolean;
//注銷熱鍵
procedure DisableAllHotkeys;
//打開配置窗口
procedure OpenConfigWindow;
published
property WindowHandlesHotkey : TWinControl write FWindow;
end;
在該類中主要完成如下幾個功能:
1. 讀寫解析XML配置文件
2. 注冊注銷系統熱鍵
3. 提供熱鍵修改窗體的入口
4. 通過類方法和類變量,管理類的對象在應用程序中的唯一性
4.4.1 解析XML文檔因爲XML文檔結構相對簡單因此使用TXMLDocument類來實現,在LoadConfigFromXML方法中通過XMLGetKeysetFather函數定位到Hotkey節點,這樣如果XML結構位置改變,只需要修改XMLGetKeysetFather函數就可以。讀出的熱鍵記錄將保存到靜態變量FkeyInfo和FkeyInfoCount中,他們是ThotKeyStatus的數組和計數器。
4.4.2 注冊注銷系統熱鍵兩個對象方法負責自動完成熱鍵的注冊和注銷:EnableAllHotkeys和DisableAllHotkeys。
熱鍵注冊時從FkeyInfo數組中讀入鍵位信息並調用WIN32 API函數RegisterHotKey注冊熱鍵,如果注冊成功則返回true。
在RegisterHotKey中需要一個窗體句柄來接受系統消息WM_HOTKEY,因此在調用EnableAllHotkeys之前需要爲屬性WindowHandlesHotkey賦一個窗體的引用值。或者在MgetInstance方法的參數中傳遞該窗體的引用。如果沒有定義WindowHandlesHotkey會使熱鍵無法注冊。
4.4.3 熱鍵編輯窗體執行OpenConfigWindow方法將彈出模式窗體,提供用戶編輯熱鍵,該窗體就是TformHotKeyConfig的實例。
在窗體打開之前,爲了不在編輯時觸發熱鍵消息,需要調用DisableAllHotkeys取消所有熱鍵。
在窗體別關閉後檢查靜態變量isXMLNeedSave判斷用戶按下的是確認還是取消。如果是確認,則要保存熱鍵配置到靜態變量FkeyInfo和XML文檔。最後根據FkeyInfo重新注冊熱鍵。
4.4.4 對象唯一性因爲一個應用程序中,對于ThotkeyConfig對象通常只需要一個就夠了,如果在每個需要用到的地方都重新創建會影響程序執行效率。所以,我使用一個靜態變量保存唯一對象的引用,然後公開一個方法MgetInstance讓程序員得到ThotKeyConfig的實例對象。具體概念請訪問我的blog。
因此類的編程模式如下:
with THotKeyConfig.MGetInstance(Form1) do begin
LoadConfigFromXML;
EnableAllHotkeys ;
//……
end;
5 程序源代碼爲了便于使用,我將3個類定義和1個記錄體聲明寫在一個單獨的UNIT中,這樣會帶來一些訪問上的安全隱患,但是作爲學習只用,程序員在調用時“自覺”一點就可以了 :-P
//--------------------------------------------------------------------------
//UNIT: UnitHotkeyConfigClass.pas
//SUMM: 熱鍵控制類
//AUTH: CST
//DATE: 2005-8-15
//DESC: 本單元文件定義了一個保存熱鍵項目的記錄體、後台控制類、一個設定窗口類
// 以及一個需要用到的自定義控件THotKeyEdit。
//REFE: HotKeyConfig類使用到了自定義控件HotKeyEdit
//BUGS: No checking for duplicated hotkey sets
// No checking for duplicated hotkey_id in xml
//
//USES: 用戶只需使用THotKeyConfig類,該類不能創建實例。
// 請使用THotKeyConfig.MGetInstance(Owner)方法來訪問對象。
//--------------------------------------------------------------------------
unit UnitHotkeyConfigClass;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, StrUtils, Contnrs, xmldom, XMLIntf,
msxmldom, XMLDoc;
type
//---------------------------------------
// 熱鍵組合
//---------------------------------------
THotKeyStatus = record
FCaption:String[20]; //熱鍵標題
FHKID :Integer; //唯一ID
FMod :Integer; //組合鍵
FVK :Integer; //虛擬鍵碼
end;
//---------------------------------------
// 熱鍵編輯控件
//---------------------------------------
THotKeyEdit = class(TLabeledEdit)
private
//當前控件接收到的熱鍵組合是否合法
FKeySetValid:Boolean;
//組合鍵
FModValue:Integer;
//虛擬鍵碼
FVirtualKeyValue:Integer;
//修改合法後顯示的顔色
FValidateColor:TColor;
//用來覆蓋OnExit事件的函數
procedure LostFocusEvent(Sender:TObject);
//用來覆蓋OnKeyDown事件的函數
procedure GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
//將熱鍵數據轉換爲直觀文字
function GetDisplayText:string;
//熱鍵組合合法執行的代碼
procedure ActionOnHotKeyValid;
//熱鍵組合非法執行的代碼
procedure ActionOnHotKeyInvalid;
public
//覆蓋構造函數
constructor Create(AOwner: TComponent); override;
//外部請求將內部數據表現爲直觀文字
procedure DisplayHotKey;
published
property HasValidKeySet:boolean read FKeySetValid;
property VirtualKeyValue:integer read FVirtualKeyValue write FVirtualKeyValue;
property KeyModValue:integer read FModValue write FModValue;
end;
//---------------------------------------
// 熱鍵設定窗體
//---------------------------------------
TFormHotkeyConfig = class(TForm)
GroupBoxLeft: TGroupBox;
PanelRight: TPanel;
ButtonYes: TButton;
ButtonNo: TButton;
procedure ButtonYesClick(Sender: TObject);
procedure ButtonNoClick(Sender: TObject);
private
//用來保存動態創建的THotKeyEdit控件的對象列表
FEditList:TObjectList;
public
constructor Create(AOwner: TComponent); override;
end;
//---------------------------------------
// 主類:提供熱鍵注冊和編輯功能
//---------------------------------------
THotkeyConfig = class (TComponent)
private
//如果用戶自定義配置文件路徑則記錄它
FAssociatedXML : String;
//配置窗體對象
FConfigureForm : TFormHotkeyConfig;
//熱鍵響應窗體引用
FWindow : TWinControl;
//定位到保存熱鍵記錄的XML子樹樹根
function XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
//隱藏的構造函數
constructor Create(AOwner: TComponent);override;
public
//獲得對象
class function MGetInstance(AOwner:TWinControl):THotKeyConfig;
//讀入XML配置文件
function LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
//保存配置
function SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
//注冊所有熱鍵設置
function EnableAllHotkeys:Boolean;
//注銷熱鍵
procedure DisableAllHotkeys;
//打開配置窗口
procedure OpenConfigWindow;
published
property WindowHandlesHotkey : TWinControl write FWindow;
end;
procedure Register;
implementation
{$R *.dfm}
//-----------------------------------------------------------------------
// 單元內全局變量
//-----------------------------------------------------------------------
var
//共享從XML中讀入的熱鍵配置信息
FKeyInfo : array of THotKeyStatus;
//讀入的熱鍵記錄個數
FKeyInfoCount: Integer;
//是否需要保存到XML中
isXMLNeedSave:Boolean;
//實體
HK_Instance:THotkeyConfig;
//-----------------------------------------------------------------------
// 自定義控件可以被注冊
//-----------------------------------------------------------------------
procedure Register;
begin
RegisterComponents('CST', [THotKeyEdit]);
end;
{********************************************************
*********************************************************
******************* THotkeyConfig *********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
// 構造函數
//-----------------------------------------------------------------------
constructor THotKeyConfig.Create (AOwner: TComponent);
begin
inherited;
end;
//-----------------------------------------------------------------------
//NAME: MGetInstance
//SUMM: 獲得類的唯一實例
//PARA: AOwner
//RETN: 唯一實例
//AUTH: CST
//DATE: 2005-8-18
//DESC: 類方法實現對象唯一性控制
//-----------------------------------------------------------------------
class function THotKeyConfig.MGetInstance(AOwner:TWinControl):THotKeyConfig;
begin
if HK_Instance = nil then begin
HK_Instance:=Create(AOwner);
HK_Instance.WindowHandlesHotkey := AOwner;
end;
Result:=HK_Instance;
end;
//-----------------------------------------------------------------------
//NAME: EnableAllHotkeys
//SUMM: 注冊所有熱鍵
//PARA: N/A
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 在應用程序加載時由用戶顯式調用。熱鍵配置修改後被類自動調用重新注冊
// 先判斷熱鍵Listener窗體是否賦值,然後調用WIN32 API注冊
//-----------------------------------------------------------------------
function THotkeyConfig.EnableAllHotkeys:Boolean;
var
M_Index:integer;
M_ErrText:string;
begin
Result:=True;
//CHECK HOTKEY HANDLE WINDOW DEFINED
if FWindow=nil then begin
ShowMessage('熱鍵處理窗體未定義。'+#13+'請使用WindowHandlesHotkey方法。');
result:=false;
exit;
end;
//REGISTER BY LOOP
for M_Index := 0 to FKeyInfoCount - 1 do begin
//SKIP UNDEFINED HOTKEY EVENTS
if FKeyInfo[M_Index].FMod < 1 then continue;
if FKeyInfo[M_Index].FVK < 1 then continue;
//START TO REGISTER HOTKEY
if not RegisterHotKey( FWindow.Handle,
FKeyInfo[M_Index].FHKID,
FKeyInfo[M_Index].FMod ,
FKeyInfo[M_Index].FVK ) then begin
//REGISTER FAILURE PROCEDURE
Result:=False;
M_ErrText:=Format('無法注冊名爲%s的熱鍵。',[FKeyInfo[M_Index].FCaption]);
//ShowMessage(M_ErrText);
end; //end of if
end; //end of for
end;
//-----------------------------------------------------------------------
//NAME: DisableAllHotkeys
//SUMM: 注銷所有熱鍵
//PARA: N/A
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 在進入熱鍵編輯之前要調用此方法注銷熱鍵。
//-----------------------------------------------------------------------
procedure THotkeyConfig.DisableAllHotkeys;
var
M_Index:integer;
begin
for M_Index := 0 to FKeyInfoCount - 1 do
UnRegisterHotKey( FWindow.Handle, FKeyInfo[M_Index].FHKID);
end;
//-----------------------------------------------------------------------
//NAME: XMLGetKeysetFather
//SUMM: 定位到保存熱鍵記錄的XML子樹樹根
//PARA: AXML-XML文檔
//RETN: 樹根節點
//AUTH: CST
//DATE: 2005-8-18
//DESC: 定位到保存熱鍵記錄的XML子樹樹根。
// 如果要改變XML結構,則只要修改這裏的定位語句。
//-----------------------------------------------------------------------
function THotkeyConfig.XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
var
M_SearchNode:IXMLNode;
begin
//NAVIGATE THROUGH XML CONFIGURE FILE
M_SearchNode:=AXML.Node;
M_SearchNode:=M_SearchNode.ChildNodes.Nodes['configure'];
M_SearchNode:=M_SearchNode.ChildNodes.Nodes['hotkeys'];
Result:= M_SearchNode;
end;
//-----------------------------------------------------------------------
//NAME: LoadConfigFromXML
//SUMM: 從XML文檔中讀取熱鍵設置,並注冊生效
//PARA: AXMLFile-XML文檔路徑,默認爲EXE同根的hotkey.xml
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 使用TXMLDocuement對象解析配置文檔,將讀取的記錄保存到類變量中
// FKeyInfo數組記錄讀入的熱鍵組合,FKeyInfoCount記錄動態數組大小
//-----------------------------------------------------------------------
function THotkeyConfig.LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
var
M_ConfigXML:TXMLDocument;
M_SearchNode, M_PropNode:IXMLNODE;
M_Index :integer;
begin
result:=False;
//Q:爲何構造方法參數爲nil就會無法解析節點?
M_ConfigXML:=TXMLDocument.Create(Self);
try
//OPEN XML CONFIGURE FILE
with M_ConfigXML do begin
LoadFromFile(AXMLFile);
Options := [];
Active := True;
end;
//RECORD ASSOCIATED XML CONFIGURATION FILE
FAssociatedXML := AXMLFile;
//GET THE ROOT WE WANT
M_SearchNode:=XMLGetKeysetFather(M_ConfigXML);
//GET COUNT FOR HOTKEY SETS
FKeyInfoCount:=M_SearchNode.ChildNodes.Count ;
SetLength(FKeyInfo, FKeyInfoCount);
//LOOP TO READ EVERY KEYSET
for M_Index := 0 to FKeyInfoCount - 1 do begin
M_PropNode:=M_SearchNode.ChildNodes.Nodes[M_Index];
with FKeyInfo[M_Index] do begin
FCaption := M_PropNode.ChildValues['caption'];
FHKID := M_PropNode.ChildValues['hkid'];
FMod := M_PropNode.ChildValues['mod'];
FVK := M_PropNode.ChildValues['vk'];
end; //end of with
end; //end of for
finally
M_ConfigXML.Active := False;
FreeAndNil(M_ConfigXML);
end;
end;
//-----------------------------------------------------------------------
//NAME: SaveConfigToXML
//SUMM: 保存修改的熱鍵配置到XML文檔
//PARA: AXMLFile-XML文檔路徑
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 配置窗口確認關閉後調用
//-----------------------------------------------------------------------
function THotkeyConfig.SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
var
M_ConfigXML:TXMLDocument;
M_SearchNode, M_PropNode:IXMLNODE;
M_Index :integer;
begin
result:=False;
M_ConfigXML:=TXMLDocument.Create(Self );
try
//OPEN XML CONFIGURE FILE
with M_ConfigXML do begin
LoadFromFile(AXMLFile);
Options := [];
Active := True;
end;
//GET THE ROOT WE WANT
M_SearchNode:=XMLGetKeysetFather(M_ConfigXML);
//LOOP TO READ EVERY KEYSET
for M_Index := 0 to FKeyInfoCount - 1 do begin
M_PropNode:=M_SearchNode.ChildNodes.Nodes[M_Index];
with FKeyInfo[M_Index] do begin
M_PropNode.ChildValues['caption']:=FCaption;
M_PropNode.ChildValues['hkid']:=FHKID;
M_PropNode.ChildValues['mod']:=FMod;
M_PropNode.ChildValues['vk']:=FVK;
end; //end of with
end; //end of for
//SAVE CHANGES
M_ConfigXML.SaveToFile(AXMLFile);
finally
M_ConfigXML.Active := False;
FreeAndNil(M_ConfigXML);
end;
end;
//-----------------------------------------------------------------------
//NAME: OpenConfigWindow
//SUMM: 打開配置窗口
//PARA: N/A
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 窗體對象爲 FConfigureForm 成員
//-----------------------------------------------------------------------
procedure THotKeyConfig.OpenConfigWindow ;
var
M_ErrMsg:String;
begin
if FConfigureForm = nil then FConfigureForm:=TFormHotkeyConfig.Create(nil);
try
//默認是不要保存修改
isXMLNeedSave:=False;
//設置之前先注銷所有熱鍵
DisableAllHotkeys ;
//打開設置窗口
FConfigureForm.ShowModal;
if isXMLNeedSave then begin
//修改後按下『確認』生效並保存
if EnableAllHotkeys then
begin
//新設置熱鍵注冊成功
MessageBox(Application.Handle, '所有熱鍵都成功注冊。'+#13+'點擊確認保存所有熱鍵設置。', '提示', MB_OK + MB_ICONINFORMATION);
SaveConfigToXML(FAssociatedXML);
end //end of if
else
begin
//新設置熱鍵有沖突
M_ErrMsg:='您設置的熱鍵組合中有一項或多項沒有注冊成功。' + #13 +
'也許是和其他應用程序産生了沖突,您可以嘗試更換其他按鍵組合。' + #13 +
'請問是否仍然要保存這次的設置,如果保存請按“是”,我們將在下次軟件啓動的時候'+
'再次嘗試注冊您的熱鍵配置,您可以在這之前注銷或修改其他應用程序的沖突設置。';
if MessageBox(Application.Handle, PChar(M_ErrMsg), '提示',MB_YESNO+MB_ICONQUESTION)=IDYES then SaveConfigToXML(FAssociatedXML);
end;
end
else begin
//按下『取消』按鈕,但是還是要恢複原先的熱鍵
EnableAllHotkeys;
end;
finally
FreeAndNil(FConfigureForm);
end;
end;
{********************************************************
*********************************************************
**************** TFormHotkeyConfig ********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
//NAME: Create
//SUMM: TFormHotkeyConfig的構造函數
//AUTH: CST
//DATE: 2005-8-15
//DESC: 繼承TForm的構造函數,動態創建THotKeyEdit控件。
// 將窗體上的熱鍵接受控件的OnKeyDown事件改寫。
//-----------------------------------------------------------------------
constructor TFormHotkeyConfig.Create(AOwner: TComponent);
var
M_Index, M_Top:integer;
HKEdit:THotkeyEdit;
const
MLEFT = 10;
MWIDTH = 200;
MHEIGHT = 21;
MMARGIN = 30;
begin
inherited;
//HOTKEYEDITORS
FEditList := TObjectList.Create ;
M_Top := 0;
for M_Index := 0 to FKeyInfoCount - 1 do begin
//計算控件位置,縱向排列
M_Top:= MMARGIN + M_Index * (MHEIGHT+MMARGIN);
//根據讀入的XML節點動態創建熱鍵編輯控件
HKEdit:=THotKeyEdit.Create(Self);
with HKEdit do begin
//定義樣式
Parent:=Self.GroupBoxLeft;
SetBounds(MLEFT,M_Top,MWIDTH,MHEIGHT);
LabelPosition := lpAbove ;
EditLabel.Caption := FKeyInfo[M_Index].FCaption;
EditLabel.Width := MWIDTH;
//定義初始數據
VirtualKeyValue := FKeyInfo[M_Index].FVK;
KeyModValue := FKeyInfo[M_Index].FMod;
//按照定義的數據顯示熱鍵組合
DisplayHotKey;
end; //end of with
//保存組件到對象列表
FEditList.Add(HKEdit);
end; //end of for
Height:= M_Top + MHEIGHT + MMARGIN;
end;
//------------------------------------------------------------
// 確定
//------------------------------------------------------------
procedure TFormHotkeyConfig.ButtonYesClick(Sender: TObject);
var
M_Index:integer;
begin
//CONVERT HK_EDITOR DATA TO HOTKEY INFO ARRAY
for M_Index := 0 to FKeyInfoCount - 1 do begin
FKeyInfo[M_Index].FMod := (FEditList.Items[M_Index] as THotKeyEdit).KeyModValue ;
FKeyInfo[M_Index].FVK := (FEditList.Items[M_Index] as THotKeyEdit).VirtualKeyValue;
end;
//END OF CONVERT
Close;
isXMLNeedSave:=True;
end;
//------------------------------------------------------------
// 取消
//------------------------------------------------------------
procedure TFormHotkeyConfig.ButtonNoClick(Sender: TObject);
begin
if MessageBox(Self.Handle,'是否要放棄修改並關閉窗口?','提示',MB_YESNO+mb_iconinformation) = IDYES then
begin
Close;
isXMLNeedSave:=False;
end;
end;
{********************************************************
*********************************************************
********************* THotKeyEdit *********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
// HotKeyEdit控件構造函數
//-----------------------------------------------------------------------
constructor THotKeyEdit.Create(AOwner: TComponent);
begin
inherited;
ReadOnly := True;
OnKeyDown := GetHotKeyDownEvent;
OnExit := LostFocusEvent;
FValidateColor := clSkyBlue;
end;
//-----------------------------------------------------------------------
//NAME: GetDisplayText
//SUMM: 將熱鍵信息轉換爲顯示字串
//PARA: N/A
//RETN: 熱鍵轉換的顯示結果
//AUTH: CST
//DATE: 2005-8-15
//DESC: 型如:"Ctrl + Alt + Shift + A "爲正確
// 數據來源 FVirtualKeyValue, FModValue
// 判斷組合是否合法,記錄在FKeySetValid中
//-----------------------------------------------------------------------
function THotKeyEdit.GetDisplayText:string;
var
M_strDisplay:String;
const
SPLUS = ' + ';
begin
FKeySetValid := True;
//處理按鍵組合
case FModValue of
1: M_strDisplay:='Alt + ';
2: M_strDisplay:='Ctrl + ';
3: M_strDisplay:='Ctrl + Alt + ';
4: M_strDisplay:='Shift + ';
5: M_strDisplay:='Shift + Alt + ';
6: M_strDisplay:='Ctrl + Shift + ';
7: M_strDisplay:='Ctrl + Shift + Alt + ';
else
begin
M_strDisplay := '';
FKeySetValid := False;
end;
end;
//處理鍵碼
case FVirtualKeyValue of
VK_F1..VK_F12:
M_strDisplay := M_strDisplay + 'F'+IntToStr(FVirtualKeyValue - VK_F1 + 1);
Ord('A')..Ord('Z'), Ord('0')..Ord('9'):
M_strDisplay := M_strDisplay + Chr(FVirtualKeyValue);
else
begin
M_strDisplay := M_strDisplay ;
FKeySetValid := False;
end;
end;
result:=M_strDisplay;
end;
//-----------------------------------------------------------------------
//NAME: LostFocusEvent
//SUMM: 控件失去焦點時檢查熱鍵合法性
//PARA: Sender-控件
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 此函數將用來覆蓋4個TLabelEdit的OnExit事件
//-----------------------------------------------------------------------
procedure THotKeyEdit.LostFocusEvent(Sender:TObject);
begin
if not FKeySetValid then begin
Text:='';
FModValue := 0;
FVirtualKeyValue := 0;
end;
end;
//-----------------------------------------------------------------------
//NAME: GetHotKeyDownEvent
//SUMM: 接受用戶輸入的熱鍵並判斷是否合法的時間函數
//PARA: Sender-控件 Key-虛擬鍵碼 Shift-輔助鍵信息
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 此函數將用來覆蓋OnKeyDown事件
//
//-----------------------------------------------------------------------
procedure THotKeyEdit.GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
var
M_StrDisplay:String;
begin
//READ HOTKEY SET MODE
FModValue := 0;
if (ssCtrl in Shift) then FModValue := FModValue + 2;
if (ssAlt in Shift) then FModValue := FModValue + 1;
if (ssShift in Shift) then FModValue := FModValue + 4;
//READ HOTKEY SET VIRTUAL KEY
FVirtualKeyValue := Key;
//GET DISPLAY TEXT AND JUDGE WHETHER KEYSET IS VALIDATE
M_StrDisplay := GetDisplayText;
//REFLECTION
if FKeySetValid then
ActionOnHotKeyValid
else
ActionOnHotKeyInvalid ;
Text := M_StrDisplay;
end;
//---------------------------------------
// 在動態創建時顯示組合鍵
//---------------------------------------
procedure THotKeyEdit.DisplayHotKey;
begin
Text := GetDisplayText ;
end;
//---------------------------------------
// 熱鍵組合合法執行的代碼
//---------------------------------------
procedure THotKeyEdit.ActionOnHotKeyValid;
begin
Color:=FValidateColor;
end;
//---------------------------------------
// 熱鍵組合非法執行的代碼
//---------------------------------------
procedure THotKeyEdit.ActionOnHotKeyInvalid;
begin
Color := clWhite;
end;
end.
6 小結6.1 沒有解決的一些問題TXMLDocument的對象在創建時如果Owner參數爲nil則無法解析到節點,如果使用帶文檔路徑參數的重載的構造函數也會如此,因爲在TXMLDocument的源碼中重載的版本Owner也是nil。爲了規避這個問題,我犧牲了效率而將Owner置爲Application並手動釋放了文檔對象。考慮到如果使用self可能會因爲釋放兩次而産生錯誤,而Application的釋放影響不會很大。
沒有實現對于XML文檔合法性的檢驗,僅過濾了超出範圍的MOD和VK值,對于HKID是否唯一沒有做檢查。
沒有實現對于用戶定義的熱鍵之間的沖突,在TformHotKeyConfig中沒有判斷是否設置的了相同的熱鍵。
熱鍵編輯控件可以注冊到Pallete中,ThotKeyConfig類尚未控件化,如果控件化可能需要改變對象調用方式,公開構造函數允許創建多個實例。取消MgetInstance方法。
6.2 程序心得雖然Delphi中對于熱鍵的使用也不繁瑣,但是使用本方法可以利用流行的xml記錄熱鍵是挺誘人的,只要稍加修改就可以繼承到應用程序中。而且這樣自由度比較高,熱鍵數量、名稱、布局都是可以自定義的。
在組件化上,我只封裝了ThotKeyEdit控件,而沒有將ThotKeyConfig類嚴格封裝。因此只能通過代碼手動創建和調用。熱鍵編輯窗口是一個挺方便的設計,可以讓使用該類的用戶不必關心熱鍵編輯的實現。
在不斷的OO開發中,我也在摸索,程序中難免會有一些不如意之處,我誠心希望各位給我提出意見,我也很高興能在相關的問題上和大家一起討論討論。
本程序的相關代碼和測試示例可以在我的YAHOO公文包上下載。
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
實現一個熱鍵注冊編輯的類
CST(http://blog.csdn.net/mrtechno) 2005-8-19
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237148]1 文檔目的... 1[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237149]2 熱鍵編程基礎... 1[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237150]2.1 API函數... 1[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237151]2.2 編程方法... 2[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237152]3 實現概述... 4[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237153]4 實現細節... 4[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237154]4.1 XML配置文檔結構... 4[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237155]4.2 熱鍵編輯控件 ThotKeyEdit 5[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237156]4.3 熱鍵自定義窗體類TformHotKeyconfig. 6[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237157]4.4 主類ThotKeyConfig. 6[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237158]4.4.1 解析XML文檔... 7[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237159]4.4.2 注冊注銷系統熱鍵... 8[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237160]4.4.3 熱鍵編輯窗體... 8[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237161]4.4.4 對象唯一性... 8[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237162]5 程序源代碼... 9[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237163]6 小結... 25[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237164]6.1 沒有解決的一些問題... 25[/url]
[url=http://blog.csdn.net/fckeditor/editor/fckeditorarea.html#_Toc112237165]6.2 程序心得... 26[/url]
1 文檔目的本文檔介紹了在Delphi 7中注冊、注銷、使用熱鍵(Hot Key)的基本函數和方法,並在此基礎上介紹了一個熱鍵控制的類THotKeyConfig。該類可以從指定的xml配置文檔中讀入熱鍵配置信息,並在程序指定的位置注冊、注銷、修改熱鍵。
本文將不涉及XML文檔的讀寫方法,也不會詳述控件開發的方法。如果您對這些內容不了解,推薦您先略讀一下相關的文章。關于xml文檔的解析,可以到我的blog找到文章。
2 熱鍵編程基礎2.1 API函數在Delphi7中使用熱鍵要用到如下幾個函數:
//注冊熱鍵
BOOL RegisterHotKey(
HWND hWnd, // 接受熱鍵消息的窗口句柄
int id, // 熱鍵ID
UINT fsModifiers, // 按鍵組合整數
UINT vk // 案件虛擬鍵碼VK
);
其中fsModifiers 值如下:
1: M_strDisplay:='Alt + ';
2: M_strDisplay:='Ctrl + ';
3: M_strDisplay:='Ctrl + Alt + ';
4: M_strDisplay:='Shift + ';
5: M_strDisplay:='Shift + Alt + ';
6: M_strDisplay:='Ctrl + Shift + ';
7: M_strDisplay:='Ctrl + Shift + Alt + ';
//注銷熱鍵
BOOL UnregisterHotKey(
HWND hWnd, //接受熱鍵消息的窗口句柄
int id //熱鍵ID
);
注銷時,根據注冊時賦予的熱鍵ID進行注銷,因此在注冊時必須保證ID唯一。
關于這兩個WIN32 API函數的具體說明可以參考WIN32 SDK文檔。
2.2 編程方法基本的熱鍵編程方法是定義2個窗體過程,和一個消息響應函數:
Procedure HotKeyOn;
Procedure HotKeyOff;
procedure HotKeyDown(var Msg: Tmessage); message WM_HOTKEY;
在HotKeyOn過程中調用API函數注冊熱鍵,代碼可以是這樣的:
procedure TfrmMain.HotKeyOn;
begin
HKStep := 1;
HKScreen := 2;
HKComponent := 3;
HKShowMain := 4;
HKShowOI := 5;
if not RegisterHotKey(Handle, HKStep, MOD_CONTROL, Ord('C')) then
showmessage('can not register the hotkey "Ctrl-C"');
if not RegisterHotKey(Handle, HKScreen, MOD_CONTROL, Ord('V')) then
showmessage('can not register the hotkey "Ctrl-V"');
if not RegisterHotKey(Handle, HKComponent, MOD_CONTROL, Ord('B')) then
showmessage('can not register the hotkey "Ctrl-B"');
if not RegisterHotKey(Handle, HKShowMain, MOD_CONTROL, VK_F11) then
showmessage('can not register the hotkey "Ctrl-F11"');
if not RegisterHotKey(Handle, HKShowOI, MOD_CONTROL, VK_F12) then
showmessage('can not register the hotkey "Ctrl-F12"');
end;
在HotKeyOff過程中調用API函數注銷熱鍵,代碼可以是這樣的:
procedure TfrmMain.HotKeyOff;
begin
UnRegisterHotKey(handle, HKStep);
UnRegisterHotKey(handle, HKScreen);
UnRegisterHotKey(handle, HKComponent);
UnRegisterHotKey(handle, HKShowMain);
UnRegisterHotKey(handle, HKShowOI);
end;
在HotKeyDown消息處理函數中判斷系統消息,根據不同的熱鍵組合執行響應的語句,代碼可以是這樣的:
procedure TfrmMain.HotKeyDown(var Msg: Tmessage);
begin
if (Msg.LParamHi = Ord('C')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-C"')
else if (Msg.LParamHi = Ord('V')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-V"')
else if (Msg.LParamHi = Ord('B')) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-B"')
else if (Msg.LParamHi = VK_F11) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-11"')
else if (Msg.LParamHi = VK_F12) and (Msg.LParamLo = MOD_CONTROL) then
ShowMessage('"Ctrl-12"')
end;
如果系統熱鍵數量少、穩定不變,則適合使用這種方法。如果系統熱鍵較多,而軟件需求又要求熱鍵可以由用戶設置修改,則需要有一個自動化管理的模塊來實現。因此我在學習了如上的方法後實現了一個熱鍵自動管理的類。
3 實現概述實現這個熱鍵管理類我定義了1個記錄體和3個類。可以將熱鍵配置信息保存在一個獨立的xml文檔中,也作爲子樹加入到應用程序的配置文檔中。
記錄體ThotKeyStatus保存從XML配置樹中讀入的熱鍵記錄,該記錄體的數組變量將被整個單元文件內的對象所共享。
類ThotKeyEdit是一個自定義的控件,用于接受用戶輸入的熱鍵組合,一方面轉化爲系統可以接受的形式,另一方面也給用戶一個即時反饋。
類TformHotkeyConfig是一個窗體類,該窗體類將根據從XML中讀入的熱鍵配置動態創建ThotKeyEdit控件,提供用戶查看和修改熱鍵。
類ThotKeyConfig是我們要使用和直接訪問的類,它提供一個後台操作的功能,用戶在引入該類後可以選擇在程序指定位置讀入XML配置文件、生效熱鍵配置、打開TformHotKeyConfig提供的編輯窗體、保存配置到XML文件。另外該類在編程上爲了控制對象的唯一性,采用了類方法MgetInstance來獲得唯一對象,關于用類方法控制對象唯一性的方法可以參考我blog中的文章。
4 實現細節4.1 XML配置文檔結構XML配置文檔的結構可以如下:
<?xml version="1.0" encoding="UTF-8"?>
<configure>
<hotkeys>
<hotkey>
<caption>添加SCREEN</caption>
<hkid>101</hkid>
<mod>2</mod>
<vk>49</vk>
</hotkey>
<hotkey>
<caption>新建STEP</caption>
<hkid>102</hkid>
<mod>2</mod>
<vk>50</vk>
</hotkey>
</hotkeys>
</configure>
其中,<hotkeys>爲保存熱鍵配置的節點的子樹樹根。每個<hotkey>子樹記錄一個熱鍵配置。
caption爲熱鍵名稱,將顯示在TformHotKeyConfig實例中的ThotKeyEdit對象的標題位置。
Hkid爲熱鍵唯一標識,對應上文API函數中的ID值,該值必須局部唯一。
Mod爲熱鍵模式,對應上文API函數中的fsModifier值。
Vk爲熱鍵虛擬鍵碼,對應上文API函數中的VK值。
4.2 熱鍵編輯控件 ThotKeyEdit該控件的聲明如下:
//---------------------------------------
// 熱鍵編輯控件
//---------------------------------------
THotKeyEdit = class(TLabeledEdit)
private
//當前控件接收到的熱鍵組合是否合法
FKeySetValid:Boolean;
//組合鍵
FModValue:Integer;
//虛擬鍵碼
FVirtualKeyValue:Integer;
//修改合法後顯示的顔色
FValidateColor:TColor;
//用來覆蓋OnExit事件的函數
procedure LostFocusEvent(Sender:TObject);
//用來覆蓋OnKeyDown事件的函數
procedure GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
//將熱鍵數據轉換爲直觀文字
function GetDisplayText:string;
//熱鍵組合合法執行的代碼
procedure ActionOnHotKeyValid;
//熱鍵組合非法執行的代碼
procedure ActionOnHotKeyInvalid;
public
//覆蓋構造函數
constructor Create(AOwner: TComponent); override;
//外部請求將內部數據表現爲直觀文字
procedure DisplayHotKey;
published
property HasValidKeySet:boolean read FKeySetValid;
property VirtualKeyValue:integer read FVirtualKeyValue write FVirtualKeyValue;
property KeyModValue:integer read FModValue write FModValue;
end;
ThotKeyEdit控件繼承自TlabeledEdit控件,包含一個顯示熱鍵名稱的LABEL和一個只讀的EDIT區,在該區將顯示用戶輸入的熱鍵組合。控件的OnKeyDown事件被GetHotKeyDownEvent過程重寫,在該過程中捕捉用戶按下的按鍵組合,先將捕捉到的鍵位信息保存到私有字段中,然後調用GetDisplayText函數判斷私有字段中的鍵位是否合法,並且返回由這些信息轉換得到的熱鍵字串。合法性將保存在一個私有布爾字段FKeySetValid中。
對于用戶提供的熱鍵布局,如果可以接受則控件edit區會變色,如果不能接受則會在失去焦點時清除內容,並恢複默認顔色。
4.3 熱鍵自定義窗體類TformHotKeyconfig該窗體類繼承自Tform類,並需要UnitHotkeyConfigClass.dfm資源文件的支持。
類的聲明代碼如下:
//---------------------------------------
// 熱鍵設定窗體
//---------------------------------------
TFormHotkeyConfig = class(TForm)
GroupBoxLeft: TGroupBox;
PanelRight: TPanel;
ButtonYes: TButton;
ButtonNo: TButton;
procedure ButtonYesClick(Sender: TObject);
procedure ButtonNoClick(Sender: TObject);
private
//用來保存動態創建的THotKeyEdit控件的對象列表
FEditList:TObjectList;
public
constructor Create(AOwner: TComponent); override;
end;
該類重寫了構造方法,並在構造方法中根據從xml中讀入的熱鍵個數動態創建和初始化響應數量的ThotKeyEdit控件,因此需要一個私有成員FeditList來維護控件數組。
4.4 主類ThotKeyConfigThotKeyConfig的聲明如下:
//---------------------------------------
// 主類:提供熱鍵注冊和編輯功能
//---------------------------------------
THotkeyConfig = class (TComponent)
private
//如果用戶自定義配置文件路徑則記錄它
FAssociatedXML : String;
//配置窗體對象
FConfigureForm : TFormHotkeyConfig;
//熱鍵響應窗體引用
FWindow : TWinControl;
//定位到保存熱鍵記錄的XML子樹樹根
function XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
//隱藏的構造函數
constructor Create(AOwner: TComponent);override;
public
//獲得對象
class function MGetInstance(AOwner:TWinControl):THotKeyConfig;
//讀入XML配置文件
function LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
//保存配置
function SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
//注冊所有熱鍵設置
function EnableAllHotkeys:Boolean;
//注銷熱鍵
procedure DisableAllHotkeys;
//打開配置窗口
procedure OpenConfigWindow;
published
property WindowHandlesHotkey : TWinControl write FWindow;
end;
在該類中主要完成如下幾個功能:
1. 讀寫解析XML配置文件
2. 注冊注銷系統熱鍵
3. 提供熱鍵修改窗體的入口
4. 通過類方法和類變量,管理類的對象在應用程序中的唯一性
4.4.1 解析XML文檔因爲XML文檔結構相對簡單因此使用TXMLDocument類來實現,在LoadConfigFromXML方法中通過XMLGetKeysetFather函數定位到Hotkey節點,這樣如果XML結構位置改變,只需要修改XMLGetKeysetFather函數就可以。讀出的熱鍵記錄將保存到靜態變量FkeyInfo和FkeyInfoCount中,他們是ThotKeyStatus的數組和計數器。
4.4.2 注冊注銷系統熱鍵兩個對象方法負責自動完成熱鍵的注冊和注銷:EnableAllHotkeys和DisableAllHotkeys。
熱鍵注冊時從FkeyInfo數組中讀入鍵位信息並調用WIN32 API函數RegisterHotKey注冊熱鍵,如果注冊成功則返回true。
在RegisterHotKey中需要一個窗體句柄來接受系統消息WM_HOTKEY,因此在調用EnableAllHotkeys之前需要爲屬性WindowHandlesHotkey賦一個窗體的引用值。或者在MgetInstance方法的參數中傳遞該窗體的引用。如果沒有定義WindowHandlesHotkey會使熱鍵無法注冊。
4.4.3 熱鍵編輯窗體執行OpenConfigWindow方法將彈出模式窗體,提供用戶編輯熱鍵,該窗體就是TformHotKeyConfig的實例。
在窗體打開之前,爲了不在編輯時觸發熱鍵消息,需要調用DisableAllHotkeys取消所有熱鍵。
在窗體別關閉後檢查靜態變量isXMLNeedSave判斷用戶按下的是確認還是取消。如果是確認,則要保存熱鍵配置到靜態變量FkeyInfo和XML文檔。最後根據FkeyInfo重新注冊熱鍵。
4.4.4 對象唯一性因爲一個應用程序中,對于ThotkeyConfig對象通常只需要一個就夠了,如果在每個需要用到的地方都重新創建會影響程序執行效率。所以,我使用一個靜態變量保存唯一對象的引用,然後公開一個方法MgetInstance讓程序員得到ThotKeyConfig的實例對象。具體概念請訪問我的blog。
因此類的編程模式如下:
with THotKeyConfig.MGetInstance(Form1) do begin
LoadConfigFromXML;
EnableAllHotkeys ;
//……
end;
5 程序源代碼爲了便于使用,我將3個類定義和1個記錄體聲明寫在一個單獨的UNIT中,這樣會帶來一些訪問上的安全隱患,但是作爲學習只用,程序員在調用時“自覺”一點就可以了 :-P
//--------------------------------------------------------------------------
//UNIT: UnitHotkeyConfigClass.pas
//SUMM: 熱鍵控制類
//AUTH: CST
//DATE: 2005-8-15
//DESC: 本單元文件定義了一個保存熱鍵項目的記錄體、後台控制類、一個設定窗口類
// 以及一個需要用到的自定義控件THotKeyEdit。
//REFE: HotKeyConfig類使用到了自定義控件HotKeyEdit
//BUGS: No checking for duplicated hotkey sets
// No checking for duplicated hotkey_id in xml
//
//USES: 用戶只需使用THotKeyConfig類,該類不能創建實例。
// 請使用THotKeyConfig.MGetInstance(Owner)方法來訪問對象。
//--------------------------------------------------------------------------
unit UnitHotkeyConfigClass;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Buttons, StrUtils, Contnrs, xmldom, XMLIntf,
msxmldom, XMLDoc;
type
//---------------------------------------
// 熱鍵組合
//---------------------------------------
THotKeyStatus = record
FCaption:String[20]; //熱鍵標題
FHKID :Integer; //唯一ID
FMod :Integer; //組合鍵
FVK :Integer; //虛擬鍵碼
end;
//---------------------------------------
// 熱鍵編輯控件
//---------------------------------------
THotKeyEdit = class(TLabeledEdit)
private
//當前控件接收到的熱鍵組合是否合法
FKeySetValid:Boolean;
//組合鍵
FModValue:Integer;
//虛擬鍵碼
FVirtualKeyValue:Integer;
//修改合法後顯示的顔色
FValidateColor:TColor;
//用來覆蓋OnExit事件的函數
procedure LostFocusEvent(Sender:TObject);
//用來覆蓋OnKeyDown事件的函數
procedure GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
//將熱鍵數據轉換爲直觀文字
function GetDisplayText:string;
//熱鍵組合合法執行的代碼
procedure ActionOnHotKeyValid;
//熱鍵組合非法執行的代碼
procedure ActionOnHotKeyInvalid;
public
//覆蓋構造函數
constructor Create(AOwner: TComponent); override;
//外部請求將內部數據表現爲直觀文字
procedure DisplayHotKey;
published
property HasValidKeySet:boolean read FKeySetValid;
property VirtualKeyValue:integer read FVirtualKeyValue write FVirtualKeyValue;
property KeyModValue:integer read FModValue write FModValue;
end;
//---------------------------------------
// 熱鍵設定窗體
//---------------------------------------
TFormHotkeyConfig = class(TForm)
GroupBoxLeft: TGroupBox;
PanelRight: TPanel;
ButtonYes: TButton;
ButtonNo: TButton;
procedure ButtonYesClick(Sender: TObject);
procedure ButtonNoClick(Sender: TObject);
private
//用來保存動態創建的THotKeyEdit控件的對象列表
FEditList:TObjectList;
public
constructor Create(AOwner: TComponent); override;
end;
//---------------------------------------
// 主類:提供熱鍵注冊和編輯功能
//---------------------------------------
THotkeyConfig = class (TComponent)
private
//如果用戶自定義配置文件路徑則記錄它
FAssociatedXML : String;
//配置窗體對象
FConfigureForm : TFormHotkeyConfig;
//熱鍵響應窗體引用
FWindow : TWinControl;
//定位到保存熱鍵記錄的XML子樹樹根
function XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
//隱藏的構造函數
constructor Create(AOwner: TComponent);override;
public
//獲得對象
class function MGetInstance(AOwner:TWinControl):THotKeyConfig;
//讀入XML配置文件
function LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
//保存配置
function SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
//注冊所有熱鍵設置
function EnableAllHotkeys:Boolean;
//注銷熱鍵
procedure DisableAllHotkeys;
//打開配置窗口
procedure OpenConfigWindow;
published
property WindowHandlesHotkey : TWinControl write FWindow;
end;
procedure Register;
implementation
{$R *.dfm}
//-----------------------------------------------------------------------
// 單元內全局變量
//-----------------------------------------------------------------------
var
//共享從XML中讀入的熱鍵配置信息
FKeyInfo : array of THotKeyStatus;
//讀入的熱鍵記錄個數
FKeyInfoCount: Integer;
//是否需要保存到XML中
isXMLNeedSave:Boolean;
//實體
HK_Instance:THotkeyConfig;
//-----------------------------------------------------------------------
// 自定義控件可以被注冊
//-----------------------------------------------------------------------
procedure Register;
begin
RegisterComponents('CST', [THotKeyEdit]);
end;
{********************************************************
*********************************************************
******************* THotkeyConfig *********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
// 構造函數
//-----------------------------------------------------------------------
constructor THotKeyConfig.Create (AOwner: TComponent);
begin
inherited;
end;
//-----------------------------------------------------------------------
//NAME: MGetInstance
//SUMM: 獲得類的唯一實例
//PARA: AOwner
//RETN: 唯一實例
//AUTH: CST
//DATE: 2005-8-18
//DESC: 類方法實現對象唯一性控制
//-----------------------------------------------------------------------
class function THotKeyConfig.MGetInstance(AOwner:TWinControl):THotKeyConfig;
begin
if HK_Instance = nil then begin
HK_Instance:=Create(AOwner);
HK_Instance.WindowHandlesHotkey := AOwner;
end;
Result:=HK_Instance;
end;
//-----------------------------------------------------------------------
//NAME: EnableAllHotkeys
//SUMM: 注冊所有熱鍵
//PARA: N/A
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 在應用程序加載時由用戶顯式調用。熱鍵配置修改後被類自動調用重新注冊
// 先判斷熱鍵Listener窗體是否賦值,然後調用WIN32 API注冊
//-----------------------------------------------------------------------
function THotkeyConfig.EnableAllHotkeys:Boolean;
var
M_Index:integer;
M_ErrText:string;
begin
Result:=True;
//CHECK HOTKEY HANDLE WINDOW DEFINED
if FWindow=nil then begin
ShowMessage('熱鍵處理窗體未定義。'+#13+'請使用WindowHandlesHotkey方法。');
result:=false;
exit;
end;
//REGISTER BY LOOP
for M_Index := 0 to FKeyInfoCount - 1 do begin
//SKIP UNDEFINED HOTKEY EVENTS
if FKeyInfo[M_Index].FMod < 1 then continue;
if FKeyInfo[M_Index].FVK < 1 then continue;
//START TO REGISTER HOTKEY
if not RegisterHotKey( FWindow.Handle,
FKeyInfo[M_Index].FHKID,
FKeyInfo[M_Index].FMod ,
FKeyInfo[M_Index].FVK ) then begin
//REGISTER FAILURE PROCEDURE
Result:=False;
M_ErrText:=Format('無法注冊名爲%s的熱鍵。',[FKeyInfo[M_Index].FCaption]);
//ShowMessage(M_ErrText);
end; //end of if
end; //end of for
end;
//-----------------------------------------------------------------------
//NAME: DisableAllHotkeys
//SUMM: 注銷所有熱鍵
//PARA: N/A
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 在進入熱鍵編輯之前要調用此方法注銷熱鍵。
//-----------------------------------------------------------------------
procedure THotkeyConfig.DisableAllHotkeys;
var
M_Index:integer;
begin
for M_Index := 0 to FKeyInfoCount - 1 do
UnRegisterHotKey( FWindow.Handle, FKeyInfo[M_Index].FHKID);
end;
//-----------------------------------------------------------------------
//NAME: XMLGetKeysetFather
//SUMM: 定位到保存熱鍵記錄的XML子樹樹根
//PARA: AXML-XML文檔
//RETN: 樹根節點
//AUTH: CST
//DATE: 2005-8-18
//DESC: 定位到保存熱鍵記錄的XML子樹樹根。
// 如果要改變XML結構,則只要修改這裏的定位語句。
//-----------------------------------------------------------------------
function THotkeyConfig.XMLGetKeysetFather(AXML:TXMLDocument):IXMLNode;
var
M_SearchNode:IXMLNode;
begin
//NAVIGATE THROUGH XML CONFIGURE FILE
M_SearchNode:=AXML.Node;
M_SearchNode:=M_SearchNode.ChildNodes.Nodes['configure'];
M_SearchNode:=M_SearchNode.ChildNodes.Nodes['hotkeys'];
Result:= M_SearchNode;
end;
//-----------------------------------------------------------------------
//NAME: LoadConfigFromXML
//SUMM: 從XML文檔中讀取熱鍵設置,並注冊生效
//PARA: AXMLFile-XML文檔路徑,默認爲EXE同根的hotkey.xml
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 使用TXMLDocuement對象解析配置文檔,將讀取的記錄保存到類變量中
// FKeyInfo數組記錄讀入的熱鍵組合,FKeyInfoCount記錄動態數組大小
//-----------------------------------------------------------------------
function THotkeyConfig.LoadConfigFromXML(const AXMLFile:string='hotkey.xml'):boolean;
var
M_ConfigXML:TXMLDocument;
M_SearchNode, M_PropNode:IXMLNODE;
M_Index :integer;
begin
result:=False;
//Q:爲何構造方法參數爲nil就會無法解析節點?
M_ConfigXML:=TXMLDocument.Create(Self);
try
//OPEN XML CONFIGURE FILE
with M_ConfigXML do begin
LoadFromFile(AXMLFile);
Options := [];
Active := True;
end;
//RECORD ASSOCIATED XML CONFIGURATION FILE
FAssociatedXML := AXMLFile;
//GET THE ROOT WE WANT
M_SearchNode:=XMLGetKeysetFather(M_ConfigXML);
//GET COUNT FOR HOTKEY SETS
FKeyInfoCount:=M_SearchNode.ChildNodes.Count ;
SetLength(FKeyInfo, FKeyInfoCount);
//LOOP TO READ EVERY KEYSET
for M_Index := 0 to FKeyInfoCount - 1 do begin
M_PropNode:=M_SearchNode.ChildNodes.Nodes[M_Index];
with FKeyInfo[M_Index] do begin
FCaption := M_PropNode.ChildValues['caption'];
FHKID := M_PropNode.ChildValues['hkid'];
FMod := M_PropNode.ChildValues['mod'];
FVK := M_PropNode.ChildValues['vk'];
end; //end of with
end; //end of for
finally
M_ConfigXML.Active := False;
FreeAndNil(M_ConfigXML);
end;
end;
//-----------------------------------------------------------------------
//NAME: SaveConfigToXML
//SUMM: 保存修改的熱鍵配置到XML文檔
//PARA: AXMLFile-XML文檔路徑
//RETN: TRUE-成功
//AUTH: CST
//DATE: 2005-8-15
//DESC: 配置窗口確認關閉後調用
//-----------------------------------------------------------------------
function THotkeyConfig.SaveConfigToXML(const AXMLFile:string='hotkey.xml'):boolean;
var
M_ConfigXML:TXMLDocument;
M_SearchNode, M_PropNode:IXMLNODE;
M_Index :integer;
begin
result:=False;
M_ConfigXML:=TXMLDocument.Create(Self );
try
//OPEN XML CONFIGURE FILE
with M_ConfigXML do begin
LoadFromFile(AXMLFile);
Options := [];
Active := True;
end;
//GET THE ROOT WE WANT
M_SearchNode:=XMLGetKeysetFather(M_ConfigXML);
//LOOP TO READ EVERY KEYSET
for M_Index := 0 to FKeyInfoCount - 1 do begin
M_PropNode:=M_SearchNode.ChildNodes.Nodes[M_Index];
with FKeyInfo[M_Index] do begin
M_PropNode.ChildValues['caption']:=FCaption;
M_PropNode.ChildValues['hkid']:=FHKID;
M_PropNode.ChildValues['mod']:=FMod;
M_PropNode.ChildValues['vk']:=FVK;
end; //end of with
end; //end of for
//SAVE CHANGES
M_ConfigXML.SaveToFile(AXMLFile);
finally
M_ConfigXML.Active := False;
FreeAndNil(M_ConfigXML);
end;
end;
//-----------------------------------------------------------------------
//NAME: OpenConfigWindow
//SUMM: 打開配置窗口
//PARA: N/A
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 窗體對象爲 FConfigureForm 成員
//-----------------------------------------------------------------------
procedure THotKeyConfig.OpenConfigWindow ;
var
M_ErrMsg:String;
begin
if FConfigureForm = nil then FConfigureForm:=TFormHotkeyConfig.Create(nil);
try
//默認是不要保存修改
isXMLNeedSave:=False;
//設置之前先注銷所有熱鍵
DisableAllHotkeys ;
//打開設置窗口
FConfigureForm.ShowModal;
if isXMLNeedSave then begin
//修改後按下『確認』生效並保存
if EnableAllHotkeys then
begin
//新設置熱鍵注冊成功
MessageBox(Application.Handle, '所有熱鍵都成功注冊。'+#13+'點擊確認保存所有熱鍵設置。', '提示', MB_OK + MB_ICONINFORMATION);
SaveConfigToXML(FAssociatedXML);
end //end of if
else
begin
//新設置熱鍵有沖突
M_ErrMsg:='您設置的熱鍵組合中有一項或多項沒有注冊成功。' + #13 +
'也許是和其他應用程序産生了沖突,您可以嘗試更換其他按鍵組合。' + #13 +
'請問是否仍然要保存這次的設置,如果保存請按“是”,我們將在下次軟件啓動的時候'+
'再次嘗試注冊您的熱鍵配置,您可以在這之前注銷或修改其他應用程序的沖突設置。';
if MessageBox(Application.Handle, PChar(M_ErrMsg), '提示',MB_YESNO+MB_ICONQUESTION)=IDYES then SaveConfigToXML(FAssociatedXML);
end;
end
else begin
//按下『取消』按鈕,但是還是要恢複原先的熱鍵
EnableAllHotkeys;
end;
finally
FreeAndNil(FConfigureForm);
end;
end;
{********************************************************
*********************************************************
**************** TFormHotkeyConfig ********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
//NAME: Create
//SUMM: TFormHotkeyConfig的構造函數
//AUTH: CST
//DATE: 2005-8-15
//DESC: 繼承TForm的構造函數,動態創建THotKeyEdit控件。
// 將窗體上的熱鍵接受控件的OnKeyDown事件改寫。
//-----------------------------------------------------------------------
constructor TFormHotkeyConfig.Create(AOwner: TComponent);
var
M_Index, M_Top:integer;
HKEdit:THotkeyEdit;
const
MLEFT = 10;
MWIDTH = 200;
MHEIGHT = 21;
MMARGIN = 30;
begin
inherited;
//HOTKEYEDITORS
FEditList := TObjectList.Create ;
M_Top := 0;
for M_Index := 0 to FKeyInfoCount - 1 do begin
//計算控件位置,縱向排列
M_Top:= MMARGIN + M_Index * (MHEIGHT+MMARGIN);
//根據讀入的XML節點動態創建熱鍵編輯控件
HKEdit:=THotKeyEdit.Create(Self);
with HKEdit do begin
//定義樣式
Parent:=Self.GroupBoxLeft;
SetBounds(MLEFT,M_Top,MWIDTH,MHEIGHT);
LabelPosition := lpAbove ;
EditLabel.Caption := FKeyInfo[M_Index].FCaption;
EditLabel.Width := MWIDTH;
//定義初始數據
VirtualKeyValue := FKeyInfo[M_Index].FVK;
KeyModValue := FKeyInfo[M_Index].FMod;
//按照定義的數據顯示熱鍵組合
DisplayHotKey;
end; //end of with
//保存組件到對象列表
FEditList.Add(HKEdit);
end; //end of for
Height:= M_Top + MHEIGHT + MMARGIN;
end;
//------------------------------------------------------------
// 確定
//------------------------------------------------------------
procedure TFormHotkeyConfig.ButtonYesClick(Sender: TObject);
var
M_Index:integer;
begin
//CONVERT HK_EDITOR DATA TO HOTKEY INFO ARRAY
for M_Index := 0 to FKeyInfoCount - 1 do begin
FKeyInfo[M_Index].FMod := (FEditList.Items[M_Index] as THotKeyEdit).KeyModValue ;
FKeyInfo[M_Index].FVK := (FEditList.Items[M_Index] as THotKeyEdit).VirtualKeyValue;
end;
//END OF CONVERT
Close;
isXMLNeedSave:=True;
end;
//------------------------------------------------------------
// 取消
//------------------------------------------------------------
procedure TFormHotkeyConfig.ButtonNoClick(Sender: TObject);
begin
if MessageBox(Self.Handle,'是否要放棄修改並關閉窗口?','提示',MB_YESNO+mb_iconinformation) = IDYES then
begin
Close;
isXMLNeedSave:=False;
end;
end;
{********************************************************
*********************************************************
********************* THotKeyEdit *********************
*********************************************************
********************************************************}
//-----------------------------------------------------------------------
// HotKeyEdit控件構造函數
//-----------------------------------------------------------------------
constructor THotKeyEdit.Create(AOwner: TComponent);
begin
inherited;
ReadOnly := True;
OnKeyDown := GetHotKeyDownEvent;
OnExit := LostFocusEvent;
FValidateColor := clSkyBlue;
end;
//-----------------------------------------------------------------------
//NAME: GetDisplayText
//SUMM: 將熱鍵信息轉換爲顯示字串
//PARA: N/A
//RETN: 熱鍵轉換的顯示結果
//AUTH: CST
//DATE: 2005-8-15
//DESC: 型如:"Ctrl + Alt + Shift + A "爲正確
// 數據來源 FVirtualKeyValue, FModValue
// 判斷組合是否合法,記錄在FKeySetValid中
//-----------------------------------------------------------------------
function THotKeyEdit.GetDisplayText:string;
var
M_strDisplay:String;
const
SPLUS = ' + ';
begin
FKeySetValid := True;
//處理按鍵組合
case FModValue of
1: M_strDisplay:='Alt + ';
2: M_strDisplay:='Ctrl + ';
3: M_strDisplay:='Ctrl + Alt + ';
4: M_strDisplay:='Shift + ';
5: M_strDisplay:='Shift + Alt + ';
6: M_strDisplay:='Ctrl + Shift + ';
7: M_strDisplay:='Ctrl + Shift + Alt + ';
else
begin
M_strDisplay := '';
FKeySetValid := False;
end;
end;
//處理鍵碼
case FVirtualKeyValue of
VK_F1..VK_F12:
M_strDisplay := M_strDisplay + 'F'+IntToStr(FVirtualKeyValue - VK_F1 + 1);
Ord('A')..Ord('Z'), Ord('0')..Ord('9'):
M_strDisplay := M_strDisplay + Chr(FVirtualKeyValue);
else
begin
M_strDisplay := M_strDisplay ;
FKeySetValid := False;
end;
end;
result:=M_strDisplay;
end;
//-----------------------------------------------------------------------
//NAME: LostFocusEvent
//SUMM: 控件失去焦點時檢查熱鍵合法性
//PARA: Sender-控件
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 此函數將用來覆蓋4個TLabelEdit的OnExit事件
//-----------------------------------------------------------------------
procedure THotKeyEdit.LostFocusEvent(Sender:TObject);
begin
if not FKeySetValid then begin
Text:='';
FModValue := 0;
FVirtualKeyValue := 0;
end;
end;
//-----------------------------------------------------------------------
//NAME: GetHotKeyDownEvent
//SUMM: 接受用戶輸入的熱鍵並判斷是否合法的時間函數
//PARA: Sender-控件 Key-虛擬鍵碼 Shift-輔助鍵信息
//RETN: N/A
//AUTH: CST
//DATE: 2005-8-15
//DESC: 此函數將用來覆蓋OnKeyDown事件
//
//-----------------------------------------------------------------------
procedure THotKeyEdit.GetHotKeyDownEvent(Sender: TObject; var Key: Word; Shift: TShiftState);
var
M_StrDisplay:String;
begin
//READ HOTKEY SET MODE
FModValue := 0;
if (ssCtrl in Shift) then FModValue := FModValue + 2;
if (ssAlt in Shift) then FModValue := FModValue + 1;
if (ssShift in Shift) then FModValue := FModValue + 4;
//READ HOTKEY SET VIRTUAL KEY
FVirtualKeyValue := Key;
//GET DISPLAY TEXT AND JUDGE WHETHER KEYSET IS VALIDATE
M_StrDisplay := GetDisplayText;
//REFLECTION
if FKeySetValid then
ActionOnHotKeyValid
else
ActionOnHotKeyInvalid ;
Text := M_StrDisplay;
end;
//---------------------------------------
// 在動態創建時顯示組合鍵
//---------------------------------------
procedure THotKeyEdit.DisplayHotKey;
begin
Text := GetDisplayText ;
end;
//---------------------------------------
// 熱鍵組合合法執行的代碼
//---------------------------------------
procedure THotKeyEdit.ActionOnHotKeyValid;
begin
Color:=FValidateColor;
end;
//---------------------------------------
// 熱鍵組合非法執行的代碼
//---------------------------------------
procedure THotKeyEdit.ActionOnHotKeyInvalid;
begin
Color := clWhite;
end;
end.
6 小結6.1 沒有解決的一些問題TXMLDocument的對象在創建時如果Owner參數爲nil則無法解析到節點,如果使用帶文檔路徑參數的重載的構造函數也會如此,因爲在TXMLDocument的源碼中重載的版本Owner也是nil。爲了規避這個問題,我犧牲了效率而將Owner置爲Application並手動釋放了文檔對象。考慮到如果使用self可能會因爲釋放兩次而産生錯誤,而Application的釋放影響不會很大。
沒有實現對于XML文檔合法性的檢驗,僅過濾了超出範圍的MOD和VK值,對于HKID是否唯一沒有做檢查。
沒有實現對于用戶定義的熱鍵之間的沖突,在TformHotKeyConfig中沒有判斷是否設置的了相同的熱鍵。
熱鍵編輯控件可以注冊到Pallete中,ThotKeyConfig類尚未控件化,如果控件化可能需要改變對象調用方式,公開構造函數允許創建多個實例。取消MgetInstance方法。
6.2 程序心得雖然Delphi中對于熱鍵的使用也不繁瑣,但是使用本方法可以利用流行的xml記錄熱鍵是挺誘人的,只要稍加修改就可以繼承到應用程序中。而且這樣自由度比較高,熱鍵數量、名稱、布局都是可以自定義的。
在組件化上,我只封裝了ThotKeyEdit控件,而沒有將ThotKeyConfig類嚴格封裝。因此只能通過代碼手動創建和調用。熱鍵編輯窗口是一個挺方便的設計,可以讓使用該類的用戶不必關心熱鍵編輯的實現。
在不斷的OO開發中,我也在摸索,程序中難免會有一些不如意之處,我誠心希望各位給我提出意見,我也很高興能在相關的問題上和大家一起討論討論。
本程序的相關代碼和測試示例可以在我的YAHOO公文包上下載。