Event reassignment

HI,

If you compare TButton and TSpeedButton you can see that TButton fires it's click event after it has been redrawn, and TSpeedButton fires it's event before it's redrawn. I mean if you press TButton, the attached procedure will be executed after you already see the button in it's original "up" position, and if you press TSpeedButton, the procedure attached will be executed while you see the button in it's "Down" state.

How can I change that for the TSpeedButton - rewrite the component somehow?

Big thanks! I just know you'll help me again...

bosism@netvision.net.il
LVL 3
MatveyAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

pjdbCommented:
Can't you use a tbitbtn? I think it redraw the way yuo want.

JDB
0
StevenBCommented:
 Have you considered inheriting a new component from the TSpeedButton which has a seperate event which fires after the Paint procedure, perhaps called OnPostPaintClick. I would imagine that this is the way forward. If you want to do this then it should be pretty simple and I'd be happy to assist further if you need.
  Post a comment if you want more info, outlining anything you're unsure of concerning inheriting this new control and setting up the event and I'll explain what's required.

  Steven.
0
ZifNabCommented:
If you want to use a TSpeedButton, follow StevenB's comment. That is the best and cleanest way.
0
Angular Fundamentals

Learn the fundamentals of Angular 2, a JavaScript framework for developing dynamic single page applications.

MatveyAuthor Commented:
1) I don't wan't to use BitBtn because it gets focuse and it can't be flat.

Steven, do you think that this event can be implemented easily? Do you understand me right: I wan't the click event after the button is redrawn back ot it's up state so it won't stuck in the dawn state while the code is running.

I think it's about TGraphicControl and TWinControl, though I had no that deep look yet...

If you know how to do exactly this, then please post here or emil me the code.

Thanks!
0
ZifNabCommented:
Matvey,

Try this, make a descendant of TSpeedButton, look in Buttons.Pas when you want to fire an event. override then the procedure and inherit it totally, place after the inherit the event.
I guess you've to override the paint method of Tspeedbutton.

Regards, Zif.

0
ZifNabCommented:
Oops, sorry was to quick, anyway here is some piece of code if you didn't know how to produce such a thing :

unit MySpButton;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons;

type
  TMatveySpeedButton = class(TSpeedButton)
  private
    { Private declarations }
    FAfterPaint : TNotifyEvent;
  protected
    { Protected declarations }
    procedure Paint; override;
  public
    { Public declarations }
  published
    { Published declarations }
    property AfterPaint: TNotifyEvent read fAfterPaint write fAfterPaint;
  end;

procedure Register;

implementation

procedure TMatveySpeedButton.Paint;
begin
 inherited Paint;
 if Assigned(fAfterPaint) then fAfterPaint(Self);
end;

procedure Register;
begin
  RegisterComponents('Samples', [TMatveySpeedButton]);
end;

end.
0
MatveyAuthor Commented:
Hmmm, let's try this one, but I'm not sure about the paint method - isn't it called whenever button just needs to be painted?

Anyway, thanks!
0
ZifNabCommented:
Yep Matvey, I said Oops because I was to quick in giving a comment about paint. Try some other out.. Regards, Zif.
0
StevenBCommented:
 Hi Matvey, Zifnab,

  The code that Zifnab gave is essentially what I was going to suggest, with a few little additions. I'd suggest that you set up a private boolean variable within the new control to keep track of whether the button has been clicked or not. As you correctly suggest Matvey, the Paint procedure will be called whenever the control requires repainting, and therefore so will Zifnab's fAfterPaint event. The code I had in mind looked something like this:

  1) setup a private variable:

  Clicked : Boolean;

  2) ensure that 'Clicked' is set to true when the button is clicked.

  3) change Zifnab's code to something like this:

procedure TMatveySpeedButton.Paint;
begin
  inherited Paint;
  if Clicked then begin
    if Assigned(fAfterPaint) then fAfterPaint(Self);
    Clicked := False;
  end;
end;

  Does this clarify things further?

  Steven.
0
ZifNabCommented:
Hi Steven, Matvey, code looks better now. c.u. Zif.
0
extremeCommented:
A very easy solution, if you inherit TSpeedButton you can write a component like this:

unit uNewSpeedButton;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons;

type
  TNewSpeedButton = class(TSpeedButton)
  private
    FHandle : THandle;
    procedure WndProc (var Msg : TMessage);
  protected
    procedure Click; override;
  public
    constructor Create (AOwner : TComponent); override;
    destructor Destroy; override;
    property Handle : THandle read FHandle;
  end;

procedure Register;

implementation

constructor TNewSpeedButton.Create;
begin
  inherited;
  FHandle := AllocateHWND (WndProc);
end;

destructor TNewSpeedButton.Destroy;
begin
  DeallocateHWND (FHandle);
  inherited;
end;

procedure TNewSpeedButton.WndProc;
begin
  with Msg do
    if Msg = WM_USER + 1
      then inherited Click
      else DefWindowProc (Msg, Handle, wParam, lParam);
end;

procedure TNewSpeedButton.Click;
begin
  PostMessage (Handle, WM_USER + 1, 0, 0);
end;

procedure Register;
begin
  RegisterComponents('Samples', [TNewSpeedButton]);
end;

end.

If you care about the handle per button you can use only one handle for all buttons, that's no problem. All you have to do is to call the PostMessage with the lParam equal to: longint (self) and in the WndProc call TNewSpeedButton (lParam).InClick, and write in the InClick a simple method that calls the inherited click.

if you want e-mail me at k2xt@iname.com and I'll made the modifications for you.

Hope this serve you.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
extremeCommented:
Well Matvewy, here's is the corrected version of my proposed answer, plus a little bug correction, sorry for not test correctly the first one before post it. Please, consider this one as the proposed answer when evaluating (if that could be done..).

unit uNewSpeedButton;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons;

type
  TNewSpeedButton = class(TSpeedButton)
  private
    procedure CallInheritedClick;
  protected
    procedure Click; override;
  public
    constructor Create (AOwner : TComponent); override;
    destructor Destroy; override;
  end;

procedure Register;

implementation

const
  WM_CALLCLICK = WM_USER + 1;
  ButtonsCount : integer = 0;

type
  TClickCaller = class
    Handle : THandle;
    constructor Create;
    destructor Destroy; override;
    procedure WndProc (var Msg : TMessage);
  end;

var
  ClickCaller : TClickCaller;

constructor TClickCaller.Create;
begin
  inherited;
  ClickCaller := self;
  Handle := AllocateHWND (WndProc);
end;

destructor TClickCaller.Destroy;
begin
  ClickCaller := nil;
  DeallocateHWND (Handle);
  inherited;
end;

procedure TClickCaller.WndProc;
begin
  with Msg do
    if Msg = WM_CALLCLICK
      then if wParam = 0
        then PostMessage (Handle, WM_CALLCLICK, 1, lParam)
        else TNewSpeedButton (lParam).CallInheritedClick
      else DefWindowProc (Msg, Handle, wParam, lParam);
end;

constructor TNewSpeedButton.Create;
begin
  inherited;
  if (ButtonsCount = 0) and (ClickCaller = nil)
    then TClickCaller.Create;
  inc (ButtonsCount);
end;

destructor TNewSpeedButton.Destroy;
begin
  dec (ButtonsCount);
  if (ButtonsCount = 0) and (ClickCaller <> nil)
    then ClickCaller.Free;
  inherited;
end;

procedure TNewSpeedButton.CallInheritedClick;
begin
  inherited Click;
end;

procedure TNewSpeedButton.Click;
begin
  if ClickCaller <> nil
    then PostMessage (ClickCaller.Handle, WM_CALLCLICK, 0, longint (self))
    else inherited;
end;

procedure Register;
begin
  RegisterComponents('Samples', [TNewSpeedButton]);
end;

end.
0
MatveyAuthor Commented:
extreme, I'm not so sure about what you do right here, but it seems not to work :-(

Here is a simple test:

procedure TForm1.NewSpeedButton1Click(Sender: TObject);
var i: LongInt;
    s: String;
begin
  for i := 1 to 10000000 do
    s := 'MU MU MU MU';
end;

Run, and click the button. If you see it's stuck in the "Down" state while this code is running then it's not what I want.

But still big thanks for you trying to help!

Cheers,
Matvey
0
StevenBCommented:
 Matvey,

  Have you tested my suggestion, or have you had problems implementing it? Leave comments if you're interested in the solution.

  Steven

0
MatveyAuthor Commented:
Ooops! forgot about you Setevn!
Sorry sorry... :-(

But how do I ensure that "Clicked" var is set to true and false correctly?
0
extremeCommented:
did you tried my code Matvey?
0
MatveyAuthor Commented:
Yes, extreme, I did - I posted a comment above... sorry, it doesn't works...
0
StevenBCommented:
 Matvey.

  You'd have to set the clicked variable by overriding the click event, but now I think about it an even simpler solution would be to do what I've demonstrated below. This code forces a repaint on a click and then fires the new event. The only problem I've been having is replicating the behaviour you're trying to eliminate in the first place. I've found that the TSpeedButton repaints before the Click event anyway.

type
  TNewSpeedButton = class(TSpeedButton)
  private
    FOnAfterPaintClick : TNotifyEvent;
  protected
    procedure Click; override;
  published
    property OnAfterPaintClick: TNotifyEvent read FOnAfterPaintClick write FOnAfterPaintClick;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Steven', [TNewSpeedButton]);
end;

procedure TNewSpeedButton.Click;
begin
  inherited;
  Repaint;
  if Assigned(FOnAfterPaintClick) then FOnAfterPaintClick(Self);
end;


  Try this component and see if it functions as you want. If not then it's possible that I've missunderstood the problem.

  Steven.

(P.S.  Yet another approach would be to look at the source code 'Buttons.pas' and edit that in some way.)

0
MatveyAuthor Commented:
Steven, don't you think that when you call inherited inside Click before Repaint that will triger the code that is attached to the event before the button repaints back?

I know the question sounds much stupid and useless, but I want to understand by this more than just making a button to do what I want... :-)

About the Buttnos.pas  - I've been looking at it for about two weeks! The point is that I'm trying to make my own button...
0
extremeCommented:
Matvey, did you checked the code that I posted as a comment. Not the answer, the code I posted as a comment is the corrected version. Do you use D3? I tested it in D2.
0
MatveyAuthor Commented:
Yes, I tested it. I posted the test I made (my comment), try to test with it... Maybe I'm wrong??
0
StevenBCommented:
 Matvey,

  "when you call inherited inside Click before Repaint that will triger the code that is attached to the event before the button repaints back?"

  Yes it will trigger the Click event, however what I'm suggesting is that you set up another event which you can use instead of the click event. In my example the OnAfterPaintClick event is definately fired after a repaint.
  As I said though:

  "The only problem I've been having is replicating the behaviour you're trying to eliminate in the first place. I've found that the TSpeedButton repaints before the Click event anyway."

  So it would seem that I don't understand the problem fully.

  Steven.
0
StevenBCommented:
 It strikes me that perhaps what you're experiencing is some subtle difference in behaviour between Windowed (TButton) and NonWindowed (TSpeedButton) Controls. Could this be the problem? Or am I missing the point again?

  Steven.
0
extremeCommented:
Matvey, I checked the code a minutes ago in my machine, and works fine. I filled the OnClick event handler of the button with the code you told me, and the program hangs up a while after the button is repainted. I have a Beta copy of D3, I'll try it in D3, maybe TSpeedButton works diferent than in D2.
0
extremeCommented:
Hi Matvey. I'm again. I tested the component in D3 and works fine, in fact, the way it works is the following:

you click the button, the button goes down
you release the button, the button goes up
after the button is repainted, the onclick is executed.

Is that what you want?

0
MatveyAuthor Commented:
Extreme, don't really know :(

Tried it... tried it...

Maybe you can send you'r test and all the code to bosism@netvision.net.il

If you say it works... (the description you wrote above is what I want...)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.