Look at the image below:
As you can see I cannot send Buttons to back. This only works for labels.
So how can I send TImage to front with its transparency.
By the way I've read This related question but didn't help me. Because you cannot even click on a button after running Andreas Rejbrand's code. Not only buttons, everything (like the scrollbar in this image)
Edit:
I don't want to make the button reachable after I send that back to the image. Just want to bring TImage to front of everything.
Thanks.
One way which could you near the goal would be to use interposer classes for the TWincontrols and paint the image moved on them, after they have been painted already, using a TControlCanvas and "hooking" WM_PAINT.
The code is showing a raw draft using a semitransparent PNG image and may be enhanced.
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, dxGDIPlusClasses, ExtCtrls;
type
TButton=Class (StdCtrls.TButton)
Procedure WMPaint(var MSG:TMessage);Message WM_Paint;
End;
TEdit=Class (StdCtrls.TEdit)
Procedure WMPaint(var MSG:TMessage);Message WM_Paint;
End;
TForm2 = class(TForm)
Image1: TImage;
SpeedButton1: TSpeedButton;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TButton }
procedure TButton.WMPaint(var MSG: TMessage);
var
cc:TControlCanvas;
begin
inherited;
CC:=TControlCanvas.Create;
CC.Control := TControl(Self);
CC.Draw(-Left,-Top,Form2.Image1.Picture.Graphic);
CC.Free;
end;
procedure TEdit.WMPaint(var MSG: TMessage);
var
cc:TControlCanvas;
begin
inherited;
CC:=TControlCanvas.Create;
CC.Control := TControl(Self);
CC.Draw(-Left,-Top,Form2.Image1.Picture.Graphic);
CC.Free;
end;
end.
Another (better) place to "hook" would be overriding PaintWindow
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Buttons, dxGDIPlusClasses, ExtCtrls;
type
TButton=Class (StdCtrls.TButton)
procedure PaintWindow(DC: HDC);override;
End;
TEdit=Class (StdCtrls.TEdit)
procedure PaintWindow(DC: HDC);override;
End;
TForm2 = class(TForm)
Image1: TImage;
SpeedButton1: TSpeedButton;
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TButton }
procedure TButton.PaintWindow(DC: HDC);
var
cc:TCanvas;
begin
inherited;
CC:=TCanvas.Create;
CC.Handle := DC;
CC.Draw(-Left,-Top,Form2.Image1.Picture.Graphic);
CC.Free;
end;
procedure TEdit.PaintWindow(DC: HDC);
var
cc:TCanvas;
begin
inherited;
CC:=TCanvas.Create;
CC.Handle := DC;
CC.Draw(-Left,-Top,Form2.Image1.Picture.Graphic);
CC.Free;
end;
end.
You do not want the Image brought to front (which by the way is impossible over a windowed control), because you want the button also reachable.
Although your question is contradicting itself, and it is not at all clear what exactly you want to achieve, I think you mean to have a transparent button over an image.
If so, then use a TSpeedButton, and set its Transparent and Flat property to True.
Here an example with the three button states: normal, hovered, pressed:
You can use the solution you linked in your question. For the controls that you want the clicks go through - disable them. Since you're putting the image on a panel, disabling both the panel and the image will let the button to be clicked.
Related
I did:
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
DataTable.qOrders.Next;
end;
It works, but the problem is when I click the button to reach the last record, the button is not disabled, like in a TDBNavigator.
How did I make the TSpeedButton disable and enable automatically like the TDBNavigator does?
Drop a TActionList onto your form and add the standard dataset actions to it. Connect these actions to your dataset and your speedbuttons to the appropriate actions. These standard actions will handle the enable state according to the current dataset state.
Here is a simple solution, that works perfectly for me.
I have a form (frmMain), dataset (dsWork), datasource (srcWork), grid and two speedbuttons (btnNext and btnPrior). The important part is in "OnDataChange" event of TDataSource. Here is the source code:
unit MainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, DB, DBTables, StdCtrls, ExtCtrls;
type
TfrmMain = class(TForm)
btnNext: TButton;
srcWork: TDataSource;
dsWork: TTable;
btnPrior: TButton;
grdWork: TDBGrid;
procedure btnNextClick(Sender: TObject);
procedure btnPriorClick(Sender: TObject);
procedure srcWorkDataChange(Sender: TObject; Field: TField);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.btnNextClick(Sender: TObject);
begin
if not dsWork.Eof then dsWork.Next;
end;
procedure TfrmMain.btnPriorClick(Sender: TObject);
begin
if not dsWork.Bof then dsWork.Prior;
end;
procedure TfrmMain.srcWorkDataChange(Sender: TObject; Field: TField);
begin
btnNext.Enabled := not dsWork.Eof;
btnPrior.Enabled := not dsWork.Bof;
end;
end.
Here I have reproduced situation I have encounter couple of times. I have two forms. Form1 and Form2. Form1 has one edit field and OnKeyup event hooked up. Form2 has only one button and OnClick hooked up. When in Form1 user press VK_Retrun in field of type TEdit, Form2.Show is executed. Form2 shows up with focus on the button. The event OnClick is hooked up with the code "Close" inside. If user hit VK_RETURN key on the keyboard, Form2 closes as expected.. but here come the problem, Form2 got fired straight up again. It seems as when Form1 get focus the key is still in "a queue" and the edit field will proceed with VK_RETURN.
Here is a full listings of this situation:
unit UTestButton;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure Edit1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses UTestButton2;
{$R *.dfm}
procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_Return) THEN
Form2.Show;
end;
end.
This is the second unit.
unit UTestButton2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
Close;
end;
end.
In praxes I use sometimes simple form with labels or informations but one or two buttons and focus on button. User can hit key on keyboard as expected. If he use the vk_return and the underlying control use the key, I have to do some workaround to clear keys of buffer as with
PeekMessage(Mgs, 0, WM_CHAR, WM_CHAR, PM_REMOVE);
That´s not totally satisfying for me. Has anyone solution for this situation?
Simply handle the OnKeyDown event instead of OnKeyUp for your initial edit box:
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = VK_RETURN) then
Form2.Show;
end;
How to create (when I want to show it) and destroy (when I want to hide it) frames on the main TForm? Frames' align = alClient.
I tried this:
The form:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, uFrame1, uFrame2;
type
TFormMain = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
f1: TFrame1;
f2: TFrame2;
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
procedure TFormMain.FormCreate(Sender: TObject);
begin
f1 := TFrame1.Create(Self);
f1.Parent := Self;
end;
end.
First frame:
unit uFrame1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame1 = class(TFrame)
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame2;
procedure TFrame1.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f2 := TFrame2.Create(FormMain);
FormMain.f2.Parent := FormMain;
end;
end.
Second frame:
unit uFrame2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TFrame2 = class(TFrame)
lbl1: TLabel;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
uses main, uFrame1;
procedure TFrame2.btn1Click(Sender: TObject);
begin
Self.Free;
FormMain.f1 := TFrame1.Create(FormMain);
FormMain.f1.Parent := FormMain;
end;
end.
but it crashes with access vialataions when I click button on FrameStart or Frame1 (TForm FormCreate works fine i.e. it creates and shows FrameStart).
Delphi 7.
You can't call Self.Free in those event handlers. When the event handler returns, the VCL code that executes next still uses a reference to an object that you just freed. And that's where the access violation comes from. If you had been running with FastMM in full debug mode then you would have been shown a helpful diagnostic message.
These frames will have to kill themselves in a more roundabout manner. Post a CM_RELEASE message to the frame asking it to call Free on the frame. You post the message, rather than sending it, so that all the in flight messages are processed first. You'll need to add a message handler to the frame to respond to the message.
You've got some of it.
The basic idea behind this sort of stuff.
add a private property to your mainform to hold the frame.
in the button click handler assuming you only want one at a time do
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
fMyFrame := TSomeFrame.Create(self);
fMyFrame.Parent := self;
fMyFrame.blah...
When you just want to get rid of it as opposed to replacing it
if assigned(fMyFrame) then
begin
fMyFrame.Free;
fMyFrame := nil;
end;
If you want your frame to raise another frame, repeat the above in there.
If you want the frame you raise in a frame to be a sibling, e.g. have the same Parent, then don't use Form1 var.
fMyNextFrame.Parent = self.Parent;
There's a huge number of ways you could improve on this once you get it working, it's a classic scenario for interfaces and or inheritance, but figure this bit out first.
mySomething := TMySomething.Create();
you can now do something with something.
After you called free, it's not can't, it's don't and don't let anything else either.
Don't do self.free, it's like playing with matches in barrel of petrol. It will hurt....
First, Please download this file ( download ).
how to i can set Form2 to "Send to back" for show Image1 to user ??
i use Image1.BringToFront; but this code not work!!
here is main unit:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses unit2;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
tFrm2:Tform2;
begin
tFrm2:=Tform2.Create(self);
tFrm2.Parent:=self;
tFrm2.Align:=alClient;
tFrm2.Show;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
// Image1.BringToFront;
end;
end.
The way you're doing it, many Form2 instances can be stacked over the image, so you can search for all child forms (I mean, all forms which parent is Form1) and hide each. Final result is image is shown again.
procedure TForm1.Button2Click(Sender: TObject);
var
I: Integer;
begin
for I := 0 to Screen.FormCount - 1 do
if (Screen.Forms[I].Parent = Self) then
Screen.Forms[I].Hide;
end;
Best regards.
When a user selects a value in my TDateTimePicker I want to override the to-be-set value to the start of the week that goes with the selected value.
I tried setting it in the OnChange event, but then the originally selected value will be set right after I finished the event.
How would I go about this?
use the "ONCloseUp" event - this sample works for me (Delphi 7, WinXP)
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, DateUtils, StdCtrls;
type
TForm1 = class(TForm)
dtp1: TDateTimePicker;
btn1: TButton;
edt1: TEdit;
procedure btn1Click(Sender: TObject);
procedure dtp1CloseUp(Sender: TObject);
private
{ Private declarations }
procedure SetDayToMonday();
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.SetDayToMonday;
begin
dtp1.DateTime := dtp1.DateTime - DayOfTheWeek(dtp1.DateTime) + 1;
end;
procedure TForm1.dtp1CloseUp(Sender: TObject);
begin
SetDayToMonday;
end;
end.
--reinhard :-)
Use the onUserInput event!
I would post a message to the form, define a message (WM_USER+1000+X), post it, and handle it. If you don't "pend" it like this, you could also do a PendingDateTimeTimer:TTimer that does validation slightly later (say 10msec) after the OnChange event sets PendingDateTimeTimer.Enabled := true.