Solved

Event reassignment

Posted on 1998-03-29
26
290 Views
Last Modified: 2010-04-06
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
0
Comment
Question by:Matvey
  • 8
  • 6
  • 6
  • +2
26 Comments
 
LVL 1

Expert Comment

by:pjdb
ID: 1361006
Can't you use a tbitbtn? I think it redraw the way yuo want.

JDB
0
 
LVL 4

Expert Comment

by:StevenB
ID: 1361007
 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
 
LVL 8

Expert Comment

by:ZifNab
ID: 1361008
If you want to use a TSpeedButton, follow StevenB's comment. That is the best and cleanest way.
0
 
LVL 3

Author Comment

by:Matvey
ID: 1361009
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
 
LVL 8

Expert Comment

by:ZifNab
ID: 1361010
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
 
LVL 8

Expert Comment

by:ZifNab
ID: 1361011
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
 
LVL 3

Author Comment

by:Matvey
ID: 1361012
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
 
LVL 8

Expert Comment

by:ZifNab
ID: 1361013
Yep Matvey, I said Oops because I was to quick in giving a comment about paint. Try some other out.. Regards, Zif.
0
 
LVL 4

Expert Comment

by:StevenB
ID: 1361014
 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
 
LVL 8

Expert Comment

by:ZifNab
ID: 1361015
Hi Steven, Matvey, code looks better now. c.u. Zif.
0
 
LVL 1

Accepted Solution

by:
extreme earned 50 total points
ID: 1361016
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
 
LVL 1

Expert Comment

by:extreme
ID: 1361017
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
 
LVL 3

Author Comment

by:Matvey
ID: 1361018
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 4

Expert Comment

by:StevenB
ID: 1361019
 Matvey,

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

  Steven

0
 
LVL 3

Author Comment

by:Matvey
ID: 1361020
Ooops! forgot about you Setevn!
Sorry sorry... :-(

But how do I ensure that "Clicked" var is set to true and false correctly?
0
 
LVL 1

Expert Comment

by:extreme
ID: 1361021
did you tried my code Matvey?
0
 
LVL 3

Author Comment

by:Matvey
ID: 1361022
Yes, extreme, I did - I posted a comment above... sorry, it doesn't works...
0
 
LVL 4

Expert Comment

by:StevenB
ID: 1361023
 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
 
LVL 3

Author Comment

by:Matvey
ID: 1361024
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
 
LVL 1

Expert Comment

by:extreme
ID: 1361025
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
 
LVL 3

Author Comment

by:Matvey
ID: 1361026
Yes, I tested it. I posted the test I made (my comment), try to test with it... Maybe I'm wrong??
0
 
LVL 4

Expert Comment

by:StevenB
ID: 1361027
 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
 
LVL 4

Expert Comment

by:StevenB
ID: 1361028
 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
 
LVL 1

Expert Comment

by:extreme
ID: 1361029
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
 
LVL 1

Expert Comment

by:extreme
ID: 1361030
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
 
LVL 3

Author Comment

by:Matvey
ID: 1361031
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

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now