• Thứ Năm, 26/10/2006 10:40 (GMT+7)

    Lập trình thay đổi Component Palette của Delphi IDE

    Nếu bạn thường làm việc với Delphi, nếu Delphi của bạn đã được cài đặt thêm rất nhiều các thành phần điều khiển (component) và nếu bạn luôn phải sử dụng rất nhiều component trong các dự án của mình thì có bao giờ bạn thấy mệt mỏi khi phải tìm đến biểu tượng component Palette mà mình mong muốn trên thanh công cụ Component hay không?

    Component Palette của Delphi IDE đơn giản là một điều khiển dạng TAB với tiêu đề chỉ gồm một hàng duy nhất, vì vậy sẽ khiến bạn mất nhiều thời gian tìm kiếm khi có quá nhiều component. Bài viết này nhằm giúp giải tỏa "nỗi bức xúc" trên bằng cách thiết lập thuộc tính Multi-lines cho điều khiển TAB Component Palette bằng những thủ thuật đơn giản mà có khi bạn không hề ngờ tới. Ở đây tôi sử dụng Delphi 7 tuy nhiên với các phiên bản thấp hơn cũng không có nhiều thay đổi.

    GIỚI THIỆU VỀ DELPHI IDE

    Delphi IDE (Integrated Development Environment) là môi trường phát triển tích hợp của Delphi. Tùy thuộc vào từng phiên bản cụ thể của Delphi mà các thành phần của Delphi IDE cũng có những thay đổi nhất định. Chẳng hạn trong Delphi 7, IDE gồm có 5 thành phần chính đó là:

    1. Cửa sổ chính của Delphi: Tên mã của cửa sổ này là TAppBuilder. Cửa sổ này bao gồm trình đơn, các thanh công cụ và một bảng gồm các công cụ phát triển (Component Palette).

    2. Cửa sổ thiết kế FORM: Đây chính là cửa sổ thực tế dành cho chương trình ứng dụng của bạn. Khởi đầu cửa sổ là một FORM trống mỗi khi bạn khởi động Delphi.

    3. Cửa sổ Object Inspector: Tên mã của cửa sổ là TPropertyInspector. Đây là cửa sổ cho phép bạn thay đổi các thuộc tính cho thành phần trên FORM như tiêu đề, tên... một cách trực quan.

    4. Cửa sổ soạn thảo mã lệnh Code Editor: Tên mã của cửa sổ là TEditWindow. Đây là nơi thực sự thể hiện nội dung của chương trình, là nơi bạn gõ lệnh, thiết kế nội dung cho thủ tục, cho hàm và cài đặt các phương thức cho lớp.

    5. Cửa sổ Object TreeView: Tên mã của cửa sổ là TObjectTreeView. Cửa sổ sẽ thể hiện cho bạn một cách trực quan thứ tự cha con của các thành phần có mặt trên FORM...

    Bản thân Delphi IDE là một môi trường lắp ghép. Delphi mở ra cho bạn rất nhiều cách tiếp cận để thay đổi và chỉnh sửa sao cho phù hợp và thuận lợi với từng cá nhân. Chẳng hạn, thanh Component Palette của Delphi IDE thực tế là một đối tượng TTabControl không hơn không kém. Bạn có thể thấy được điều này thông qua một phần đoạn mã dùng để cài đặt cho cửa sổ TAppBuilder.

    object TabControl: TComponentPaleAppBuildertteTabControl

    Left = 0

    Top = 0

    Width = 64

    Height = 47

    Align = alClient

    Constraints.MinWidth = 20

    HotTrack = True

    PopupMenu = PaletteMenu

    TabOrder = 0

    TabStop = False

    OnChange = TabControlChange

    OnDragDrop = TabControlDragDrop

    OnDragOver = TabControlDragOver

    OnEndDrag = TabControlEndDrag

    OnMouseDown = TabControlMouseDown

    OnMouseMove = TabControlMouseMove

    OnStartDrag = TabControlStartDrag

    BorderStyle = bsNone

    OnHelpRequest = ComponentPaletteHelpRequest

    object PageScroller1: TPageScroller

    Left = 32

    Top = 6

    Width = 31

    Height = 39

    Align = alClient

    AutoScroll = True

    TabOrder = 0

    OnScroll = PageScroller1Scroll

    end

    object Panel2: TPanel

    Left = 4

    Top = 6

    Width = 28

    Height = 39

    Align = alLeft

    BevelOuter = bvNone

    TabOrder = 1

    object SelectorButton: TSpeedButton

    Left = 0

    Top = 0

    Width = 28

    Height = 28

    GroupIndex = 1

    Down = True

    Flat = True

    end

    end

    end

    end

    Như vậy, có hai cách để thiết lập thuộc tính Multi-lines cho điều khiển TAB Component Palette. Ý tưởng của cách thứ nhất là trực tiếp thay đổi mã nhị phân của file delphi32.exe trong thư mục BIN của Delphi. Để làm được điều này các bạn hãy thêm vào phần cài đặt thuộc tính của TabControl trong đoạn mã ở trên dòng lệnh sau:

    MultiLine = True

    Tôi đã thử cách này và kết quả mang lại khá tốt. Tuy nhiên cách này có một nhược điểm nhỏ khi Component Palette của bạn đang ở trạng thái Dock trên cửa sổ chính của Delphi thì việc thay đổi kích thước xem chừng không thể (xem hình 1).

    Hình 1: Lỗi với cách sửa trực tiếp file delphi32.exe

    Ý tưởng của cách thứ 2 là ta sẽ viết một component nhỏ. Mỗi khi Delphi nạp component này nó sẽ có nhiệm vụ đi tìm cửa sổ chính của Delphi, tiếp đến tìm đúng điều khiển TAB Component Palette và thay đổi trực tiếp thuộc tính MultiLine của TAB. Trông thì cứ như là chuyện không tưởng nhưng như đã đề cập, Delphi IDE là một môi trường lắp ghép chuyên nghiệp. Bản thân Delphi IDE mở ra rất nhiều hướng để bạn tùy biến. Chúng ta sẽ từng bước tìm hiểu mã lệnh để thực hiện những công việc trên.

    Tìm cửa sổ chính của Delphi

    Có rất nhiều cách để tìm đến cửa sổ chính của Delphi. Lưu ý, component mà bạn chuẩn bị viết tương tác trực tiếp với Delphi IDE nên bản thân nó lấy cửa sổ Application như là cửa sổ Application của Delphi. Vì vậy, theo ý kiến riêng, bạn có thể dùng đoạn mã sau để tìm cửa sổ chính:

    function GetIdeMainForm: TCustomForm;

    begin

    Result := TForm(Application.FindComponent(AppBuilder));

    end;

    Tìm điều khiển TAB Component Palette

    Để tìm được điều khiển TAB này, bạn hãy dùng đoạn mã sau:

    function GetTabControl : TTabControl;

    var

    MainForm : TCustomForm;

    begin

    Result := nil;

    MainForm := GetIdeMainForm;

    if MainForm <> nil then

    Result := TTabControl(MainForm.FindComponent(TabControl))

    end;

    Tìm menu popup của điều khiển TAB Component Palette

    Để làm được điều này, bạn hãy dùng:

    function GetComponentPalettePopupMenu : TPopupMenu;

    var

    MainForm : TCustomForm;

    begin

    Result := nil;

    MainForm := GetIdeMainForm;

    if MainForm <> nil then

    Result := TPopupMenu(MainForm.FindComponent(PaletteMenu));

    end;

     

    Hình 2: Mục chọn mới

    Sở dĩ chúng ta muốn tìm menu popup này vì ta sẽ thêm một mục chọn Multi-Lines dùng để chuyển đổi giữa hai trạng thái của TAB Component Palette (xem hình 2).

    Toàn bộ nội dung mã lệnh của component có thể xem ở phần "Mã nguồn".

    CÀI ĐẶT VÀ SỬ DỤNG

    Để sử dụng component vừa tạo, bạn cần phải cài đặt vào Delphi IDE.

    Bước 1. Lưu toàn bộ nội dung mã lệnh ở trên vào một file, chẳng hạn tôi chọn file tên là IdeEnhancement.pas.

    Bước 2. Chọn chức năng Install Component trên menu Component của Delphi IDE. Một cửa sổ mới xuất hiện. Bạn hãy khai báo các thông tin như ở hình 3. Sau đó nhấn OK.

    Hình 3: Thiết lập thông tin cho component

    Bước 3. Delphi sẽ hỏi bạn có biên dịch ngay component này hay không. Bạn hãy mạnh dạn chọn "không". Sau đó ghi lại những gì vừa thực hiện.

    Bước 4. Trong cửa sổ Package của IDE bạn hãy chọn chức năng Install (xem hình 4).

    Hình 4: Cài đặt Component

    Như vậy là đã xong. Bạn hãy đóng package lại sau đó thử nhấn chuột phải trên TAB Component Palette xem sao. Chắc bạn sẽ ngạc nhiên vì thấy sự xuất hiện của một mục chọn mới với tên là Multi-Lines. Hãy nhấn mục chọn này và quan sát sự khác biệt. (Xem hình 5)

    Hình 5. Minh họa kết quả

    Nếu tinh ý một chút chắc các bạn có thể dễ dàng nhận ra Delphi IDE của tôi được hỗ trợ theo Style XP (khi chạy trên nền Windows XP). Để làm được điều này, rất đơn giản các bạn hãy tạo một file tên delphi32.exe.manifest (thủ thuật này đã được nói nhiều trong các bài viết đã đăng trên TGVT trước đây) với nội dung như sau:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

    <assemblyIdentity

    name="NQA.XP.Theme"

    processorArchitecture="x86"

    version="1.0.10.8"

    type="win32"/>

    <description>Ngo Quoc Anh</description>

    <dependency>

    <dependentAssembly>

    <assemblyIdentity

    type="win32"

    name="Microsoft.Windows.Common-Controls"

    version="6.0.0.0"

    processorArchitecture="x86"

    publicKeyToken="6595b64144ccf1df"

    language="*"

    />

    </dependentAssembly>

    </dependency>

    </assembly>

    Sau đó lưu cùng thư mục với file delphi32. exe là được (xem hình 6).

    Hình 6: Minh họa Style XP cho Delphi IDE

    Bài viết này thực sự mới chỉ dừng lại ở giới thiệu một số mẹo nhỏ để tùy biến Delphi IDE. Hy vọng tôi sẽ có dịp khác trình bày các thủ thuật hay hơn trong lập trình cho Delphi IDE.

     

    MÃ NGUỒN

     
     

    unit IdeEnhancement;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, ExtCtrls,
    Forms, Dialogs, ComCtrls, Menus, Registry;

    type
    TMyExpertObject = class(TComponent)
    private
    App : TCustomForm;
    TabControl: TTabControl;
    MultiLine : Boolean;
    ComponentPaletteMenu : TPopupMenu;

    //Hai mục chọn cho menu popup mà ta thêm vào

    MultiLineItem, SeperatorItem : TMenuItem;
    procedure UpdateOtherWindows(OldHeight: Integer);
    procedure ResizeMultiLineComponentPalette(Sender : TObject);
    function GetIdeMainForm: TCustomForm;
    function GetTabControl: TTabControl;
    function GetComponentPalettePopupMenu: TPopupMenu;
    procedure OnMenuPopup(Sender: TObject);
    procedure OnMultiLineItemClick(Sender : TObject);
    procedure SetMultiLineComponentPalette(_multiLine : Boolean);
    procedure CreateMenuItem(_multiLine : Boolean);
    procedure DestroyMenuItem;
    procedure SaveSettings;
    public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    end;
    var
    MyExpertObject : TMyExpertObject;

    implementation

    {Đây là phương thức sẽ được Delphi gọi mỗi khi component được nạp. Chúng ta cần gọi phương thức này để đọc thuộc tính MultiLines trong Registry}

    constructor TMyExpertObject.Create;
    begin
    inherited;
    with TRegistry.Create do
    begin
    RootKey := HKEY_CURRENT_USER;
    if OpenKey(\Software\Ngo Quoc Anh, False) then
    if KeyExists(MultiLines) then
    MultiLine := ReadBool(MultiLines);
    end;
    end;

    {Đây là phương thức sẽ được Delphi gọi mỗi khi component giải phóng}

    destructor TMyExpertObject.Destroy;
    begin
    inherited;
    end;

    {Ghi lại thông tin về MultiLines trong Registry mỗi khi có sự thay đổi}
    procedure TMyExpertObject.SaveSettings;
    begin
    with TRegistry.Create do
    begin
    RootKey := HKEY_CURRENT_USER;
    if OpenKey(\Software\Ngo Quoc Anh, True) then
    WriteBool(MultiLines, MultiLine)
    end;
    end;

    {Tính toán lại kích thước của điều khiển TAB mỗi khi có sự thay đổi}
    procedure TMyExpertObject.ResizeMultiLineComponentPalette(Sender: TObject);
    var
    AHeight : Integer;
    begin
    with Sender as TTabControl do
    begin
    AHeight := Height - ( DisplayRect.Bottom - DisplayRect.Top ) + 29;
    Constraints.MinHeight := AHeight;
    ((Sender as TTabControl).Parent as TWinControl).Constraints.MaxHeight := AHeight;
    end;
    end;

    {Điều chỉnh lại vị trí của 2 cửa sổ TObjectTreeView và TEditWindow mỗi khi thay đổi kích thước của FORM chính}
    procedure TMyExpertObject.UpdateOtherWindows(OldHeight: Integer);
    const
    WinClasses : array[0..1] of string = (TObjectTreeView, TEditWindow);
    var
    AForm : TCustomForm;
    I, J, MainTop, HeightDelta : Integer;
    begin
    AForm := GetIdeMainForm;
    if AForm = nil then Exit;
    HeightDelta := AForm.Height - OldHeight;
    if HeightDelta = 0 then Exit;
    MainTop := AForm.Top;
    for I := Low(WinClasses) to High(WinClasses) do
    begin

    //Duyệt qua tất cả các cửa sổ

    for J := 0 to Screen.CustomFormCount - 1 do
    begin

    //Nếu tìm được thì tiến hành thay đổi kích thước

    if Screen.CustomForms[J].ClassNameIs(WinClasses[I]) then
    begin
    AForm := Screen.CustomForms[J];
    AForm.Top := AForm.Top + HeightDelta;
    AForm.Height := AForm.Height - HeightDelta;
    end;
    end;
    end;
    end;

    {Tìm cửa sổ chính của Delphi}

    function TMyExpertObject.GetIdeMainForm: TCustomForm;
    begin
    Result := TForm(Application.FindComponent(AppBuilder));
    end;

    {Tìm điều khiển TAB Component Palette}
    function TMyExpertObject.GetTabControl : TTabControl;
    var
    MainForm : TCustomForm;
    begin
    Result := nil;
    MainForm := GetIdeMainForm;
    if MainForm <> nil then
    Result := TTabControl(MainForm.FindComponent(TabControl))
    end;

    {Tìm menu popup cho điều khiển TAB Component Palette}

    function TMyExpertObject.GetComponentPalettePopupMenu : TPopupMenu;
    var
    MainForm : TCustomForm;
    begin
    Result := nil;
    MainForm := GetIdeMainForm;
    if MainForm <> nil then
    Result := TPopupMenu(MainForm.FindComponent(PaletteMenu));
    end;

    {Cài đặt cho phương thức popup của menu popup của điều khiển TAB. Chúng tôi không đề xuất mã lệnh nào cho sự kiện này. Điều này phụ thuộc vào ý chủ quan của bạn}

    procedure TMyExpertObject.OnMenuPopup(Sender: TObject);
    begin
    end;

    {Cài đặt cho sự kiện OnClick của mục chọn mới trong menu popup của TAB}

    procedure TMyExpertObject.OnMultiLineItemClick(Sender: TObject);
    begin
    if Sender is TMenuItem then
    begin
    MultiLine := not (Sender as TMenuItem).Checked;

    //Thay đổi trạng thái Checked của mục chọn

    (Sender as TMenuItem).Checked := MultiLine;

    //Thiết lập và ghi lại trạng thái vào Registry

    SetMultiLineComponentPalette(MultiLine);
    SaveSettings;
    end;
    end;

    {Thêm mục chọn cho menu popup của điều khiển TAB Component Palette}

    procedure TMyExpertObject.CreateMenuItem(_multiLine : Boolean);
    begin
    ComponentPaletteMenu := TPopupMenu.Create(nil);
    ComponentPaletteMenu.OnPopup := OnMenuPopup;
    ComponentPaletteMenu := GetComponentPalettePopupMenu;

    //Kiểm tra sự tồn tại của mục chọn trước, nếu chưa tồn tại thì tạo mới

    if ComponentPaletteMenu.Items.Find(&Multi-Lines) = nil then
    begin
    SeperatorItem := TMenuItem.Create(nil);
    SeperatorItem.Caption := -;

    //Thêm thanh phân cách

    ComponentPaletteMenu.Items.Add(SeperatorItem);

    MultiLineItem := TMenuItem.Create(nil);
    MultiLineItem.Checked := _multiLine;
    MultiLineItem.OnClick := OnMultiLineItemClick;
    MultiLineItem.Caption := &Multi-Lines;

    //Thêm mục chọn với tên Multi-Lines

    ComponentPaletteMenu.Items.Add(MultiLineItem);
    end;
    end;

    {Xoá mục chọn của menu popup mỗi khi Component được giải phóng}

    procedure TMyExpertObject.DestroyMenuItem;
    var
    MI : TMenuItem;
    Pos : Integer;
    begin
    MI := ComponentPaletteMenu.Items.Find(&Multi-Lines);
    if MI <> nil then
    begin
    Pos := ComponentPaletteMenu.Items.IndexOf(MI);
    ComponentPaletteMenu.Items.Delete(Pos - 1);
    ComponentPaletteMenu.Items.Delete(Pos - 1);
    end;
    end;

    {Thiết lập thuộc tính Multi-Lines}
    procedure TMyExpertObject.SetMultiLineComponentPalette(_multiLine : Boolean);
    var
    OldHeight : Integer;
    begin
    App := GetIdeMainForm;
    if App <> nil then
    begin
    OldHeight := App.Height;
    TabControl := GetTabControl;
    if TabControl <> nil then
    begin
    TabControl.MultiLine := _multiLine;
    if _multiLine then
    begin
    TabControl.OnResize := ResizeMultiLineComponentPalette;
    TabControl.OnResize(TabControl);
    CreateMenuItem(_multiLine);
    end
    else
    TabControl.OnResize := nil;
    UpdateOtherWindows(OldHeight);
    end;
    App.Invalidate;
    end;
    end;

    {Lời gọi mỗi khi Component được nạp}
    initialization

    MyExpertObject := TMyExpertObject.Create(nil);
    MyExpertObject.CreateMenuItem(MyExpertObject.MultiLine);
    MyExpertObject.SetMultiLineComponentPalette(MyExpertObject.MultiLine);

    {Lời gọi mỗi khi Component bị huỷ}
    finalization

    MyExpertObject.SetMultiLineComponentPalette(False);
    MyExpertObject.DestroyMenuItem;
    MyExpertObject.Free;

    end.

     

    Ngô Quốc Anh
    ĐHKHTN, ĐHQG Hà Nội
    Email:
    bookworm_vn@yahoo.com

    ID: A0610_102