Richard Woolf
Richard Woolf

Reputation: 579

Delphi: Screen.Cursor not working and can't figure out Windows.SetCursor(crHourGlass)

In my app I have

Screen.Cursor := crHourGlass;
Application.ProcessMessages;
try
...

finally
  Screen.Cursor := crDefault;
  Application.ProcessMessages;
end;

But this simply isn't working as expected. It seems to immediately change back to crDefault when it is processing.

After some Googling I decided to try Windows.SetCursor() - but I've searched the MSDN and I can't find the list of cursor types.

Update I thought I found the solution (using SetSystemCursor(Screen.Cursors[crHourGlass], OCR_NORMAL);) but I can't seem to then change the cursor back to normal :(.

Upvotes: 5

Views: 14330

Answers (4)

Djone Kreusch
Djone Kreusch

Reputation: 1

Try this:

Screen.Cursor := crHourGlass;
try
  ...
finally
  TThread.Synchronize(nil,
    procedure
    begin
      Screen.Cursor := crDefault;
    end
  );
end;

for me it's working properly.

Upvotes: 0

LZR
LZR

Reputation: 958

I had the same problem on my application and the solution that worked for me was to call the Application.ProcessMessages in the form constructor

here is my test application:

// MainFormUnit
type
  TMainForm = class(TForm)
    btnClickMe: TButton;
    procedure btnClickMeClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses
  LazyFormUnit;

{$R *.dfm}

procedure TMainForm.btnClickMeClick(Sender: TObject);
var
  oLazyForm: TLazyForm
begin
  oLazyForm := TLazyForm.Create(Self, 0);
  oLazyForm.ShowModal;
  oLazyForm.Free;
end;

and the second form

// LazyFormUnit
type
  TLazyForm = class(TForm)
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    constructor Create(p_oComponent: TComponent; p_nValue: Integer); reintroduce;
  end;

implementation

{$R *.dfm}

constructor TLazyForm.Create(p_oComponent: TComponent; p_nValue: Integer);
begin
  inherited Create(p_oComponent);
  Application.ProcessMessages;
end;

procedure TLazyForm.FormShow(Sender: TObject);
begin
  Screen.Cursor := crHourGlass;
  Application.ProcessMessages;
  try
    Sleep(1000 * 5);
  finally
    Screen.Cursor := crDefault;
  end;
end;

This solution will keep the system cursor unchanged.

Upvotes: 3

Richard Woolf
Richard Woolf

Reputation: 579

I think I have the solution:

Here is how to change the cursor for 'the whole desktop' - not just for your application:

SetSystemCursor(Screen.Cursors[crDefault], OCR_NORMAL);

But be warned: any other applications/windows that want to change cursors will do so - so this is only effective if your user doesn't mess around with other applications while YOUR application is busy. As an over-ride, you could temporarily change all your systems default cursors to the cursor you want - and change them all back after the process.

I am still disappointed at the MSDN for not providing its cursor types for SetCursor - but fortunately I didn't end up having to use it.

Update: This seems to be the right track, but I can't seem to change the cursor back after SetSystemCursor(Screen.Cursors[crHourGlass], OCR_NORMAL); If anybody's reading this, I would appreciate if you take a moment to provide me with some working code - that 1. Sets the System Cursor to an hourglass, and then back to an arrow.

edit: Sample code for reverting back to default cursor:

procedure TForm1.Button1Click(Sender: TObject);
var
  cArrow, cHour: HCURSOR;
begin
  cArrow := CopyImage(Screen.Cursors[crArrow], IMAGE_CURSOR, 0, 0, LR_COPYFROMRESOURCE);
  cHour := CopyImage(Screen.Cursors[crHourGlass], IMAGE_CURSOR, 0, 0, LR_COPYFROMRESOURCE);
  if (cArrow <> 0) and (cHour <> 0) and SetSystemCursor(cHour, OCR_NORMAL) then
    try

      // do processing

    finally
      SetSystemCursor(cArrow, OCR_NORMAL);
    end;
end;

Upvotes: 6

Larry Lustig
Larry Lustig

Reputation: 51000

Depends on what's in your try block. If that doesn't take any time, then the cursor will change back right away. If you put a debug statement right before the finally, you should see it execute before the cursor returns to crDefault.

Also, you should not necessarily assume that the cursor was crDefault when you start your routine. A safe method is:

var
  C: TCursor;

begin

  C : = Screen.Cursor;
  Screen.Cursor := crHourGlass;

  try
    // long running code here
  finally
    Screen.Cursor := C;
  end;

end;

And, finally (if you'll excuse the expression), you do not need Application.ProcessMessages if the purpose you're using it for is to make sure the changed cursor is shown.

Upvotes: 1

Related Questions