chrisbray
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(Sende r: TObject; var Key: Word; Shift: TShiftState);
end;
// ...
end.
Unit Form1
// ....
procedure TTradePartnerForm.FormCrea te(Sender: TObject);
begin
inherited;
ContactBirthdaywwDBDateTim ePicker.On KeyDown := TDatePickHandler.UpdateDat eTimePicke r; // Compile Error
end;
// ....
end.
This produces the error 'incompatible types tkeyevent and procedure' - how can I get around this problem?
Chris Bray.
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(Sende
end;
// ...
end.
Unit Form1
// ....
procedure TTradePartnerForm.FormCrea
begin
inherited;
ContactBirthdaywwDBDateTim
end;
// ....
end.
This produces the error 'incompatible types tkeyevent and procedure' - how can I get around this problem?
Chris Bray.
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.
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.
ASKER
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.
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.
ASKER
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(Sende r: TObject; var Key: Word; Shift: TShiftState);
end;
// ........... other code
var DatePickHandler: TDatePickHandler; // adding this solved the problem
implementation
// ........... other code
procedure TDatePickHandler.UpdateDat eTimePicke r(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
ContactBirthdaywwDBDateTim ePicker.On KeyDown := DatePickHandler.UpdateDate TimePicker ;
end;
Given that I answered the question myself I propose to request a points refund.
Chris Bray.
unit Common;
type TDatePickHandler = Class(TWinControl) // Dummy Class
procedure UpdateDateTimePicker(Sende
end;
// ........... other code
var DatePickHandler: TDatePickHandler; // adding this solved the problem
implementation
// ........... other code
procedure TDatePickHandler.UpdateDat
begin
case Key of
VK_ADD:
begin
Key := 0;
with TwwDBDateTimePicker(Sender
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
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
ContactBirthdaywwDBDateTim
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.
ASKER
Note on the above code - the (TWinControl) is not needed:
type TDatePickHandler = Class // this is sufficient.
Chris Bray
type TDatePickHandler = Class // this is sufficient.
Chris Bray
AH! .. I didnt spot that mistake in your code.
Glad you found the answer.
Glad you found the answer.
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(Sende r: TObject; var Key: Word; Shift: TShiftState);
end;
...
class procedure TDatePickHandler.UpdateDat eTimePicke r(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
// do something
end;
and usage:
procedure TTradePartnerForm.FormCrea te(Sender: TObject);
begin
inherited;
ContactBirthdaywwDBDateTim ePicker.On KeyDown := TDatePickHandler.UpdateDat eTimePicke r; // Compile Error
end;
Regards, Geo
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(Sende
end;
...
class procedure TDatePickHandler.UpdateDat
begin
// do something
end;
and usage:
procedure TTradePartnerForm.FormCrea
begin
inherited;
ContactBirthdaywwDBDateTim
end;
Regards, Geo
Hi RomMod,
I don't object closing the question. Just wanted to give another solution to the issue.
Regards, Geo
I don't object closing the question. Just wanted to give another solution to the issue.
Regards, Geo
ASKER
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.
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 :-)
ASKER
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.
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
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
You could instead write the keypress event into the form instead and not just the datetimepickers
procedure TForm1.FormKeyPress(Sender
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.ContactBirthdaywwDB
begin
MyDatePickerFunc(Sender, Key, Shift);
end;