My Android application is a little big, and when I launch it to my phone I need to wait 3-5 seconds.
So I decided to use TTask.
In my FormCreate event I use a TTask to load my Database to a personnal class.
When loading is finnish I load item on a TListBoxItem.
I got a problem, all background of my TImage are black, same for text and other objects.
The black rectangle with write "+2 Voir tout" is with a Opacity of 0.5, and then here it's all black.
TTask.Run(procedure
begin
Gestionnaire := TGestionnaire.Create(Cnx);
TThread.Queue(nil,
procedure
begin
ChargerHomePage;
TabControl.ActiveTab := tabHome;
aniindicator1.Enabled := false;
end);
end);
Related
I'm attempting to write an analog VU meter. I use a bitmap of a VU meter and draw the needle on to the bitmap. I am using a trackbar and it's onChange event to test the meter:
procedure TForm1.TrackBar1Change(Sender: TObject);
var
angle : integer;
x,y : integer;
Peaked : boolean;
begin
Angle := 120 - Round(sTrackBar1.Position / sTrackBar1.Max * 100 )+20;
Peaked := Angle < PeakVol;
if Peaked then
Buffer.Picture := VUImagePeaked.Picture
else
buffer.picture := VUImage.Picture;
buffer.Picture.Bitmap.Canvas.Pen.Color := clSilver;
buffer.Picture.Bitmap.Canvas.Pen.Width:=2;
buffer.Canvas.MoveTo(pivot.x,Pivot.y);
x := 150 + Round(Cos(DegToRad(Angle)) * NeedleLen);
y := PaintBox1.Height - Round(Sin(DegToRad(Angle)) *NeedleLen);
buffer.Canvas.LineTo(x,y);
PaintBox1.Canvas.Draw(0,0,buffer.Picture.Bitmap)
end;
Seems to work but what I can't get is to display the bitmap of the meter when the program starts. I have even resorted to copying the above code to both the Form.Create and Form.Activate event handlers but no joy. I created a button and added the following code to trigger the onChange event handler for the trackbar. This works and displays the meter.
procedure TForm1.Button1Click(Sender: TObject);
begin
TrackBar1.Position := 1;
end;
When I copied this to the Form.Activate handler it doesn't. Can anyone please tell me what I'm doing wrong? I'm using Delphi Berlin starter edition. Thanks
A TPaintBox must be painted using its OnPaint event. This event is triggered every time Windows needs you to redraw the control. You can't simply draw to the control's Canvas at any time, because it will just be drawn over the next time the control is repainted.
In your particular case, you don't need to move your entire block of code to the OnPaint event handler. Instead, all you need is:
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
PaintBox1.Canvas.Draw(0,0,buffer.Picture.Bitmap)
end;
Can you give me the names of the functions needed for this purpose? I'm using Delphi XE 5. I want to get this effect:
Window: half transparent
Font: fully visible.
I will use "System" font (zero problems with AA)
What do I look on MSDN? What functions (name) do I need to use?
This is basically the same idea as in Marcus' answer, but with some enhancements. You might have to adjust this to your needs, but the principle is the following:
Create form1 with the following properties:
AlphaBlend := True;
AlphaBlendValue := 128;
BorderStyle := bsNone;
Create form2 with the controls as desired and the following properties:
Color := clFuchsia; // or whatever color is not used
TransparentColor := true;
TransparentColorValue := Color;
Declare a Boolean field in form1 named AllowMove.
In TForm1.FormShow call the following code:
begin
form2.BorderStyle := bsNone;
form2.SetBounds(0, 0, ClientWidth, ClientHeight);
form2.Show;
AllowMove := true;
end;
Declare a Boolean field in form1 named AllowMove and a message handler for WM_MOVE:
procedure WMMOVE(var Message: TMessage); message WM_MOVE;
procedure TForm1.WMMOVE(var Message: TMessage);
begin
inherited;
if AllowMove then begin
Form2.Left := Message.LParamLo;
Form2.Top := Message.LParamHi;
end;
Message.Result := 0;
end;
The only way that I know to get that kind of effect is to render the window contents to an in-memory bitmap, then apply the desired alpha values to the non-font pixels, and then use UpdateLayeredWindow() to display the bitmap on a window. You cannot achieve that affect with a TForm as it relies on SetLayeredWindowAttributes() instead.
Create a 32bit bitmap and draw the desired background on it with alpha values, using a separate array to keep track of the current pixel values in the spots you are going to draw text on, then draw the actual text and use the array to detect which pixels were changed so you can clear the alpha values from just those pixels. Then display the bitmap.
You can get something close by layering two forms over each other. Set the bottom form's color to blue, enable AlphaBlend, and set AlphaBlend to something like 100. That just provides the blue background.
On second form, set TransparentColor to clBtnFace, and put your label there. Set the label font's quality to fqAntialiased.
Set both form's BorderStyle to bsNone.
Lay the second form over the first form, and there you go.
This might be workable if you don't plan on letting the user move the forms, or you move them together.
I use Delphi7, PageControl with owner-draw. I can't get so plain and nice look of tabs, as I see on not-owner-drawn PageControls. What's bad:
when using owner-draw, I can't draw on "entire" tab header area, small 1-2px frame around tab header is painted by OS.
1) Delphi not owner-draw, look is OK too (XPMan used):
2) Delphi owner-draw, you see not entire tab header can be colored (XPMan used):
I draw current tab with blue and others with white, here. Only example.
Code:
procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
c: TCanvas;
begin
c:= (Control as TPageControl).Canvas;
if Active then
c.Brush.Color:= clBlue
else
c.Brush.Color:= clWhite;
c.FillRect(Rect);
end;
2b) Delphi owner-draw in real app (XPMan used):
Why do i need to use owner-draw? Simple. To draw X button on tab headers, to paint upper-line with custom color, to paint icons from imagelists.
I'm looking for a way to paint ENTIRE rect of tab headers, not decreased rect which is given to PageControl owner-draw events. I tried to increase the rect given by owner-draw events, but this doesn't help, OS repaints this thin 1-2px frame around tab headers anyway.
The tabs of an owner drawn native "tab control" (TPageControl in VCL, although its ascendant is appropriately named TCustomTabControl - it is anyone's guess why the creative naming..), is expected to be painted by its parent control while processing WM_DRAWITEM messages, as documented here.
The VCL takes the burden from the parent by mutating the message to a CN_DRAWITEM message and sending it to the control itself. In this process the VCL has no further intervention. It just calls the OnDrawTab message handler if it is assigned by user code, passing appropriate parameters.
So, it's not the VCL that draws the borders around tabs, but the OS itself. Also, evidently, it doesn't do this during processing of WM_DRAWITEM messages but later in the painting process. You can verify this by putting an empty WM_DRAWITEM handler on the parent of a page control. Result is, whatever we paint in the event handler, it will later get borders by the OS.
What we might try is to try to prevent what the OS draws take effect, we have the device context (as Canvas.Handle) after all. Unfortunately this route also is a dead end because the VCL, after the event handler returns, restores the device context's state.
The only way, then, we have is to completely abandon handling an OnDrawTab event, and acting upon CN_DRAWITEM message. Below sample code use an interposer class, but you can subclass the control any way you like. Make sure that OwnerDrawn is set.
type
TPageControl = class(comctrls.TPageControl)
protected
procedure CNDrawitem(var Message: TWMDrawItem); message CN_DRAWITEM;
end;
TForm1 = class(TForm)
..
..
procedure TPageControl.CNDrawitem(var Message: TWMDrawItem);
var
Color: TColor;
Rect: TRect;
Rgn: HRGN;
begin
Color := 0;
// draw in different colors so we see where we've drawn
case Message.DrawItemStruct.itemID of
0: Color := $D0C0BF;
1: Color := $D0C0DF;
2: Color := $D0C0FF;
end;
SetDCBrushColor(Message.DrawItemStruct.hDC, Color);
// we don't want to get clipped in the passed rectangle
SelectClipRgn(Message.DrawItemStruct.hDC, 0);
// magic numbers corresponding to where the OS draw the borders
Rect := Message.DrawItemStruct.rcItem;
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then begin
Inc(Rect.Left, 2);
// Inc(Rect.Top, 1);
Dec(Rect.Right, 2);
Dec(Rect.Bottom, 3);
end else begin
Dec(Rect.Left, 2);
Dec(Rect.Top, 2);
Inc(Rect.Right, 2);
Inc(Rect.Bottom);
end;
FillRect(Message.DrawItemStruct.hDC, Rect,
GetStockObject(DC_BRUSH));
// just some indication for the active tab
SetROP2(Message.DrawItemStruct.hDC, R2_NOTXORPEN);
if Bool(Message.DrawItemStruct.itemState and ODS_SELECTED) then
Ellipse(Message.DrawItemStruct.hDC, Rect.Left + 4, Rect.Top + 4,
Rect.Left + 12, Rect.Top + 12);
// we want to clip the DC so that the borders to be drawn are out of region
Rgn := CreateRectRgn(0, 0, 0, 0);
SelectClipRgn(Message.DrawItemStruct.hDC, Rgn);
DeleteObject(Rgn);
Message.Result := 1;
inherited;
end;
Here is how the above looks here:
From what I can tell, you are simply looking to have themed painting of your application. In Delphi 7, all you need to do to achieve that is to add an application manifest that specifies the use of comctl32 version 6. The simple way to do so is to add a TXPManifest component to one of your forms or data modules, or just to reference the XPMan unit in your project.
Since you want the system to paint your page control, you must not do any owner drawing.
We have a win control object which moves its clients to some other coordiantes. The problem is, when there are too many children - for example 500 controls - the code is really slow.
It must be because of each control being repainted each time I set Left and Top property. So, I want to tell the WinControl object stop being repainted, and after moving all objects to their new positions, it may be painted again (Something like BeginUpdate for memo and list objects). How can I do this?
Here's the code of moving the objects; it's quite simple:
for I := 0 to Length(Objects) - 1 do begin
with Objects[I].Client do begin
Left := Left + DX;
Top := Top + DY;
end;
end;
As Cosmin Prund explains, the cause for the long duration is not an effect of repainting but of VCL's realignment requisites at control movement. (If it really should take as long as it does, then you might even need to request immediate repaints).
To temporarily prevent realignment and all checks and work for anchors, align settings and Z-order, use DisableAlign and EnableAlign. And halve the count of calls to SetBounds by called it directly:
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
Control: TControl;
begin
for I := 0 to 499 do
begin
Control := TButton.Create(Self);
Control.SetBounds((I mod 10) * 40, (I div 10) * 20, 40, 20);
Control.Parent := Panel1;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
C: TControl;
begin
// Disable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(False), 0);
Panel1.DisableAlign;
try
for I := 0 to Panel1.ControlCount - 1 do
begin
C := Panel1.Controls[I];
C.SetBounds(C.Left + 10, C.Top + 5, C.Width, C.Height);
end;
finally
Panel1.EnableAlign;
// Enable Panel1 paint
SendMessage(Panel1.Handle, WM_SETREDRAW, Integer(True), 0);
// Update client area
RedrawWindow(Panel1.Handle, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW or RDW_ALLCHILDREN);
end;
end;
Your assumption that the slowness comes from re-painting controls is probably true, but not the whole story. The default Delphi code that handles moving controls would delay painting until the next WM_PAINT message is received, and that would happen when the message queue is pumped, after you complete moving all the controls. Unfortunately there are a lot of things involved in this, that default behavior can be altered in many places, including Delphi and Windows itself. I've used the following code to test what happens when you move a control at runtime:
var i: Integer;
begin
for i:=1 to 100 do
begin
Panel1.Left := Panel1.Left + 1;
Sleep(10); // Simulate slow code.
end;
end;
The behaviour depends on the control! A TControl (example: TLabel) is going to behave according to Delphi's rules, but a TWinControl depends on too many factors. A simple TPanel is not repainted until after the loop, in the case of TButton on my machine only the background is re-painted, while a TCheckBox is fully repainted. On David's machine the TButton is also fully repainted, proving this depends on many factors. In the case of TButton the most likely factor is the Windows version: I tested on Windows 8, David tested on Windows 7.
AlignControl Avalanche
Anyhow, there's an other really important factor to be taken into account. When you move a control at runtime, all the rules for alignment and anchoring for all the controls need to be taken into account. This likely causes an avalanche of AlignControls / AlignControl / UpdateAnchorRules calls. Since all those calls end up requiring recursive invocations of the same, the number of calls will be exponential (hence your observation that moving lots of objects on a TWinControl is slow).
The simplest solution is, as David suggests, placing everything on a Panel and moving the panel as one. If that's not possible, and all your controls are actually TWinControl (ie: they have a Window Handle), you could use:
BeginDeferWindowPos, DeferWindowPos, EndDeferWindowPos
I would put all the controls in a panel, and then move the panel rather than the controls. That way you perform the shift in a one single operation.
If you would rather move the controls within their container then you can use TWinControl.ScrollBy.
For what it is worth, it is more efficient to use SetBounds than to modify Left and Top in separate lines of code.
SetBounds(Left+DX, Top+DY, Width, Height);
To speed up you should set the Visible property of you WinControl to False during child movement to avoid repainting.
Together with SetBounds you will get the best from moving the child controls.
procedure TForm1.MoveControls( AWinControl : TWinControl; ADX, ADY : Integer );
var
LIdx : Integer;
begin
AWinControl.Visible := False;
try
for LIdx := 0 to Pred( AWinControl.ControlCount ) do
with AWinControl.Controls[LIdx] do
begin
SetBounds( Left + ADX, Top + ADY, Width, Height );
end;
finally
AWinControl.Visible := True;
end;
end;
BTW As David suggested, moving the parent is much faster than each child.
I have some troubles with rendering my canvas. Actually I'm trying to write game like space invaders, without using any OpenGL or DirectX. So at the background I have moving sky, and jet moving over it. But the jet is blinding, and sky moves not uniformly. Here's my code
sky := TBitmap.Create;
sky.LoadFromFile('sky.bmp');
jet := TBitmap.Create;
jet.LoadFromFile('jet.bmp');
jet.Transparent := True;
while True do
begin
for k := 0 to sky.Height do
begin
for i := -1 to (pbMain.Height div sky.Height) do
begin
for j := 0 to (pbMain.Width div sky.Width) do
begin
pbMain.Canvas.Draw(nx, ny, jet);
pbMain.Canvas.Draw(j*sky.Width, k + i*sky.Height, sky);
end;
Application.ProcessMessages;
end;
Sleep(1);
end;
end;
Thank you.
You can't write a standard Windows app like that. You have to do your painting in response to WM_PAINT messages. In Delphi terms this equates to overriding the Paint method of a TWinControl descendent, or perhaps using a TPaintBox and providing an OnPaint event handler. I'm going to assume that you use a TPaintBox.
If you need to avoid flicker it is common practice to draw to an off-screen bitmap and then show this when you are asked to paint.
Your application should probably use a timer control to provide a regular pulse. Then, on each pulse, update your off-screen bitmap. Then call Invalidate on your paint box to force a paint cycle.
The code might look like this:
procedure TMainForm.RefreshTimerTimer(Sender: TObject);
begin
RedrawOffscreenBitmap;
PaintBox.Invalidate;
end;
procedure TMainForm.RedrawOffscreenBitmap;
begin
//paint to FOffscreenBitmap
end;
procedure TMainForm.PaintBoxBox(Sender: TObject);
begin
PaintBox.Canvas.Draw(0, 0, FOffscreenBitmap);
end;