Customizing the DBNavigator

Enhancing the TDBNavigator component with modified graphics (glyphs), custom button captions, and more. Exposing the OnMouseUp/Down event for every button.

"Ok, the DBNavigator does its job of navigating data and managing records. Unfortunately, my customers want more user-friendly experience, like custom button graphics and captions, ..."

Recently, I got an email (the sentence above comes from it) from a Delphi developer searching for a way to enhance the power of the DBNavigator component.

The 'old' DBNavigator

The DBNavigator is a great component - it provides a VCR-like interface for navigating data and managing records in database applications. Record navigation is provided by the First, Next, Prior, and Last buttons. Record management is provided by the Edit, Post, Cancel, Delete, Insert, and Refresh buttons. In one component Delphi provides everything you need, to operate on your data.

However, and I must agree with the author of the e-mail inquiry, the DBNavigator lacks some features like custom glyphs, button captions, etc...

A more powerful DBNavigator
As discussed in the "accessing protected members of a component" article, many Delphi components have useful properties and methods that are marked invisible ("protected") to a Delphi developer. Hopefully, to access such protected members of a component, a simple technique called the "protected hack" can be used.

First, we'll add a caption to every DBNavigator button, then we'll add custom graphics, and finally we'll OnMouseUp-enable each button.
From the "boring" DBNavigator, to either of:

» standard graphics + custom captions
DBNavigator Captions 1
» only captions
DBNavigator Captions 2
» custom graphics + custom captions
DBNavigator Captions + Custom Graphics

Let's rock'n'roll
The DBNavigator has a protected Buttons property. This member is an array of TNavButton, a descendant of TSpeedButton.
Since each button in this protected property inherits from TSpeedButton, if we get our hands on it, we'll be able to work with "standard" TSpeedButton properties like: Caption (a string that identifies the control to the user), Glyph (the bitmap that appears on the button), Layout (determines where the image or text appears on the button)...

From the DBCtrls unit (where DBNavigator is defined) we "read" that the protected Buttons property is declared as

Buttons: array[TNavigateBtn] of TNavButton;

Where TNavButton inherits from TSpeedButton and TNavigateBtn is an enumeration, defined as :

TNavigateBtn = 
    (nbFirst, nbPrior, nbNext, nbLast, nbInsert, 
     nbDelete, nbEdit, nbPost, nbCancel, nbRefresh);
 
 

Note that TNavigateBtn holds 10 values, each identifying different button on a TDBNavigator object. Now, let's see how to hack a DBNavigator:

Enhanced DBNavigator
First, set up a simple data editing Delphi form by placing at least a DBNavigator, a DBGrid, a DataSoure and a Dataset object of your choice (ADO, BDE, dbExpres, ...). Make sure all components are "connected".

Second, hack a DBNavigator by defining an inherited "dummy" class, above the Form declaration, like:

type THackDBNavigator = class(TDBNavigator);
  
type
  TForm1 = class(TForm)
  ...
 
 

Next, to be able to display custom captions and graphics on each DBNavigator button, we'll need to set up some glyphs. I suggest you to use the TImageList component and assign 10 pictures (bmp or ico), each representing an action of a particular button of a DBNavigator.

Third, in the OnCreate event for the Form1, add a call like:

procedure TForm1.FormCreate(Sender: TObject);
  SetupHackedNavigator(DBNavigator1, ImageList1);
end;
 
 

Make sure you add the declaration of this procedure in the private part of the form declaration, like:

type
 TForm1 = class(TForm)
  ...
 private
  procedure SetupHackedNavigator(const Navigator : TDBNavigator;
                                 const Glyphs : TImageList);
  ...
 
 

Fourth, add the SetupHackedNavigator procedure. The SetupHackedNavigator procedure adds custom graphics to each button and assigns custom caption to each button.

uses Buttons; 

procedure TForm1.SetupHackedNavigator
  (const Navigator : TDBNavigator; 
   const Glyphs : TImageList);
const
  Captions : array[TNavigateBtn] of string =
      ('Initial', 'Previous', 'Later', 'Final', 'Add',
       'Erase', 'Correct', 'Send', 'Withdraw', 'Revive');

var
  btn : TNavigateBtn;
begin
  for btn := Low(TNavigateBtn) to High(TNavigateBtn) do
  with  do
  begin
    
    Caption := Captions[btn];

    
    NumGlyphs := 1;
    
    Glyph := nil;
    
    Glyphs.GetBitmap(Integer(btn),Glyph);
                                        
    Layout := blGlyphTop;

    
    
  end;
end; 
 
 

Ok, let's explain. We iterate through all the buttons in the DBNavigator. Recall that each button is accessible from the protected Buttons array property - therefore the need for the THackDBNavigator class. Since the type of the Buttons array is TNavigateBtn we go from the "first" (using the Low function) button to the "last" (using the High function) one. For each button we simply remove the "old" glyph, assign the new one (from the Glyphs parameter), add the caption from the Captions array and mark the layout of the glyph.

Note that you can control which buttons are displayed by a DBNavigator (not the hacked one) through its VisibleButtons property. Another property whose default value you may want to change is Hints - use it to supply Help Hints of your choosing for the individual navigator button. You can control the display of the Hints by editing the ShowHints property.

That's it. "This is why you've picked Delphi" - as I love to say ;)

Gimme more!
Why stop here? You know that when you click the 'nbNext' button the dataset's current position is advanced to the next record. What if you want to move, let's say, 5 records ahead if the user is holding the CTRL key while pressing the button? How about that?
The "standard" DBNavigator does not have the OnMouseUp event - the one that caries the Shift parameter of the TShiftState - enabling you to test for the state of the Alt, Ctrl, and Shift keys. The DBNavigator only provides the OnClick event for you to handle.
However, the THackDBNavigator can simply expose the OnMouseUp event and enable you to "see" the state of the control keys and even the position of the cursor above the particular button when clicked!
Ctrl + Click := 5 rows ahead!
To expose the OnMouseUp you simply assign your custom event handling procedure to the OnMouseUp event for the button of the hacked DBNavigator. This exactly is already done in the SetupHackedNavigator procedure:
OnMouseUp := HackNavMouseUp;

Now, the HackNavMouseUp procedure could look like:

procedure TForm1.HackNavMouseUp
  (Sender:TObject; Button: TMouseButton; 
   Shift: TShiftState; X, Y: Integer);
const MoveBy : integer = 5;
begin
 if NOT (Sender is TNavButton) then Exit;

 case TNavButton(Sender).Index of
   nbPrior:
     if (ssCtrl in Shift) then
       TDBNavigator(TNavButton(Sender).Parent).
         DataSource.DataSet.MoveBy(-MoveBy);
   nbNext:
     if (ssCtrl in Shift) then
       TDBNavigator(TNavButton(Sender).Parent).
         DataSource.DataSet.MoveBy(MoveBy);
  end; 
end;(*HackNavMouseUp*)
 
 

Note that you need to add the signature of the HackNavMouseUp procedure inside the private part of the form declaration (near the declaration of the SetupHackedNavigator procedure):

type
 TForm1 = class(TForm)
  ...
 private
  procedure SetupHackedNavigator(const Navigator : TDBNavigator;
                                 const Glyphs : TImageList);
  procedure HackNavMouseUp(Sender:TObject; Button: TMouseButton;
                           Shift: TShiftState; X, Y: Integer);
  ...
 
 

Ok, let's explain, one more time. The HackNavMouseUp procedure handles the OnMouseUp event for each DBNavigator button. If the user is holding the CRL key while clickig the nbNext button, the current record for the linked dataset is moved "MoveBy" (defined as constant with the value of 5) records ahead.

What? Overcomplicated?
Yep. You do not need to mess with all this if you only need to check the state of the control keys when the button was clicked. Here's how to do the same in the "ordinary" OnClick event of the "ordinary" DBNavigator:

procedure TForm1.DBNavigator1Click
(Sender: TObject; Button: TNavigateBtn);
  function CtrlDown : Boolean;
  var
    State : TKeyboardState;
  begin
    GetKeyboardState(State);
    Result := ((State[vk_Control] And 128) <> 0);
  end;
const MoveBy : integer = 5;
begin
  case Button of
    nbPrior:
      if CtrlDown then
        DBNavigator1.DataSource.DataSet.MoveBy(-MoveBy);
    nbNext:
      if CtrlDown then
        DBNavigator1.DataSource.DataSet.MoveBy(MoveBy);
  end; //case
end;(*DBNavigator2Click*)
 
 

That's all folks!
And finally we are done. If you have any questions feel free and encouraged to post to the Delphi Programming Forum.

... Uh, oh, I cannot stop writing ...
Here's a scenario/task/idea for you:
4 buttons in 1Let's say you want only one button to replace the nbFirst, nbPrevious, nbNext, and nbLast buttons. You can use the X, and Y parameters inside the HackNavMouseUp procedure to find the position of the cursor when the button was released. Now, to this one button ("to rule them all") you can attach a picture that has 4 areas, each area is suppose to mimic one of the buttons we are replacing ... got the point?

转载于:https://www.cnblogs.com/tecsoon/archive/2009/06/04/1496281.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值