'cursor is in BOF position' error - delphi

On before delete of the table I have :
procedure TData_Module.MyTableBeforeDelete(DataSet: TDataSet);
begin
if MessageDlg('Are you sure you want to delete the record written by '+
QuotedStr(MyTable.FieldByName('written_by_who').AsString),mtConfirmation,[mbYes,mbNo],0) = mrYes then
MyTable.Delete;
end;
However, on trying I get the error :
...cursor is in BOF position
What goes ? Database is Accuracer.
edit : The entire code above :)

As I said in comments, it sounded from your q as if you are calling Delete within the dataset's BeforeDelete event. Don't do that, because the BeforeDelete event occurs while the dataset is already in the process of deleting the record. So you deleting it is pulling the rug out from under the dataset's built-in code.
So, if you want the user's confirmation, get it before calling Delete, not inside the BeforeDelete event. Btw, the standard TDBNavigator has a ConfirmDelete property associated with its integrated DeleteRecord button which will do exactly that, i.e. pop up a confirmation prompt to the user.
More generally, people frequently create problems for themselves by trying to perform actions within a dataset event which are inappropriate to the state the dataset is in when the event code is called. The TDataSet has a very carefully designed flow of logic to its operations, and t is rather too easy for the inexperienced on unwary programmer to subvert its logic by doing something in a TDataSet event that shouldn't be there: One example is calling Delete inside an event. Another, frequently encountered ,one is executing code which moves the dataset's cursor inside an event that's called as result of moving the cursor, like the AfterScroll event, f.i.
There's no easy rule to say what you should and shouldn't do inside a dataset event, but generally speaking, any action you take which attempts to change the dataset's State property (see TDataSetState in the OLH) should be your prime suspect when you find that something unexpected is happening.
I suppose another general rule, with exceptions that are usually clear from the descriptions of events in the OLH, is that dataset events should generally be used for reacting to the event by doing something to some other object, like updating a status panel, rather than doing something to the dataset. F.i. the AfterScroll event is very useful for reacting to changes when the dataset's cursor is moved. The exceptions are events like BeforePost which are intended to allow you the opportunity to do things (like validate changes to the dataset's fields).
Btw, you can call Abort from inside the BeforeDelete event and it will prevent the deletion. However, imo, it's cleaner and tidier to check whether a deletion should go ahead and plan and code for its consequences before going ahead rather than have to back out part way through. So, with respect, I disagree with the other answer. The time to decide whether to cross a bridge is before you start, not when you're already part way across it. Ymmv, of course.

The question is in the right place. To ask before the delete is wrong because it forces you to ask the question every time you call Delete. A more correct answer is to abort the delete here, in the OnBeforeDelete, if the user doesn't want to delete.

Related

responding to dbCheckbox changes programmatically

I have a no of DBCheckboxes on a form I want to be able to check/uncheck one or more of these programmatically depending upon whether the user checks /unchecks a different DBCheckbox also on the same form I cant seem to be able to do this using the onclick event
I can't seem to be able to do this using the onclick event
The reason is the tight coupling between the state of db-aware components such as TDBCheckBox and that of the dataset they are connected to. If you attempt to interfere with this by trying to set the gui state of the component (e.g. the Checked state of a DBCheckBox), the db-aware model these components all work to will fight you every inch of the way, because you are effectively trying to subvert the mechanism by which the gui state of the component is kept in sync with the value in the corresponding dataset field.
So, as Val Marinov correctly said, the thing you need to do is to manipulate the field value instead, as in the following:
if MyDataSet.FieldByName('OtherBooleanField').AsBoolean <> RequirdValue then begin
if not (MyDataSet.State.State in [dsInsert, dsEdit]) then
MyDataSet.Edit;
MyDataSet.FieldByName('OtherBooleanField').AsBoolean := RequiredValue;
It's up to you what trigger you respond to, to execute code like this.
on clickevent of check box set the Field of appropriate checkbox
In the OnChange event of the fields change the values of the other fields that need to change.

How I can get the changes of a TClientDataset to another TClientDataset?

How can I get from a TClientDataset the changes?
I have a TClientDataset named GetDataset and I have a grid. I want the changes in a new TClientDataset named ChangeDataset.
How can I do this?
If you have a source ClientDataSet CDS1, you should be able to copy the changed records to a second ClientDataSet CDS2 by doing
if CDS1.ChangeCount > 0 then
CDS2.Data := CDS1.Delta;
As you'll see if you try that, it gives you a "before" record and one with the change(s). That may not necessarily be what you want - frankly you'd do best to read the Whipple article posted in a comment and in the OLH to get the exact result you might be wanting to achieve. The point is, all the information you need is in the source CDS until you flush it away (by calling ApplyUpdates() - after that, if it succeeds, the change log is empty).
If you look at the rows in CDS2, it's not immediately obvious how you tell whether a particular field contains a changed value and how you distinguish one that does from one which is just empty. Istr there was a very good post quite a long time ago in one of the Borland NTTP newsgroups by their Mark Edington, I think, explaining how to do this. Basically, it's a question of evaluating VarIsClear on the field's NewValue property:
if VarIsClear(CDS2.Fields[i].NewValue) then
// means Fields[i] does not have a changed value
Incidentally, since you can save the state of a CDS to XML, you can use XML manipulations, e.g. with a DOM parser such is the Windows built-in one (see MSXML.Pas) to do easily many things which are troublesome to do using the TDataSet paradigm.

Delphi - Running code without showing form

What do you think about this programming practice:
- I need to execute one transaction at first form and after that to force some updates that are placed at another form (for each item that is shown at another form). I.e. it would be like show that form and click at some button. Because it is mandatory to execute these functionalities from second form, I thought to do it without showing second form. Is that good programming practice or you have some other recommendation?
Also, is it enough just to set property> Visible:=False before ShowModal for the second form or I need to do some other actions?
Well, it's unusual to have a form that you don't show. Normally you separate your business logic from the UI.
To answer your question, I don't think you need to call ShowModal at all. Just define a method on the form class and call that. Ultimately forms are just Delphi objects and you can use them as such. If you don't want to show them, don't call ShowModal or Show.
Second question first: Setting Visible := False is of no benefit because the point of all ShowXXX methods is to make the form visible. As David says, you could perform the actions without calling Show at all, provided of course your form doesn't rely on any OnActivate or OnShow code in order to do it's job properly.
As for whether this is a good idea, I say no!
As I've already pointed out there is a concern you have to watch out for. I.e. that currently (or even due to maintenance at some point in the future) your form relies on being visible to do its job properly.
Of course, you could work around that by letting the form flicker open, and be programatically closed. Clearly an aesthetically poor choice.
Not to mention the problems of getting it right. You'll end up writing a bunch of patch-work code to wrap the form so that it can do what you need to do, when you should rather do the following...
Correct Approach
Your form is currently doing at least 2 distinct things:
Visual UI control (call it A)
and "mandatory functionalities" (call it B)
It doesn't matter much whether B is doing validation rules, extra processing, or whatever.
B is a process that does not require user interaction.
Therefore, you need to:
Copy B into a non-UI location (either a simple unit with a custom object or a data module). Call it B*
Modify the form to call B* instead of using B.
Test that your form still behaves correctly.
Delete B
And now you can have your new form call B* instead.
The above approach will save you huge headaches in the future.

What is the purpose of the 'Tag' property of Delphi VCL components?

Is there any specific purpose for the 'Tag' property of Delphi VCL components?
I have Googled a few examples using it as, for example, a 'color' property or using the value as a pointer address, but is it 'good practice' to use it, or is it considered 'bad practice' as it ties the program logic to the UI?
The "tag" property is there as a "cargo container" for whatever you might want to do with it.
Something it's often used for is in event handlers when you have a lot of similar components sharing one event handler. The event handler can find its caller and then query its tag value to get some more information about what it's supposed to be acting on.
EDIT:
Example: A calculator app might tag the number buttons with their respective numbers... silly and incomplete example, but you get the idea. The event handler could then pull the number to add into the display and accumulator right out of the tag instead of having to go figure out which button is meant to do what.
It is a place to add a piece of information to any component, even if you don't have the source for that component. It should be used carefully, because you can use it for only one purpose per component. For that reason Libraries should never use it.
I have some fundamental problems with the Tag property.
Well not exactly this property itself because it works as intended.
In general I consider using any universal/general/multi-purpose variables as a 'bad practice'.
They can be useful during debugging but are very harmful in production/mission critical environment.
They reduce code readability and understandability because nobody knows what a 'Tag' named attribute or property does. Of course you know why you are using this variable. But sooner or later you will forget (I know you will) and relying on this value makes anything more complicated.
This is why we should properly name every variable and property to help us understand what the code does.
Using Tag property is just a workaround/shortcut to avoid implementing understandable and well written code.
This is the PRACTICE and it is addictive.
Next time you need to store a new integer value bound to a component you will use the Tag property without considering any other way to store the desired values.
And storing a pointer in Tag property is a horrible idea: you have to cast this value every time you debug pointers.
Tell me: how many times did you find yourself in a situation where you wanted to store a new value in the Tag property but you realized this property is already used for a different purpose (if only there would be a 'Tag2' property in every component...).
As others have said, it's a place to put anything. Typically this comes in handy when associating two objects via an object reference or pointer. The tag happens to be perfectly sized to hold a pointer, so if you need to, say, keep an object tied to an item in a listbox, it becomes pretty straightforward.
Also it can be used for grouping purposes, say you'd want to access all components with a specific tag value, regardless of the component's type.
It's great! A freebie. I use it all the time to store one additional piece of information associated with the object.
Often I store a pointer to an associated data structure, or sometimes an integer which may be an index into some other array.
You can use it as a counter for times the object is accessed, or whatever.
The only downside is if your program uses lots of memory and you have millions of objects, those 4 bytes for each tag add up, especially if you're not using it. In that case, for your most prolific object type, you may want to create your own version without the tag.
You have 2 buttons on your form, on one you set the Tag = 1, and the other one Tag = 2. Now you assign the same OnClick event to both buttons and writhe the code like this:
procedure TForm28.Button1Click(Sender: TObject);
begin
case (Sender as TButton).Tag of
1: Caption := 'you pressed button 1';
2: Caption := 'you pressed button 2';
end;
end;
or more compact:
procedure TForm28.Button1Click(Sender: TObject);
begin
Caption := 'you pressed button ' + IntToStr((Sender as TButton).Tag);
end;
Basically,Tag will let you identify what control fired the event. Think if you have a form with dynamically created buttons... a list with users from the database, and on each record you put a button "Delete User". In this situation you can't create an event for each button, you will create one event that will assigned to all the buttons... and you can put in the Tag the userid for example. That way when you implement the event to handle all the buttons, you'll know what user to delete.
I Use tags all the time.
here are some examples;
a simple sample: you have a notebook (Like a pagecontroll without tabs)
so you can define buttons as tabs and write
NoteBook.ActivePage := TButton(Sender).Tag;
A more complicated sample;
an integer can hold 16 bitwise bolleans;
I can then check the senders up to 16 conditions to decide how to continue the pricedure
If (BitCheck (Bit2,TButton(sender).tag=True) And BitCheck(bit12,TButton(Sender).Tag=False) Then
Begin
end;
If (BitCheck (Bit9,TButton(sender).tag=True) Or BitCheck(bit14,TButton(Sender).Tag=True) Then
Begin
end;
You Get the idea

How long does a TDataset bookmark remain valid?

I have code like below in a project I'm working.
procedure TForm.EditBtnClick(Sender:TObject);
begin
// Mark is form variable. It's private
Mark = cdsMain.GetBookmark;
// blabalbal
.
.
.
end;
procedure TForm.OkBtnClick(Sender:TObject);
var
mistakes: Integer;
begin
//Validation stuff and transaction control
//removed to not clutter the code
If cdsMain.ChangeCount <> 0 then
mistakes := cdsMain.AppyUpdates(-1);
cdsMain.Refresh;
try
cdsMain.GotoBookmark(Mark);
// Yes, I know I would have to call FreeBookmark
// but I'm just reproducing
except
cdsMain.First;
end;
end;
Personally, I do not use bookmarks much — except to reposition a dataset where I only moved the cursor position (to create a listing, fill a string list, etc). If I Refresh, update (especially when a filter can make the record invisible), refetch (Close/Open) or any operation that modifies the data in the dataset, I don't use bookmarks. I prefer to Locate on the primary key (using a TClientDataset, of course) or requery modifying the parameters.
Until when is a bookmark valid? Until a Refresh? Until a Close/Open is done to refetch data? Where does the safe zone end?
Consider in the answer I'm using TClientDataset with a TSQLQuery (DbExpress).
Like both c0rwin and skamradt already mention: the bookmark behaviour depends on the TDataSet descendant you use.
In general, bookmarks become invalid during:
close/open
refresh (on datasets that support it)
data changes (sometimes only deletions)
I know 1. and 2. can invalidate your bookmarks in TClientDataSets. I am almost sure that for TClientDataSets it does not matter which underlying provider is used (TSQLQuery, TIBQuery, etc).
The only way to make sure what works and what not is testing it.
Which means you are totally right in not using them: bookmarks have an intrinsic chance of being unreliable.
To be on the safe side, always call BookmarkValid before going to a bookmark.
Personally I rarely ever use bookmarks. I instead use the id of the record I am viewing and perform a locate on it once the refresh is complete. If I need to iterate over all of the records in the set, I do that using a clone of the tClientDataset (which gets its own cursor).
It is my understanding is that the implementation of the bookmark is up to the vendor of the tDataset descendant and can vary between implementations. In my very simple dataset (tBinData), I implemented bookmarks as the physical record number so it would persist between refreshes as long as the record was not deleted. I can not speak this true for all implementations.
TDataSet implements virtual bookmark methods. While these methods ensure that any dataset object derived from TDataSet returns a value if a bookmark method is called, the return values are merely defaults that do not keep track of the current location. Descendants of TDataSet, such as TBDEDataSet, reimplement the bookmark methods to return meaningful values as described in the following list:
BookmarkValid, for determining if a specified bookmark is in use.
CompareBookmarks, to test two bookmarks to see if they are the same.
GetBookmark, to allocate a bookmark for your current position in the dataset.
GotoBookmark, to return to a bookmark previously created by GetBookmark
FreeBookmark, to free a bookmark previously allocated by GetBookmark.
Get it from here

Resources