Create TVirtualTable via Code only without drop component - delphi

Recently i want to export data using TscExcelExport.
If i'm using TVirtualTable using Design Component, it works perfectly.
EmpVT.First;
EmpVT.Filtered := False;
while not EmpVT.Eof do
begin
//Salary Virtual Table
VirtualTableBankTransferListExport.Append;
VirtualTableBankTransferListExport.FieldByName('EMPID').AsInteger := EmpVT.FieldByName('EMPID').AsInteger;
VirtualTableBankTransferListExport.FieldByName('EMPBANKGROUPNO').AsInteger := EmpVT.FieldByName('BANKGROUPNO').AsInteger;
VirtualTableBankTransferListExport.FieldByName('EMPTRANSBANKACCNAME').AsString := EmpVT.FieldByName('EMPBANKACCNAME').AsString;
VirtualTableBankTransferListExport.FieldByName('EMPTRANSBANKACCNO').AsString := EmpVT.FieldByName('EMPBANKACCNO').AsString;
VirtualTableBankTransferListExport.FieldByName('BANKACCNO').AsString := EmpVT.FieldByName('BANKACCNO').AsString;
VirtualTableBankTransferListExport.FieldByName('PERIOD').AsString := PaySlipPeriod;
VirtualTableBankTransferListExport.FieldByName('BANKNAME').AsString := EmpVT.FieldByName('BANKNAME').AsString;
VirtualTableBankTransferList.FieldByName('TRANSFERAMOUNT').AsInteger := 0;//Format('%s%s%.*d', [EmpVT.FieldByName('REGIONCODE').AsString,EmpVT.FieldByName('EMPCODE').AsString,4,EmpVT.FieldByName('EMPCODE').AsInteger]); //EmpVT.FieldByName('EMPCODE').AsString;
VirtualTableBankTransferListExport.FieldByName('BANKID').AsString := EmpVT.FieldByName('BANKID').AsString;
VirtualTableBankTransferListExport.FieldByName('TBANKNAME').AsString := EmpVT.FieldByName('TBANKNAME').AsString;
VirtualTableBankTransferListExport.FieldByName('BANKLOCATION').AsString := EmpVT.FieldByName('BANKLOCATION').AsString;
VirtualTableBankTransferListExport.Post;
EmpVT.Next;
end;
scExcelExport1.WorksheetName := 'Bank Transfer List';
scExcelExport1.Dataset:=VirtualTableBankTransferListExport;
scExcelExport1.ExportDataset;
scExcelExport1.Disconnect;
What is the correct way to create TVirtualTable via Code only without dropping TVirtualTable in form?
Thank you for your help.

Install CnWizards (more features but less stable) or GExperts IDE (more polished but less features) add-on
They both have "Component-To-Code" wizard.
So you would select your component, run this command from the menu and would get the pure Pascal sources of creating it.
PS. Actually there might be several components, not a single one: fields are usually components of their own.

Related

Datasnap\FireDAC: Query executed twice

I have the following problem:
1) I use Delphi XE7 to develop a 3-layer system.
2) The server layer, created with datasnap using REST.
3) I use Firebird as database and the access is performed with FireDAC.
4) I have a sequence with value 01.
5) I created the following query in the server layer:
Select GEN_ID (gen_my_sequence, 1) from rdb $ database
6) On the server returns the sequence value in the query is: 02.
7) But the client layer returns 03.
I do not understand why the query is executed twice.
Can anyone help me?
This is the nature of generators (sequences) in firebird. Their value is increased every time you request it and the value of the generator is updated from that request and remains updated. Also generators live outside of transaction control.
See this firebirdsql generatorguide-basics. It doesn't matter where you request it from.
I use technical standards that the Embarcadero indicates.
What I realized was this:
1) The unit Data.FireDACJSONReflect in TFDJSONInterceptor.ItemListToJSONObject routine has this block of code:
if not LActive then
LDataSet.Active := True;
try
LJSONDataSet := DataSetToJSONValue(LDataSet);
// Use AddPair overload that will accept blank key
AJSONObject.AddPair(TJSONPair.Create(LPair.Key, LJSONDataSet))
finally
if not LActive then
LDataSet.Active := False;
end;
See he activates the query once, causing the sequence to be incremented.
But in DataSetToJSONValue (LDataSet) routine; This code block is:
if (LMemTable = nil) then
begin
LMemTable := TFDMemTable.Create(nil);
LAdapter := TFDTableAdapter.Create(nil);
LMemTable.Adapter := LAdapter;
LAdapter.SelectCommand := ADataSet.Command;
LMemTable.Active := True;
end;
See he again activates the query, where the sequence is again incremented.
Now I do not know if I made a mistake or if it is a bug, but I created a new class inherited from TFDMemTable and thought there was some mistake in this class, but did a test with TFDMemTable component, standard component of FireDAC, and even then the activation of any query is performed twice, because the code does not consider any of these two classes, as a TFDCustomMemTable, even though they were inherited directly from this class.
I commented the code of DataSetToString routine (const ADataSet: TFDAdaptedDataSet) that looked like this:
LMemTable := nil;
LAdapter := nil;
try
//if (ADataSet is TFDCustomMemTable) then
LMemTable := TFDCustomMemTable(ADataSet);
{if (LMemTable = nil) then
begin
LMemTable := TFDMemTable.Create(nil);
LAdapter := TFDTableAdapter.Create(nil);
LMemTable.Adapter := LAdapter;
LAdapter.SelectCommand := ADataSet.Command;
LMemTable.Active := True;
end;}
In this way the problem was solved, and the performance of the application seemed to have improved.

Update existing Google calendar entry using TMS Cloud component

I use the following code to add a new entry to my Google calendar using TMS Cloud component
I have the new entries in a table in a database and they are added OK
But I would like to be able to update an entry also, but that I cant find any help about.
The demo that is supplied with the controls has the option, but I don't want to load all entries from the calendar and select the one to edit there. I want to save some sort of entryID that it is given and use that to update.
DevExpress has an option that lets you sync a table to and from Outlook calendar (works very nice) so I think it should be possible to do with TMS
The question is: how do I get an ID of a calendar entry when creating it so I can store it with the record in table and use it afterwards for updating?
var
ci: TGCalendarItem;
begin
AdvGCalendar.App.Key := Settings.Google.Key;
AdvGCalendar.App.Secret := Settings.Google.Secret;
AdvGCalendar.Logging := true;
if not AdvGCalendar.TestTokens then
AdvGCalendar.RefreshAccess;
if not AdvGCalendar.TestTokens then
AdvGCalendar.DoAuth
else
Connected := True;
ci := AdvGCalendar.Items.Add;
AdvGCalendar.GetCalendars();
ci.CalendarID := Settings.Google.Calendar;
if ci.CalendarID <> '' then
begin
ci.Location := CiLocation;
ci.Description := CiDescription;
ci.Summary := CiSummary;
ci.StartTime := EncodeDateTime(YearOf(StartDate), MonthOf(StartDate), DayOf(StartDate), HourOf(StartTime), MinuteOf(StartTime), 0, 0);
ci.EndTime := EncodeDateTime(YearOf(StopDate), MonthOf(StopDate), DayOf(StopDate), HourOf(StopTime), MinuteOf(StopTime), 0, 0);
ci.IsAllDay := False;
ci.Visibility := viPrivate;
AdvGCalendar.Add(ci);
end;
end;
The problem was related to the fact that there was an error in the version I was using of the component so that the ID was not returned OK.
Upgrading to a newer version fixed this so now it is working.

Launching Word x64 in Delphi

I am currently working on very old project, which is based on Delphi 5. When I am trying to open new Word document on x64 system, I recive this error:
According to MSDN, the source of problem might be the version of my Office (x64). In application we use TWordApplication object to manage Word invoking. Is there any solution for that problem? On Office x86 everything works fine.
This is part of the code:
Word := TWordApplication.Create(nil);
Word.ConnectKind := ckNewInstance;
Word.AutoQuit := True;
Word.Connect;
Word.Options.CheckSpellingAsYouType := False;
Word.Options.CheckGrammarAsYouType := False;
Word.Options.SuggestSpellingCorrections := False;
Word.Options.CheckGrammarWithSpelling := False;
Word.Options.ShowReadabilityStatistics := False;
Word.Options.IgnoreInternetAndFileAddresses := False;
Word.Options.IgnoreUppercase := True;
Word.Options.IgnoreMixedDigits := True;
Word.Options.ReplaceSelection := True;
The problem appears to be in the VBA code attached to your document rather than the Delphi code. Specifically a hidden module named API is implicated. Perhaps this is in a template or an add-in or an auto start.
Hidden modules are protected which suggests that you may not be able to modify the module. You could try contacting the author of the protected module or perhaps removing whatever it is that introduced this module.
Reverting to 32 bit Office is an easier solution. This will require you to uninstall 64 bit Office first since the two editions do not co-exist.

is there a virtual Listbox with headers in Delphi XE?

Sorry, background's a little convoluted on this one... I am in the process of converting a D5 project to DXE... It has a listbox with several thousand items. A full progressive text search is done on these items with each keystroke in the searchbox. In D5 (pre-virtual lists), I had to make my own virtual listbox using the LMD listbox (as there were several columns with headers in the listbox), a separate scrollbar and an Array of records. The Listbox would then be populated as the user navigated through the search results or by modifying the search. This performed very well but since now virtual listboxes are native to Delphi I was going to convert my custom listbox to the native one but I cannot find a listbox component with headers that is virtual-capable. Help?
Is there a component available that has virtual lists and headers/columns?
I forgot to mention: I am aware of Soft Gems VirtualTreeView components - these are excellent and is probably what I'll be using but... Is there a way in DXE to accomplish this without 3rd party utilities? I'm concerned that I'm missing something obvious in DXE as I've only been using it for about a month.
TListView is a thin wrapper around the Windows list view common control. Run it in virtual mode with report view style to achieve what I believe you are asking for.
In order to set up a virtual list view you need to set OwnerData to True and supply an OnData event handler.
procedure TVirtualListViewForm.FormCreate(Sender: TObject);
begin
ListView1.ViewStyle := vsReport;
ListView1.Columns.Add.Caption := 'Column1';
ListView1.Columns.Add.Caption := 'Column2';
ListView1.OwnerData := True;
ListView1.OnData := ListViewData;
ListView1.Items.Count := 42;
end;
procedure TVirtualListViewForm.ListViewData(Sender: TObject; Item: TListItem);
begin
Item.Caption := Format('Column 0, index %d', [Item.Index]);
Item.SubItems.Add(Format('Column 1, index %d', [Item.Index]));
end;
For your needs an OnDataFind may be needed to implement the progressive text search.
You can use my component TDzListHeader, available at GitHub: https://github.com/digao-dalpiaz/DzListHeader
This component allows you to create columns in a TCollection and you should drop a TListBox inside the TListHeader, and link them.
DzListHeader example
All usage and detailed documentation are available at github project.

Determine when an Excel workbook has closed with Delphi

The following code opens the document specified by the 'app' parameter and then waits until the particular document has been closed. This works fine for all document types, except when you have an Excel workbook open and open another Excel workbook. The code thinks the document has closed when it is actually still open. How would I solve this?
procedure RunAppAndWAit( a: TApplication; app, par, verb: string);
var
seinfo: tshellexecuteinfo;
exitcode: dword;
begin
fillchar( seinfo, sizeof( seinfo), 0);
seinfo.cbsize := sizeof( tshellexecuteinfo);
with seinfo do
begin
fmask := see_mask_nocloseprocess;
wnd := a.Handle;
lpfile := pchar( app);
lpDirectory := pchar( ExtractFileDir( app));
lpParameters := pchar( par);
lpVerb := pchar( verb);
nshow := sw_shownormal;
end;
if ShellExecuteEx( #seinfo) then
begin
repeat
a.ProcessMessages;
GetExitCodeProcess( seinfo.hprocess, exitcode);
until ( exitcode <> still_active) or a.terminated;
end
else
sshowmessage( 'Unable to open ' + app);
end;
Your attempt only works for applications that open the document in the same process which launches the document.
A lot of applications don't work this way any more: the process launching the document will pass the document to another process that shows/edits it, and the launching process dies.
You will need to find an API that supports event callbacks (in this case for Excel, most likely the COM API that Excel exposes) that lets you watch more closely what Excel actually does with your document.
Open your document using this API, register an event that gets called when the document is closed, wait for the event, then close.
This isn't pretty and may not be as reliable as you wish, but you could loop (or better, use a timer event?) calling the Windows EnumWindows function looking for title bars that match what you'd expect Excel to show for this file. (Obviously, this is an Excel-specific solution.)
For example, look for a title bar that contains the word "Excel" and your file name, which is what Excel shows in the title bar.
There may be holes in this approach that make it fragile. In fact, I'm a bit hesitant to post this since I don't think the solution is particularly robust. However, if you have no other way to solve your problem, this might work...
Google "EnumWindows Delphi" for sample code.
... on further thought,below is another way. As Jeroen noted, you could use an API to Excel. If you're doing a lot of these calls, then put the CreateOLEObject and unAssigned assignment outside the function might make it less heavy. (And you'll need some try...except blocks in case Excel is no longer running, etc.) This solution, too, is Excel-specific and clumsy, IMO. I don't know if there might be circumstances (like a File, Dialog box open in Excel?) that would cause this to return erroneous result.
So, basically, I'm saying, here are two relatively weak approaches that are specific to Excel and may not always work. (When I say it that way, I'd almost rather just delete this entire post... But, maybe it'll give you some ideas on how you want to proceed.)
This code is not tested, but similar code has worked for me in the past:
uses ComObj;
function FindWorkbook( Workbookname: String):boolean;
var
ExcelOLE: Variant;
WorkbookNumber: Integer;
begin
Result := FALSE;
ExcelOLE := CreateOLEObject('Excel.Application');
try
for WorkbookNumber := 1 to ExcelOLE.Workbooks.Count do
if UpperCase(WorkbookName) = UpperCase(ExcelOLE.Workbooks[WorkbookNumber].Name) then
Result := TRUE;
finally
ExcelOLE := unAssigned;
end;
end;

Resources