实现一个热键注册编辑的类

实现一个热键注册编辑的类

CSThttp://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 主类ThotKeyConfig

ThotKeyConfig的声明如下:

    //---------------------------------------

    //  主类:提供热键注册和编辑功能

    //---------------------------------------

    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函数就可以。读出的热键记录将保存到静态变量FkeyInfoFkeyInfoCount中,他们是ThotKeyStatus的数组和计数器。

 

4.4.2 注册注销系统热键

两个对象方法负责自动完成热键的注册和注销:EnableAllHotkeysDisableAllHotkeys

热键注册时从FkeyInfo数组中读入键位信息并调用WIN32 API函数RegisterHotKey注册热键,如果注册成功则返回true

RegisterHotKey中需要一个窗体句柄来接受系统消息WM_HOTKEY,因此在调用EnableAllHotkeys之前需要为属性WindowHandlesHotkey赋一个窗体的引用值。或者在MgetInstance方法的参数中传递该窗体的引用。如果没有定义WindowHandlesHotkey会使热键无法注册。

 

4.4.3 热键编辑窗体

执行OpenConfigWindow方法将弹出模式窗体,提供用户编辑热键,该窗体就是TformHotKeyConfig的实例。

在窗体打开之前,为了不在编辑时触发热键消息,需要调用DisableAllHotkeys取消所有热键。

在窗体别关闭后检查静态变量isXMLNeedSave判断用户按下的是确认还是取消。如果是确认,则要保存热键配置到静态变量FkeyInfoXML文档。最后根据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: 此函数将用来覆盖4TLabelEditOnExit事件

//-----------------------------------------------------------------------

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文档合法性的检验,仅过滤了超出范围的MODVK值,对于HKID是否唯一没有做检查。

 

没有实现对于用户定义的热键之间的冲突,在TformHotKeyConfig中没有判断是否设置的了相同的热键。

 

热键编辑控件可以注册到Pallete中,ThotKeyConfig类尚未控件化,如果控件化可能需要改变对象调用方式,公开构造函数允许创建多个实例。取消MgetInstance方法。

 

6.2 程序心得

虽然Delphi中对于热键的使用也不繁琐,但是使用本方法可以利用流行的xml记录热键是挺诱人的,只要稍加修改就可以继承到应用程序中。而且这样自由度比较高,热键数量、名称、布局都是可以自定义的。

 

在组件化上,我只封装了ThotKeyEdit控件,而没有将ThotKeyConfig类严格封装。因此只能通过代码手动创建和调用。热键编辑窗口是一个挺方便的设计,可以让使用该类的用户不必关心热键编辑的实现。

 

在不断的OO开发中,我也在摸索,程序中难免会有一些不如意之处,我诚心希望各位给我提出意见,我也很高兴能在相关的问题上和大家一起讨论讨论。

 

本程序的相关代码和测试示例可以在我的YAHOO公文包上下载。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在VB.NET中注册多个热键,可以使用Windows API函数来实现。以下是一个示例代码,说明如何在VB.NET中注册多个热键: 首先,导入系统的用户界面库: Imports System.Runtime.InteropServices 然后,定义一些常量和API函数: Public Class Form1 ' 定义常量 Private Const MOD_CONTROL As Integer = &H2 Private Const WM_HOTKEY As Integer = &H312 ' 导入Windows API函数 <DllImport("user32.dll")> _ Private Shared Function RegisterHotKey(ByVal hWnd As IntPtr, ByVal id As Integer, ByVal fsModifiers As Integer, ByVal vk As Integer) As Boolean End Function <DllImport("user32.dll")> _ Private Shared Function UnregisterHotKey(ByVal hWnd As IntPtr, ByVal id As Integer) As Boolean End Function 接下来,在窗体加载事件中注册热键: Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 注册热键 RegisterHotKey(Me.Handle, 1, MOD_CONTROL, Keys.A) RegisterHotKey(Me.Handle, 2, MOD_CONTROL, Keys.B) End Sub 接着,在窗体消息处理事件中处理热键消息: Protected Overrides Sub WndProc(ByRef m As Message) If m.Msg = WM_HOTKEY Then Dim hotkeyId As Integer = m.WParam.ToInt32() Select Case hotkeyId Case 1 ' 处理热键1的逻辑 MessageBox.Show("热键1被触发") Case 2 ' 处理热键2的逻辑 MessageBox.Show("热键2被触发") End Select End If MyBase.WndProc(m) End Sub 最后,在窗体关闭事件中取消注册热键: Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing ' 取消注册热键 UnregisterHotKey(Me.Handle, 1) UnregisterHotKey(Me.Handle, 2) End Sub 以上代码演示了如何在VB.NET中注册多个热键,并在热键被触发时处理相应的逻辑。你可以根据需要修改和扩展这个示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值