Delphi and ADODataset.Cancel - delphi

I'm using Dephi 10.1 Berlin and Access 2013.
My problem is related to TADODataSet.Cancel().
I want to show my user a message box asking for a confirmation before posting, in case data has been modified.
In the TADODataSet.BeforePost event, I added the following code:
if Application.MessageBox('Save changes?', '', 52) = idNo then
ADODataSet1.Cancel;
If the user click on btnNo, something unexpected happens.
Changes are canceled from the current record, but a new record with all fields empty is created.
The only field with some data is the one that was previously modified by the user.
If I cancel the modification via the cancel button of TDBNavigator, everything is fine.
If I simulate a click of the Cancel button of the TDBNnavigator in the BeforePost event:
if Application.MessageBox('Save changes?', '', 52) = idNo then
DBNavigator1.BtnClick(nbCancel);
I have the same behaviour, so a new empty record is created.
Any suggestion?

The help for TADODataSet.BeforePost says in part:
call Abort to cancel the Post operation (Delphi) or throw an exception (C++).
So:
if Application.MessageBox('Save changes?', '', 52) = idNo then
abort;
Note this is meant for preventing changes that don't pass validation (the common use for BeforePost) from being posted. It doesn't reset the edit buffers like cancel does. Usually that is a separate function in the UI so the user doesn't have to reenter all the changed data in the edits each time posting is rejected by calling abort in BeforePost.

Related

Why this code do not work in second time click button

I write this code for button click event . the first time I click the button every thing work correctly but when click for the second time on button it raise an error. whats the problem?
procedure TfrmMain.Button1Click(Sender: TObject);
var
B : Boolean;
begin
DM.tblTemp.DisableControls;
B:= DM.tblTemp.Locate('FoodName', DM.tblAsli.FieldByName('FoodName').AsString,[]) ;
if B then
begin
DM.tblTemp.Edit;
DM.tblTemp.FieldByName('Number').AsInteger:= DM.tblTemp.FieldByName('Number').AsInteger + 1;
DM.tblTemp.Post;
end
else
begin
DM.tblTemp.insert;
DM.tblTemp.FieldByName('FoodName').AsString := DM.tblAsli.FieldByName('FoodName').AsString;
DM.tblTemp.FieldByName('UnitPrice').AsInteger := DM.tblAsli.FieldByName('FoodPrice').AsInteger;
DM.tblTemp.FieldByName('Number').AsInteger := 1;
DM.tblTemp.Post;
end;
TotalPrice:= TotalPrice + DM.tblTemp.FieldByName('TotalPrice').AsInteger;
DM.tblTemp.EnableControls;
end;
the Error is
Row cannot be located for updating. Some values may have been changed
since it was last read
DM is data madual
tmbTbl is ADOTable
It is a pity you haven't said which DBMS you are using (e.g. Sql Server or MS Access,
nor told us a full list of the table's column types and indexes, if any.
The most likely answer to your q is that the variable B is set to False the first time
because the call to tblTemp.Locate fails to locate the Food name in question, so the Insert branch executes and the food's data is
added to the table but the second time the Edit branch executes and the error occurs,
though you have not said where, exactly. My guess would be on the call to .Post, because
the error message is one the ADO layer, which sits between the DBMS provider and your app,
emits when it attempts to post a change to the table but cannot identify which table row to update.
As I mentioned in a comment, the fix to this is usually at add a primary key index to the table, and I gather from your latest comment that this has succeeded for you. For the record and benefit of future readers it would be helpful if you could confirm which DBMS you are using and which Ole Driver you are using in your connection string.
Fwiw, I've tested your code using a table on MS Sql Server and MS Access and do not get the
error with either database.
Btw there is an obvious q, "How is it that tblTemp.Locate succeeds, but ADO is unable to
identify the correct record to post the update?" The answer is that tblTemp.Locate works in
a different way than identifying the relevant row to post the update.

Event Handler later than OnPublished

Are there any Project Server event handler events later than the "OnPublished"-event when creating a new project?
The time when the on published event occurs seems to be to early. Loading the project FieldValues does not work. They are always null.
PublishedProject project = projCollection.First().IncludeCustomFields;
projectContext.Load(project);
projectContext.Load(project.IncludeCustomFields);
CustomFieldCollection fields = project.CustomFields;
projectContext.Load(fields);
projectContext.ExecuteQuery();
Dictionary<string, object> fieldValues = project.FieldValues;
When executing the same code for an existing project everything works fine.
Instead I could do a timeout for x seconds but I would prefer a later server event where all values have already been set.
Edit:
Seems there was something wrong with my other code before executing this. Custom Fields and FieldValues are loading correctly now.
But loading the projectSiteUrl afterwards is still to early at this point. The ProjectSiteUrl stays null.
projectContext.Load(project, p => p.ProjectSiteUrl);
projectContext.ExecuteQuery();
Once a project is completely published, OnProjectChangedRemote event of ReportingEventReceiver Class gets fired.
When a new project is created and the creation process completes, OnProjectCreatedRemote event of ReportingEventReceiver Class gets fired.
I used PSI not CSOM and "ProjectSiteUrl" is null after first publish(OnPublished event).
For first publish I used OnWssWorkspaceCreated event which is in WssInteropEventReceiver class or interface.

IF statement for MessageDlg Delphi

How do I use an if statement to get the status of a button that was clicked in a `MessageDlg'?
Heres my code:
if MessageDlg('Message',mtError,[mbYesNoCancel],0) = No
then ShowMessage('Message2');
I saw my IT teacher write something like this a while ago, but I don't remember the syntax.
The documentation says:
MessageDlg returns the value of the button the user selected. The
following table lists the TMsgDlgBtn values for each type of button
that can appear in the message box, and the corresponding value that
is returned if the user selects that button:
TMsgDlgBtn Value Corresponding return value
mbOK mrOk
mbCancel mrCancel
mbYes mrYes
mbNo mrNo
mbAbort mrAbort
mbRetry mrRetry
mbIgnore mrIgnore
mbAll mrAll
mbNoToAll mrNoToAll
mbYesToAll mrYesToAll
mbClose mrClose
So you need to test for mrNo.
I do recommend that you learn where to find documentation to make your life easier.

Box::info showing "refreshEx"

I have a method that displays a validation result using the syntax
Box::info(message,title);
However, the first time I run the code it displays the correct title, but the message refreshEx.
Debugging the code the message that is being used is correct, Valid Account Number, but what displays is refreshEx. If I rerun the process the correct message is displayed, this only happens the first time.
Just in case it matters the flow is
Form - DoValidation method creates Class to call...
Class - public AccountValidation method that calls...
- private displayValidation method that contains this code
Thanks...
I have seen this error (unfortunately), in an AX 2009 installation, launched from code behind a button in a form:
if(HIEItemOrderSetup.RMAvailable < HIEItemOrderSetup.RMQuantity)
{
ok = DialogButton::Ok == box::okCancel("#HIE848",DialogButton::Ok,"#HIE849");
}
As far as I can tell it only occurs when you have a breakpoint on your form, when you are updating it. Removing the breakpoint will show the original message or at least this is what I have found.
If the message contains some fields from the database, try to execute a reread() or refresh() or refreshEx() method (depending on the context) to the datasource before showing the value through the info box.
May be the cached data is not refreshed after an update or insert.
EDIT:
If you are specting a return parameter from an Event, don't forget that this is an async process. An example on MSDN:
http://msdn.microsoft.com/en-us/library/gg843664.aspx

Why does my IMessageFilter not always work?

I'm working on Word automation and to get rid of "Call was rejected by callee" / "the message filter indicated that the application is busy" errors I implemented an IMessageFilter. The messagefilter works like a charm when I automate Word directly like:
Word.Documents.Open(...)
Document.SaveAs(...)
But when I call TOleContainer.DoVerb(ovPrimary), I still get errors when Word is displaying a modal dialog. Why does the MessageFilter not work with TOleContainers DoVerb methode?
"Call was rejected by callee" is what you always get when Word is in interactive state, ie displaying a dialog. This is not restricted to Word. It also happens with Excel, for example when the user was editing a cell. And it does not have to be obvious in the user interface either. When you start editing a cell, move focus to another application and come back to Excel, the UI doesn't give you a clue but it is still in "interactive" mode and will reject automation calls with the "Call was rejected by callee" error.
So basically when you automate Word in conjunction with user interaction (and not just with Word in a background process), you should be prepared to get and handle these errors.
Edit
If you want to know whether Excel or Word is in interactive mode before calling any other COM method: just ask the COM-server whether it is "Ready":
Result := _GetActiveOleObject('Excel.Application');
try
aSharedInstance := not VarIsClear(Result);
if aSharedInstance then
Version := Result.Version; // If this produces an exception, then use a dedicated instance.
// In case checking the version does not produce an exception, but Excel still isn't
// ready, we'll check that as well.
// By the way, for some unclear reason, partial evaluation does not work on .Ready,
// so we'll do it like this:
if aSharedInstance and (StrToIntDef(StringBefore('.', Version), 0) >= EXCEL_VERSION_2002) then
aSharedInstance := Result.Ready;
except
aSharedInstance := False;
end;
if not aSharedInstance then
Result := CreateOleObject('Excel.Application');
Update
Apparently Word doesn't have a "Ready" property (whoever said Microsoft was consistent?). In that case you need to determine its readiness yourself by calling a simple (and fast) property before the actual call, and assuming that when that throws an exception, Word isn't ready. In the above example the Version is retrieved before the Ready property. If that throws an exception, we just assume that the application (Excel in this case) isn't ready and proceed accordingly.
Something along the lines of:
while Tries <= MaxTries do
try
Version := Word.Version;
Tries := MaxTries + 1; // Indicate success
Word.TheCallYouReallyWantToDo;
except
Inc(Tries);
sleep(0);
end;
Note Word.Version does not throw an exception when a dialog is open, so that is no use for figuring out whether Word is ready. :( You will have to experiment to find one that does.
IMessageFilter doesn't handle all exceptions, for example, at some points, office applications 'suspend' their object model, at which point it cannot be invoked and throws: 0x800AC472 (VBA_E_IGNORE)
In order to get around this, you have to put your call in a loop and wait for it to succeed:
while(true)
{
try
{
office_app.DoSomething();
break;
}
catch(COMException ce)
{
LOG(ce.Message);
}
}
// continue after successful call
See here for more details.

Resources