I use a server client component, and when a file is being received in the TransferFile event of this component, I use an alert message component. So I want, if the user clicks on the alert message, the program to continue executing code in the TransferFile event to accept the file transfer in case the button is clicked, or to exit the procedure when not.
pls see bellow code:
procedure TfrmReadFile.ServerReceiveEvent(Sender: TObject;
Client: TSimpleTCPClient; Event: TTOOCSEvent);
begin
if (Event is TTOOCSEventFileTransfert) then
begin
Alert.Show;
if Alert.OnAlertClick then
begin
with (Event as TTOOCSEventFileTransfert) do
if (dlgSaveFile.Execute) then
with TMemoryStream.Create do
try
Write(Content[1], Length(Content));
SaveToFile(dlgSaveFile.FileName);
finally
Free;
end;
end;
end;
end;
but "if Alert.OnAlertClick then" is wrong
procedure TfrmReadFile.AlertAlertClick(Sender: TObject);
begin
end;
Please help me for these codes.
the AlertMessage is one of the TMS component and it hasn't ShowModal but it has Alert.Show Procedure that I use. and I want to pause the executing code untill the alert show time is finish and if user not click on alert executing the code aborted and no file saved.
From your code it's obvious that by the time you get to show the Alert, the file transfer already occurred: it's only a matter of "Do I save to file" or "Do I discard the content I already received". I'm inferring this information from your use of the TMemoryStream.Write() - that function takes a buffer as a parameter, so I assume the Content[1] gives your the buffer. This also means the Content is allready populated with the data you need. Too late to NOT transfer it, it's already in memory, all you can do is save it to disk or discard it.
I also have no idea how TMS's Alert works, but I'm going to assume only one Alert can be shown at any given time, and I'm going to assume you dropped your Alert on a component (ie: There's only one Alert in the whole program).
You should first alter the code for your "received event" to immediately move the content to a TMemoryStream. Also make sure you don't get into trouble with recursive re-entrance. Add a private field to your form, call it FLastContentReceived: TMemoryStream; Now alter your code to look like this:
procedure TfrmReadFile.ServerReceiveEvent(Sender: TObject;
Client: TSimpleTCPClient; Event: TTOOCSEvent);
begin
if (Event is TTOOCSEventFileTransfert) then
begin
// Re-entry before we managed to handle the previous received file?
if Assigned(FLastContentReceived) then
raise Exception.Create('Recursive re-entry not supported.');
// No re-entry, let's save the content we received so we can save it to file
// if the user clicks the Alert button.
FLastContentReceived := TMemoryStream.Create;
// I don't know what Content is, but you've got that in your code so I
// assume this will work:
FLastContentReceived.Write(Content[1], Length(Content);
// Show the alert; If the OnAlertClick event fires we'll have the received file content
// in the FLastContentRecevied and we'll use that to save the file.
Alert.Show;
end;
end;
You're trying to do an if on Alert.OnAlertClick - so I assume there's an event in your Alert component that's called OnAlertClick. Write this in it's event handler:
procedure TfrmReadFile.AlertAlertClick(Sender: TObject);
begin
if not Assigned(FLastContentReceived) then raise Exception.Create('Bug');
try
if dlgSaveFile.Execute then
FLastContentReceived.SaveToFile(dlgSaveFile.FileName);
finally FreeAndNil(FLastContentReceived);
end;
end;
You'd also need a way to discard the FLastContentReceived if the form is closed before the Alert button gets clicked OR there's a time-out (the Alert goes away without the user clicking it). The first job (getting rid of FLastContentReceived) when the form is closed is simle: add this to your form's OnDestroy:
FLastContentRecevid;Free;
Handling the timeout might a bit more difficult. If the Alert has an event that's called when the alert times out and the Balloon goes away without being clicked then use that event handler to do this:
FreeAndNil(FLastContentRecevid);
If it offers no such thing you could set up a TTimer for an interval equal to the timeout of the alert (or slightly longer to be safe), enable it before showing the alert and do this from it's OnTimer:
procedure TFrmReadFile.Timer1Timer(Sender: TObject);
begin
Timer1.Enabled := False;
FreeAndNil(FLastContentRecevid);
end;
Related
I am trying to figure out how to get a TObject (TLabel or TCard) as a returned value after a player has clicked on the object.
Goes like this
Player plays a spell //
Spell says hey i need a target //
spell calls get a target //
player selects a target //
target is returned to spell//
spell does xx with target//
Here is an example of code.
procedure TSpell.Spell;
var
secondTarget : Tobject;
begin
if true then
begin
secondTarget := GetSecondTarget(); //how??
if SecondTarget.classname = TCard then
begin
showmessage('you selected a TCard');
end
if SecondTarget.classname = TLabel then
begin
showmessage('you selected a label');
end
end
GetSecondTarget() : TObject;
begin
showmessage('Select a Target');
//wait for user to click something
//return the TObject to TSpell.spell
end;
I have no idea on how to do the GetSecondTarget... how to wait for user to click on something then return it to TSpell.Spell Any help on how to do this?
Windows GUI programs are event driven. You should not contemplate calling a function on the main thread that will block until an event happens.
What's more, your call to ShowMessage results in a modal dialog. And modal dialogs disable their owning windows which means that the user is not able to click on the object that you are hoping they will click on. If you wish to show a form with an instruction, then at the very least it needs to be a modeless dialog.
The bottom line here is that you need to adapt your ideas to the concepts of event-driven programming. When the user clicks on an object, an OnClick event will be raised. Attach a handler to that event and respond to it when that handler executes. If you need to behave differently to the OnClick event at different times, you'll need to maintain some state in your program to determine that behaviour switching.
I'm having trouble with the following scenario:
2 Edit's
Type something in Edit1 and press TAB, focus goes to Edit2
Edit1.OnExit -> show a Form with a message "Processing..." (makes a lengthy validation)
After the form closes, the focus on Edit2 seems to be "crashed"...
- the hole TEXT in Edit2 isn't selected
- the carret isn't flashing
Example:
Create a new form
Put 2 edits
Set this as OnExit event in Edit1:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
with TForm.CreateNew(self) do
try
Width := 100;
Height := 50;
Position := poMainFormCenter;
show;
sleep(200);
finally
Free;
end;
end;
Run the application
Set focus in the Edit1 and press TAB
I'm using:
Delphi 7 Enterprise
Windows 7 x64
This is a known problem. Windows has problems when you change focus before it's completed the last focus change (eg., focus starts changing from Edit1 to Edit2, but Edit1.OnExit does something to change focus to another control or form.
This happens, for instance, when apps try to do validations in an OnExit event and then try to return focus to the original control when the validation fails.
The easiest solution is to post a message to your form handle in the OnExit instead, and handle the focus change need there. It will fire once the target control gets the input focus, and Windows doesn't get confused.
const
UM_EDIT1_EXITED = WM_USER + 1;
type
TForm1=class(TForm)
...
private
procedure UMEdit1Exited(var Msg: TMessage); message UM_EDIT1_EXITED;
end;
implementation
procedure TForm1.Edit1Exit(Sender: TObject);
begin
PostMessage(Handle, UM_EDIT1_EXITED, 0, 0);
end;
procedure TForm1.UMEdit1Exited(var Msg: TMessage);
begin
// Show your other form here
end;
From an old Borland NG post by Dr. Peter Below of TeamB:
here is my general sermon on the "show dialog from OnExit" problem:
If an OnExit handler is triggered (which happens in response to the
Windows
message WM_KILLFOCUS) Windows is in the midst of a focus change. If you do
something in the handler that causes another focus change (like popping up
a message box or doing a SetFocus call) Windows gets terribly confused.
The
missing cursor is a symptom of that.
If you have to display a message to your user from an OnExit handler, do
it
this way:
Define a constant for a user message somewhere in the INterface
section
of your unit, above the type declaration for your form
'Const
UM_VALIDATE = WM_USER + 200;'
Give your Form a handler for this message, best placed in the private
section of the class declaration:
Procedure UMValidate( Var Msg: TMessage ); message UM_VALIDATE;
Post a UM_VALIDATE message to the form from the OnExit handler if
the contents of the field are not ok. You can pass additional
information in the wparam and lparam parameters of the message, e.g.
an error number and the Sender object. In fact you could do the whole
validation in the UMValidate handler!
I'm not sure precisely what's going on here, but it looks like the order of processing of messages is a bit messed up. Instead of killing your other form with Free, use Release and the focus will behave as you desire.
Another option is to use ShowModal instead of Show. Normally you show a processing dialog modally because you don't want the user making modifications to the main form whilst you are processing. If you do that then you can carry on using Free.
I have created one application in which Main Form Calls Sub Form on FormShow event of Main Form. Sub Form is displayed and gives two options to choose. If First option on sub form is selected then then a Message is displayed and after that Main form will be displayed. Now when application runs on first time then after option selected on subform Meassage will be displayed. But i want to display message with Main Form as Background. So any solution to this. below is the FormShow code.
Procedure TMainForm.FormShow(Sender:TObject);
begin
if (SubForm.ShowModal = mrOK) and bOption1 then
begin
ShowMessage('Enter the value');
end;
end;
If I understand correctly then your problem is that when the message box show up your main form is still invisible.
If this is the case then you have two options:
Don't show your SubForm from the OnShow event of the main form, but at a later time
Don't show the message directly after ShowModal returns, but at a later time
For point number 2 you can use a similar approach as I suggested here, using PostMessage. So your code would look somethind like this:
procedure TMainForm.FormShow(Sender:TObject);
begin
if (SubForm.ShowModal = mrOK) and bOption1 then
begin
PostMessage(Self.Handle, WM_SHOWMYDIALOG, 0, 0);
end;
end;
The handler of WM_SHOWMYDIALOG then displays the actual message. This approach can also work for point 1, see ain's answer.
PostMessageposts a message to your application's message queue which will be processed after the main form finished becoming visible.
Another Option would be to use OnActivate of the Mainform instead of onShow.
If I understand you right you want
const
UM_AFTERSHOW = WM_APP + 1;
type
TForm1 = class(TForm)
protected
procedure UMAfterShow(var Msg: TMessage); message UM_AFTERSHOW;
procedure DoShow; override;
end;
procedure TForm1.DoShow;
begin
inherited;
PostMessage(Self.Handle, UM_AFTERSHOW, 0, 0);
end;
procedure TForm1.UMAfterShow(var Msg: TMessage);
begin
ShowMessage('Enter the value');
end;
By showing your message in the UMAfterShow handler you give the main form opportunity to become visible and thus to be in background.
The problem you are seeing (if I understand properly) is that FormShow is called before your main form is actually visible. So the message dialog shows before your main form.
What you need to do is use PostMessage to post a message to your main form that you then handle. This will allow your FormShow code to finish and the code will be triggered after the form is shown.
Take a look here for an example.
Yet another option would be to drop a TTimer component on your main form to trigger the message dialog.
Drop a TTimer component on your main form and set the enabled property to False and change the time from 1000 to 100. Code your message dialog and also set the Timer.Enabled property to False in the timer event to avoid repeated firings.
Now you can Enable the Timer at the point where you would have shown the message dialog in your main form's OnShow event.
In my Delphi form's OnShow method, I determine that a dialog must be opened automatically once the form is opened - and I should be able to do this by simulating a click on a menuitem.
However, calling menuitem.Click brings up the dialog before the main form has opened - which is not what I want.
I expect that should do what I want, but I cannot find what parameters to pass for "wparam" to send the click to my menuitem.
PostMessage(handle, WM_COMMAND, wparam, 0)
The MSDN WM_COMMAND docs talk about IDM_* identifiers, but how does that appear in Delphi?
(I know this is a very old question but despite being resolved in some way the real question has really gone unanswered.)
--
The command item identifier of a 'TMenuItem' is in the Command property. According to WM_COMMAND's documentation the high word of 'wParam' would be '0' and the low word would be the menu identifier;
PostMessage(Handle, WM_COMMAND, MakeWParam(MyMenuItem.Command, 0), 0);
or simply;
PostMessage(Handle, WM_COMMAND, MyMenuItem.Command, 0);
With a popup menu item there would be a slight difference: the VCL handles popup menus' messages with a different utility window. The global PopupList variable has the handle to it in its Window property;
PostMessage(PopupList.Window, WM_COMMAND, MyPopupMenuItem.Command, 0);
Perhaps you can try to open the dialog in the OnActivate event ?
I am not really sure if the OnActivate gets fired again other than when the form is shown but if it does you can use :
procedure TForm1.FormActivate(Sender: TObject);
begin
Form2.ShowModal;
Self.OnActivate := nil;
end;
Wouldn't you have to do this with a one-time timer, if you want the form to appear as per a normal Show/ShowModal, get drawn (etc) fully, and then immediately do something else?
tmrKickOff : a TTimer, 100 ms interval, disabled at design time,
fires off a 'tmrKickOffTimer' event.
in form create,
tmrKickOff.Enabled:=false; //just in case something happened in IDE
in form show, at end of all other stuff;
tmrKickOff.Enabled:=true;
in tmrKickOffTimer
begin
tmrKickOffTimer.Enabled:=false;
menuItemClick(nil);
end;
with apologies for style, form and any error-trapping :-)
Alternatively, handle the Application.OnIdle event with something along the lines of ...
if not DialogDone then
begin
MyDialogForm.ShowModal; // or menuItem.Click ....
DialogDone := true;
end;
OnIdle won't fire (for the first time) until the Form is shown and the message queue is empty.
I don't think you can send a message directly to your menu item, but you can just post it to the main window and show your dialog from there. I do this and it works great so that the dialog box (in my case, a login prompt) appears on top of the main window to avoid confusion.
-Mark
procedure WMPostStartup(var Message: TMessage); message WM_POSTSTARTUP;
procedure TMainForm.WMPostStartup(var Message: TMessage);
begin
Self.Refresh;
// Now show the dialog box.
end;
One method I have used, which is very simular to MarkF's solution, is to create a new user defined message type and send a message using that type to yourself when you determine that you need to perform this other process after your main form displays:
const
wm_SpecialProc = wm_User + 1;
procedure TForm1.WMSpecialProc(var Message:tMessage); message wm_SpecialProc;
begin
Form2.ShowModal;
end;
procedure TForm1.OnShow(Sender:tObject);
begin
if true then
PostMessage(Application.MainForm.Handle,wm_SpecialProc,0,0);
end;
The nice thing about this method is that you are in control of the message generation, so can populate ANY lparam or wparam you want to later use by your handler. I sent the message directly through the application.mainform but you could also just say handle for the current form.
In one part of the application I'm working on, there is a form control that does validation on reception of the CMExit message, which is exactly how the Delphi documentation says to do it (this code sample is from the Delphi Help files):
procedure TDBCalendar.CMExit(var Message: TWMNoParams);
begin
try
FDataLink.UpdateRecord; { tell data link to update database }
except
on Exception do SetFocus; { if it failed, don't let focus leave }
end;
inherited;
end;
The purpose of this is to perform the validation as soon as the control loses focus. So, for example, if I were to click on the OK button, the form control would lose focus, this method would run and on an exception would set the focus back to that form control. (Thus the "click" event on the OK button would never go through and the dialog would never close).
The problem I'm having is that this form control is inside a modal dialog window. Clicking OK does indeed send the CMExit message and causes the record to update (and validation to occur). However, pressing Enter while in the form control causes the modal dialog to close without sending the CMExit message. It's as if the form control never "loses focus". This means that not only does the dialog close without the form actually validating the data, but the data set isn't updated, either.
Given this problem, where is the best place for me to place my dataset updating/validation code? I could move it up to the dialog form itself and implement it in the OnCloseQuery handler, but this would mean that the logic is duplicated in both the form control and on the form itself. (The form control is used in other places, and I want to avoid changing its behaviour).
(I speculate that CMExit isn't triggered because the control never actually does lose focus. The form is closed, but the form control still "has focus" on the closed form.)
Closing a form does not necessarily fire the on-exit event of a TControl. The user could hit Alt-F4, for example.
I'd suggest moving the validation to a separate proc, and call that separate proc from both the on-exit and on-close events.
The below code should work without too much modification:
function TDBCalendar.UpdateSuccessful: boolean;
begin
{ tell data link to update database }
{ if successful, return True, else return False }
{ this function must be Public if a form is gonna check this value }
Result := True;
try
FDataLink.UpdateRecord;
except on Exception do
Result := False;
end;
inherited;
end;
procedure TDBCalendar.CMExit(var Message: TWMNoParams);
begin
//if not valid, then don't let them leave
if not(UpdateSuccessful) then begin
SetFocus;
end;
end;
///////////////////////////////////////////
//on the form that contains your control...
///////////////////////////////////////////
procedure TMyForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
//if not valid, then don't let them close the form
if not(dbcal.ControlIsValid) then begin
Action := caNone;
end
else begin
inherited;
end;
end;