7

For example I need to disable two buttons in runtime. After I disabled first button it bacame gray, the second - it also became gray. But I do not know how to make the repainting simultaneous!

I need something like that:

  1. freeze the Form (disable repainting)
  2. disable first button
  3. disable second button
  4. Enable Form repainting

How to implement that?

Kromster
  • 7,181
  • 7
  • 63
  • 111
Alexander
  • 153
  • 1
  • 2
  • 8

5 Answers5

10

Look at the Win32 API WM_SETREDRAW message. For example:

SendMessage(Handle, WM_SETREDRAW, False, 0);
Button1.Enabled := False;
Button2.Enabled := False;
SendMessage(Handle, WM_SETREDRAW, True, 0);
InvalidateRect(Handle, nil, True);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I use WM_SETREDRAW in several projects, it works fine. Also, VCL components that do have Begin/EndUpdate() methods use WM_SETREDRAW internally. – Remy Lebeau Mar 11 '10 at 01:34
  • It didn't work in this case... *1)* FALSE and TRUE are not valid params for SendMessage() [Integer required] *2)* After changing the FALSE and TRUE params to 0 and 1 respectively so that it does compile and work as intended the form doesn't repaint at the end - the buttons remain visibly "enabled" unless/until obscured and thereby forced to repaint *3)* even if it had worked, try/finally would be de rigeur *4)* also even if it had worked, it still would have been pointless due to the reliance on message processing for the painting to occur – Deltics Mar 11 '10 at 02:50
  • WM_SETREDRAW may be viable for changing the painting behaviour of an individual control (such as a listbox, whilst adding/removing items etc), but I do not believe that is going to be reliable when trying to affect the behaviour of controls that are parented by (or are themselves parents of) other controls ... you are too dependent on clipping behaviour and interactions of same between all the involved controls. i.e. in this case you would need to **WM_SETREDRAW** and subsequently Invalidate the **buttons** (and any/all other controls involved), not the parent form. – Deltics Mar 11 '10 at 02:55
  • 2
    Thanks a lot! With really tiny changes it works perfekt: SendMessage(Handle, WM_SETREDRAW, 0, 0); Dutton1.Enabled := False; Dutton2.Enabled := False; SendMessage(Handle, WM_SETREDRAW, -1, 0); Repaint; – Alexander Mar 11 '10 at 21:40
  • 1
    Careful with SETREDRAW: http://fgaillard.com/2011/02/the-unfortunate-effect-of-wm_setredraw/ – Gabriel Dec 17 '12 at 16:09
5

Messages cannot be processed until your application re-enters a message loop, so any attempt to modify/update control state that relies on message processing will not work within a single sequence of code that does not "pump" messages.

Fortunately the VCL controls typically provide a means for force repainting without waiting for messages to be processed, via the Update method:

Button1.Enabled := False;
Button2.Enabled := False;
Button1.Update;
Button2.Update;

This works independently of having to disable form repainting. The form will not repaint until your application goes into a message loop anyway, so disabling form painting and re-enabling within a single procedure that does not itself cause message processing is a waste of time.

This may not be exactly simultaneous repainting of the two buttons, but truly simultaneous painting of two separate control is impossible without getting into multithreaded GUI painting code which I think is way beyond the scope of this problem. Calling Update on two buttons in this way will be as near simultaneous in effect as you need however.

Deltics
  • 22,162
  • 2
  • 42
  • 70
  • 1
    Synchronous messages are processed immediately. Asynchronous ones need the message queue to be pumped. – David Heffernan Mar 25 '14 at 23:30
  • Only if the HWND to which the message is being sent (SendMessage) was created by the same thread that is sending the message. Otherwise, the message is still synchronous but the receiving window will only process the message when it pumps the message queue. Similarly, whilst you might send a synchronous PAINT message, the processing of that message might itself rely on asynchronous messages resulting from the PAINT handler. cont... – Deltics Mar 26 '14 at 01:55
  • ... the consequence of the unreliability of synchronous messages being processed truly synchronously (or immediately) is that if you wish to call a routine that is ordinarily called in response to a message and to do so synchronously, then the most reliable way to do so is to simply call that routine synchronously and not rely on messaging. Conversely of course, messaging is to be the preferred and more reliable mechanism under other circumstances. :) – Deltics Mar 26 '14 at 01:57
  • No. That's not really accurate. Synchronous does not mean "processed immediately". It means processed before the calling function returns. SendMessage doesn't return until the target window has processed the message. And you cannot send WM_PAINT. – David Heffernan Mar 26 '14 at 08:53
  • Anyway, I was led here by another recent question. Your answer is making more sense the the accepted answer which makes no sense to me. That said, the question is bogus because it describes a problem without explaining how to recreate the issue. Setting Enabled to True on two buttons in the same method causes no visible problems. I guess the real issue is in code that has not been shared. – David Heffernan Mar 26 '14 at 08:57
3

The above decision with WM_SETREDRAW does not update child windows.

Instead, i recommend RedrawWindow:

RedrawWindow(Handle, nil, 0, RDW_INVALIDATE or RDW_ALLCHILDREN);
3

To Elias551:

LockWindowUpdate is probably not the best way to handle this since it is intended for drag and drop operations and can introduce subtle bugs when misused.

See http://blogs.msdn.com/b/oldnewthing/archive/2007/02/22/1742084.aspx

Instead use SendMessage(hwnd, WM_SETREDRAW, FALSE, 0)

Jon Grewer
  • 31
  • 1
-1

This could help: the API LockWindowUpdate(Handle: HWND) locks drawing to the handle and children.

ex:

procedure TForm1.ColorButtons();
begin
  LockWindowUpdate(Self.Handle);
  // Make some stuff
  LockWindowUpdate(0);
end;

Once the locked handle is reset, the component is repainted

elias551
  • 15
  • 2
  • 7
    Please see http://blogs.msdn.com/b/oldnewthing/archive/2007/02/19/1716211.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2007/02/20/1726880.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2007/02/21/1735472.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2007/02/22/1742084.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2007/02/23/1747713.aspx to see why it's not a good idea. – Sertac Akyuz Jul 20 '10 at 15:37