Link to home
Start Free TrialLog in
Avatar of chrisbray
chrisbrayFlag for United Kingdom of Great Britain and Northern Ireland

asked on

How can I create a generic KeyEvent handler?

I have an event handler that I use in the OnKeyDown event of a TwwDBDateTimePicker to modify the way date changes are handled when the + or - key is pressed in various combinations.  Because I have a large number of these components in my application I want to put the code in a shared unit with other common functions and procedures so that it can be assigned to all datetimepickers.

I understand that you cannot assign a regular procedure to an event, as it generates and error.  In the past I have used a dummy class to get around this problem, but it does not work in this case.  Here is what I have tried so far:

unit Common;

Uses .... wwdbdatetimepicker, DateUtils;

type TDatePickHandler = Class // Dummy Class
  procedure UpdateDateTimePicker(Sender: TObject; var Key: Word; Shift: TShiftState);
end;

// ...

end.

Unit Form1

// ....

procedure TTradePartnerForm.FormCreate(Sender: TObject);
begin
  inherited;
  ContactBirthdaywwDBDateTimePicker.OnKeyDown := TDatePickHandler.UpdateDateTimePicker; // Compile Error
end;

// ....

end.

This produces the error 'incompatible types tkeyevent and procedure' - how can I get around this problem?

Chris Bray.
Avatar of Mike Littlewood
Mike Littlewood
Flag of United Kingdom of Great Britain and Northern Ireland image

You dont need to create that function in that class. That is why you are getting the error. You are trying to assign a function that does not compare with a TwwDateTimePicker class.

You could instead write the keypress event into the form instead and not just the datetimepickers

procedure TForm1.FormKeyPress(Sender:TObject; var Key: Char);
begin
  if Sender is TwwDateTimePicker then
  begin

  end;
end;

or use a global unit

procedure MyDatePickerFunc(Sender: TObject; var Key: Word; Shift: TShiftState);
begin

end;

Then for every date time picker just put a call to this instead

procedure TForm1.ContactBirthdaywwDBDateTimePickerKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  MyDatePickerFunc(Sender, Key, Shift);
end;
Avatar of chrisbray

ASKER

Hi Mike,

Thanks for your input.  

I don't want to use the FormKeyDown option as there are LOADS of forms leading to more duplication.  I also don't really want to put the call to my procedure in the individual event handler again because of the amount of duplication.  My preference is to assign the procedure during the form create event, as it means less direct coding.  I could, for example, loop through the components setting the OnKeyDown handler thereby meaning that any other pickers added are automatically fixed.  Using a commonly available unit which contains various functions and procedures available to all forms in the application seems the best route.

Of course, I could also derive my own datetimepicker and add the functionality to that or even modify the original component source -  but again it is not what I want to do.  I could also create an extra Form to hold the event handler, but that is not efficient.

You can pass an OnClick event handler correctly using code similar to that I have shown, but not OnKeyDown.  Why not?

This is as much a learning experience as anything else, and I want to find out how to do it using a unit rather than a form or any of the other options... any further ideas?

Chris Bray.
Hi Mike,

One more point - of course if you don't use the dummy class you get an entirely different error: Incompatible types: 'method pointer and regular procedure'...

Chris Bray.
Found the answer myself - I had not created a VAR of the relevant type in the unit!  Stupid mistake...

unit Common;

type TDatePickHandler = Class(TWinControl) // Dummy Class
  procedure UpdateDateTimePicker(Sender: TObject; var Key: Word; Shift: TShiftState);
end;

// ........... other code

  var DatePickHandler: TDatePickHandler;  // adding this solved the problem

implementation

// ........... other code

procedure TDatePickHandler.UpdateDateTimePicker(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  case Key of
    VK_ADD:
      begin
        Key := 0;
        with TwwDBDateTimePicker(Sender) do
          begin
            if Shift = [] then
              Date := Date +1
            else if Shift = [ssCtrl] then
              Date := IncMonth(Date)
            else if Shift = [ssCtrl, ssShift] then
              Date := IncYear(Date);
          end;
      end;
    VK_SUBTRACT:
      begin
        Key := 0;
        with TwwDBDateTimePicker(Sender) do
          begin
            if Shift = [] then
              Date := Date -1
            else if Shift = [ssCtrl] then
              Date := IncMonth(Date, -1)
            else if Shift = [ssCtrl, ssShift] then
              Date := IncYear(Date, -1);
          end;
      end;
  end;
end;

end.

Unit Form1

// ........... other code

procedure TForm1.FormCreate(Sender: TObject);
begin
  ContactBirthdaywwDBDateTimePicker.OnKeyDown := DatePickHandler.UpdateDateTimePicker;
end;

Given that I answered the question myself I propose to request a points refund.

Chris Bray.
Im unsure why you cant assign that specific function, let me have a bit more of a look into it for you.
Note on the above code - the (TWinControl) is not needed:

  type TDatePickHandler = Class // this is sufficient.

Chris Bray
AH! .. I didnt spot that mistake in your code.
Glad you found the answer.
Avatar of geobul
geobul

Hi,

I think a more correct way for doing this is by using a class procedure in the dummy class instead (without declaring  'var DatePickHandler'):

  TDatePickHandler = Class // Dummy Class
    class procedure UpdateDateTimePicker(Sender: TObject; var Key: Word; Shift: TShiftState);
  end;
...
class procedure TDatePickHandler.UpdateDateTimePicker(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  // do something
end;

and usage:
procedure TTradePartnerForm.FormCreate(Sender: TObject);
begin
  inherited;
  ContactBirthdaywwDBDateTimePicker.OnKeyDown := TDatePickHandler.UpdateDateTimePicker; // Compile Error
end;

Regards, Geo
Hi RomMod,

I don't object closing the question. Just wanted to give another solution to the issue.

Regards, Geo
Hi Geo,

Thank you for your input, I will test it shortly.  In the meantime, can you explain why this is 'more correct'?  What is the advantage compared to the other working method?  Is it just the removal of the extra variable from the stack?

Chris Bray.
I cannot explain to myself how your way works without instantiating DatePickHandler variable right now. This doesn't necessary mean that your way is incorrect :-)
Hi Geobul,

It doesn't - that was the error I had made and found for myself.  When I originally created the dummy class, I did not instantiate the variable.  After adding the variable to the unit it worked.

This was at least in part a learning exercise, and I am grateful for your input.

Your method works flawlessly, and would appear to have the advantage that there is no need to add an extra variable to the stack - I just wondered if there were other or better reasons.  If you can answer this I would be happy to give you the points...

Chris Bray.
As long as there is no need to create an instance of that dummy class, the memory occupied by the variable is equal to one pointer only, right?

Don't worry about points. I learned something I didn't know too. I'll think why that thing works when I find some time.

Regards, Geo
ASKER CERTIFIED SOLUTION
Avatar of Netminder
Netminder

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial