1

Since Vcl.Forms.TScrollBox does not have BeginUpdate / EndUpdate methods, I use this code to reduce flickering while loading a document in a viewer which is a child of TScrollBox container:

procedure MyLockWinControl(const WC: Vcl.Controls.TWinControl; ALock: Boolean);
begin
  if (not Assigned(WC)) or (WC.Handle = 0) then EXIT;
  if ALock then
    WC.Perform(WM_SETREDRAW, 0, 0)
  else
  begin
    WC.Perform(WM_SETREDRAW, 1, 0);
    RedrawWindow(WC.Handle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INVALIDATE or RDW_ALLCHILDREN);
  end;
end;

procedure TFormMain.LoadDocFile(const ADocFile: string);
begin
  Screen.Cursor := crHourGlass;
  try
    MyLockWinControl(ScrollBox, True);
    try
      DoLoadDocFile(ADocFile);
    finally
      MyLockWinControl(ScrollBox, False);
    end;
  finally
    Screen.Cursor := crDefault;
  end;
end;

This works well except in some rare cases where the application reproducibly crashes on MyLockWinControl(ScrollBox, False);

So my question is: Is there a bug in MyLockWinControl, or is there a better method to stop and restore updating in TScrollBox?

user1580348
  • 5,721
  • 4
  • 43
  • 105
  • Hard to say without that reproduction. – David Heffernan Jan 25 '17 at 13:27
  • Dear David, my question is not about the mentioned crash but about whether there is a bug in `MyLockWinControl` or if there is a better method to stop and restore updating in `TScrollBox`. – user1580348 Jan 25 '17 at 13:31
  • It depends on the preconditions. For instance, if that function is called from a thread, then there's a problem. Is it? I think it is futile to ask if a single function is broken in isolation, without giving any context. Anyway, if that's the question you want to ask, fine. But I cannot help you. If there a better way to stop flickering? Almost certainly. But again, as asked, I cannot help you. Good luck. – David Heffernan Jan 25 '17 at 13:45
  • Dear David, the function is not called from a thread. – user1580348 Jan 25 '17 at 13:50
  • As I said, I cannot help you. Good luck. – David Heffernan Jan 25 '17 at 13:58
  • 1
    If it works well there is no bug. If there is a reproducible crash, then there is a bug somewhere - but not necessarily here. If you are saying the crash is nothing to do with it, then what is the question? If it is, then let us see what produces the crash. – Dsm Jan 25 '17 at 14:37
  • 1
    FYI, `if WC.Handle = 0` will *never* be true. The `Handle` getter allocates a new HWND if none is assigned, raising an exception if an error occurs. To test if the HWND is allocated without allocating a new one, use `if WC.HandleAllocated` instead. – Remy Lebeau Jan 25 '17 at 16:18
  • @Dsm If everything which (apparently) works well contains no bug then we wouldn't need debugging; we would only need to look if something (apparently) works well. This is a bad concept for programming. – user1580348 Jan 25 '17 at 16:47
  • How can anyone tell you whether the function is correct if you can't tell them what it is expected to do, what pre conditions there are and so on? Quite why you won't show a MCVE is beyond me. It's almost as if you do t want help. – David Heffernan Jan 25 '17 at 18:15
  • Could you provide stack trace for crash – EugeneK Jan 25 '17 at 18:21
  • David, if you would think logically you would come to the conclusion that there is nothing to tell: If there was something to tell I would have done so, as I am just following the advice you once gave me: It is useless to tell you something which cannot be repeated because it concerns a third party component. But that doesn't refer to my question - please reread it. Thank you. – user1580348 Jan 25 '17 at 18:27
  • It's the `RDW_FRAME` flag in the `RedrawWindow` function which causes the problem in this particular case. When I remove it the problem vanishes. Unfortunately, I cannot provide an MCVE because the concerning code is located inside a closed source DLL. So in this case, the only remaining option from the OR condition in my question is: Is there another way to stop and restore updating in `TScrollBox`? It seems there is not. – user1580348 Jan 25 '17 at 19:06

2 Answers2

2

I have used the following code quite reliably (with a TScrollBox):

  SendMessage(scrollbox.Handle, WM_SETREDRAW, 0, 0);
  try
    //do some stuff with the scrollbox here
  finally
    SendMessage(scrollbox.Handle, WM_SETREDRAW, 1, 0);
    RedrawWindow(scrollbox.Handle, nil, 0, RDW_ERASE or RDW_INVALIDATE or RDW_FRAME or RDW_ALLCHILDREN);
  end;

Your code (via the MyLockWinControl procedure) seems to essentially do the same thing - except for the use of Perform (send a message directly to the control) instead of SendMessage (uses the Windows message queue). You have also mentioned that you use a component that is not TScrollBox, but a descendent - so the issue could very well lie there as well.

Only thing I can suggest is to put a try/except block around your code and catch/log the error. That may provide further insight into where the issue lies...

Rohit
  • 909
  • 1
  • 9
  • 20
-1

The first thing I'd try is replacing your call to RedrawWindow with a call to WC.Invalidate.

I have to suppose that TWinControl is doing some stuff in addition to the API redraw that isn't getting done reliably when you call RedrawWindow directly.

Stefanya42
  • 37
  • 1
  • 4