Can VirtualStringTree avoid attaching data to a Node? - delphi

A standard VirtualStringTree in the examples I've seen has a GetText procedure to get at data which is stored in memory via InitNode. It uses a set of Nodes to manage display, ordering etc.
An application which collects and manages some data in its own class (TMyDataClass) but uses a VirtualStringTree just to display it could, however, simply access TMyDataClass's data in GetText without bothering to attach that data to a Node in InitNode (and later freeing it in FreeNode).
I'm trying to understand any downside there is to that. What functionality of VirtualStringTree will I miss if I don't use InitNode and FreeNode? I have nothing against those of course but I just want to understand better.

The TVirtualStringTree is used to handle elements related to display wihtout worrying about what the data actually is.
To allow it to do that, each of the Node records can store additional information to get to the data it needs to know. The Data property. In general you don't want to have multiple copies of your data, so in the Data property of the Node you would store a pointer (reference) to your object from which it can get the data. I understand that the Data property is defined as a record - so you need to define a record to hold your data. (Please note I may be referring to a different version than you are using - check your docs or source code to confirm what you need).
So that would mean you have, for example:
CellText := TMyNodeRecord(Node.Data).AppData.DisplayText;
Where TMyNodeRecord is defined as something like:
type TMyNodeRecord = record
AppData: TMyDataClass;
end;
Where TMyDataClass is your own data object that supplies the text through the DisplayText property (or any other property / metod you like).

Related

TDateEdit cannot LiveBinding with a datasource bidirection

This is a simple question.
Start a multidevice App, place a TDateEdit and DBTable with a field containing TdateTime data. Then use LiveBinding designer link the data source field to TDateEdit.DateTime property. However, this link is unidirectional , The control DateEdit can accept the data from the Datasource, but cannot update the changes to the datasource. How to change the link to bidreiction???
There may be some things that are not clearly understood.
Normally a LiveBindings link will be bidirectional - so if you setup two TDateEdits and bind them together changing one should change the other, irrespective of which one you change.
It seems that what you are hoping is that changing the TField will update the data in the database table.
That's not typically how the Data Access components in Delphi work.
The TField is part of a TDataSet. The TDataSet has a Post method to write changes to the underlying data store, if that's supported.
The Data Access components are extremely powerrful and can cope with lots of different scenarios, and allow you to extended capabilities throguh exposed events as well as subclassing the components.
If you want to update your underlying data store you need to have a writeable TDataSet and you need to call Post on it to write.
I suggest you start off with some of the videos in Enbarcadero's YouTube channel about how to use the data acecss components. It's not difficult to do but beyond the scope of an answer here.
If you use the LiveBindings Wizard to hook the DateEdit with a field, it will hook it up bi-diretionally to a "virtual" SelectedDateTime property of the DateEdit. That property doesn't really exist in the TDateEdit but is used by the LiveBindings to map the separate Date and Time properties of the DateEdit.

Delphi - Passing query results between units?

In one unit I'm running a query which will return one users details from the database. Right now I'm thinking of creating a user object and assigning the results of the query to the different properties, the setting that as a global variable. I wanted to know if there was a way to pass the data between the units without having to use the global variables.
Avoiding global variables is actually a good idea. Also, storing the query result as properties of a (database-independent) object makes sense, because the application might need the information also when the connection is not active.
To avoid a global variable, the easiest way would be to make the object a field of a main form (or datamodule), and use Getter methods to make it (and its fields) read-only. I would also implement the procedure of loading the dataset values into the object properties as a spearate class.

How to create a report using fast Reports with out connecting directly to a database

I have been asked by my company to update the reporting functionality of a paticular application written in delphi and using Quick reports to use FastReports instead.
The current implementation pulls all the data out of the database, does a lot of work to organise and calculate the required data for the reports and stores all this in several different objects. The Quick Report OnNeedData events are then used to fill out the bands until there is no more data (signified by setting MoreData = false)
The problem I'm having is Fast Reports seems to need a band to be connected to an actual data source, which I don't have. Also fastReports doesn't seem to have an event similar to OnNeedData.
Is there anyway to fill out the values of a data band in code and have it print again until all data is printed without connecting the band to a dataset?
I appologise for the vagueness of this question, I am very new to reporting software and any suggestions of where to go and what to look at would be greatly appreciated.
Fast reports use a intermediary object descending from _TFrxDataSet to connect the report engine which the data it prints.
To connect a report to a data source managed by the program itself, you use a TfrxUserDataSet component, which let's you see a data set inside the report, but you manually specify the column names in the Fields (TStrings) property and manage and supply values programatically writing event handlers for the following events:
OnCheckEOF is functionally equivalent to the OnNeedData, if there's no more to print, you set the EOF var parameter to true
OnFirst you do whatever you have to do to start walking for the data.
OnGetValue and OnNewGetValue you provide values for each of the different columns of the current row
OnNext, OnPrior you move your current row one next or one prior position.
As you see, the row/column concept (a DataSet) is used to provide the data to the report, but you can pull your data from any structure you use to store the result of your calculations (lists, arrays, or any other object/structure/file etc.)
Inside the report, you link the band to this logical DataSet and you use standard components to print the column values of this DataSet.
If you already have the data in a DataSet, for example a in-memory DataSet after your calculations, better use a TfrxDBDataset to directly bind your report to that source of data.
you can use TfrxUserDataSet.There is a demo named 'printstringlist' under folder 'demos'.
In our project we have implemented our own class inherited from TfrxCustomQuery. This new query class simply redirects its SQL statements to our application' internal query engine. We registered this new class in FastReport palette (used frxDsgnIntf.frxObjects.RegisterObject* for FR version 3 and 4) and now it is used in all our report templates instead of TfrxADOQuery or other built-in dataset classes.
Here is another alternative:
I've been using FastReport for years. I sometimes run into a similar situation. For offline tabular reports I use an in-memory dataset. I purchased DevExpress long time ago and so I have TdxMemData. But even without it, you should be happy using the TClientDataset component.
Besides that, the TfrxUserDataset is an alternative which I use when showing lists of objects.
There is a possibility to do it like this, it is slow though ,
Code:-
var
FRX: TfrxReport;
procedure NewPage;
begin
MyPage := TfrxReportPage.Create(FRX);
MyPage.CreateUniqueName;
MyPage.PaperSize := DMPAPER_A4;
end;
procedure ...(AText: string);
var
frMemo : TfrxMemoView;
begin
frMemo := TfrxMemoView.Create(MyPage);
frMemo.CreateUniqueName;
frMemo.Text := AText;
end;
Regards
Herman

How to create a simple READ-ONLY TDataSet to access a memory structure

I have a memory structure which is (pretty much) static and read-only. I would like to present its data as a TDataSet descendent to enable me to use DB grids etc to view / report the data. I have no need of editing (in fact this must be prohibited). I've see this SO question and other advice that some home-rolled code 'has problems with bookmarks'. I really only want a simple solution and ideally this would be where I could create a simple 'virtual' table with my known field types and then be given an 'OnGetFieldData' event for each one. My other choice is to use a DevExpress TDxMemData in-memory table and populate it on changes in my data but this is less efficient and more messy. Is there any other solution?
Don't use TDataSet descendant, use LiveBindings instead. Your situation is exactly what they were created for. Here are some videos about their use: http://www.embarcadero.com/coderage/sessions

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