Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

reading setrange start and end values

Status
Not open for further replies.

zenpicker

Programmer
Nov 15, 2005
4
US
In a D7 app, I need to derive the current range start and end key values at run time. I.e., there are situations where I pass a client dataset to a function that needs to save the state of the incoming dataset, including current index, filter, and setrange. There seems to be no straightforward property or method to get the range info from the dataset.

Something that seems to work, though it's really a kluge, is to call EditRangeStart/EditRangeEnd and read the values of the index keys into local variables. Normally, of course, you'd ASSIGN these values while in Setkey mode, but it appears you can read them too. The problem here is that I can't figure out how to take the dataset back out of Setkey state once I've read my values. The EditRangeStart/EditRangeEnd design assumes you're going to issue either a Cancelrange or an Applyrange once you've set your keys. Issuing an Applyrange works most of the time (though I suspect I'm incurring needless overhead), but sometimes does fail with a "Number is out of range" exception from the bowels of the VCL.

Either of two options would enable me to solve my problem:

1. A straightforward way of reading the setrange keys without going the EditRangeStart route

2. A way of canceling the Setkey state without Applyrange or Cancelrange, so I can use EditRangeStart safely.

Can anyone enlighten a poor soul?
 
I have used something like the following to close all flashfiler table and reopen them. It compiles with ttTable but no guarantees. ;)

var ClosedTableList = Tlist = nil;

procedure CloseAllAppTables(AppIsClosing: boolean);
var
aForm: TForm;
iTableCounter: integer;
i,j: integer;
begin
if not AppIsClosing then
begin
ClosedTableList.Clear;

// Get count of all open tables (TTable) instantiated in application
iTableCounter := 0;

for i := 0 to Screen.FormCount - 1 do
begin
aForm := Screen.Forms;
for j := 0 to aForm.ComponentCount - 1 do
if (aForm.Components[j] is TTable) and TTable(aForm.Components[j]).Active then
Inc(iTableCounter);
end;

// dimension arrays of table ranges (arrays are zero-based)
VarArrayRedim(SavedRangeStartValues, iTableCounter - 1);
VarArrayRedim(SavedRangeEndValues, iTableCounter - 1);
end;

// Now store the pointers and close all datasets instantiated in application

iTableCounter := 0;

for i := 0 to Screen.FormCount - 1 do
begin
aForm := Screen.Forms;
for j := 0 to aForm.ComponentCount - 1 do
begin
if ( (aForm.Components[j] is TTable) and TTable(aForm.Components[j]).Active )then
begin
if not AppIsClosing then
ClosedTableList.Add(aForm.Components[j]);

if aForm.Components[j] is TTable then
begin
if not AppIsClosing then
begin
AddSavedRangeValues(TTable(aForm.Components[j]), iTableCounter);
Inc(iTableCounter);
end;

TTable(aForm.Components[j]).Close;
end;
end;
end;

procedure ReopenAllAppTables;
var
iTableCounter: integer;
i: integer;
begin
if ClosedTableList.Count > 0 then
begin
iTableCounter := 0;
// open all tables
for i := 0 to ClosedTableList.Count - 1 do
begin
if TObject(ClosedTableList) is TTable then
begin
TTable(ClosedTableList).Open;
RestoreSavedRangeValues(TTable(ClosedTableList), iTableCounter);
Inc(iTableCounter);
end;
end;
ClosedTableList.Clear;
end;
end;

var
ClosedTableList: TList = nil;
SavedRangeStartValues: variant;
SavedRangeEndValues: variant;

procedure AddSavedRangeValues(const aTable: TTable; const VariantOffset: integer);
var
IndexFieldsCount: integer;
StartValues, EndValues: variant;
i: integer;
begin
IndexFieldsCount := aTable.IndexFieldCount;

if IndexFieldsCount = 0 then
EXIT;

StartValues := VarArrayCreate([0,IndexFieldsCount-1], varVariant);
EndValues := VarArrayCreate([0,IndexFieldsCount-1], varVariant);

aTable.EditRangeStart;
for i := 0 to IndexFieldsCount - 1 do
StartValues := aTable.IndexFields.Value;

aTable.EditRangeEnd;
for i := 0 to IndexFieldsCount - 1 do
EndValues := aTable.IndexFields.Value;

aTable.CancelRange;

SavedRangeStartValues[VariantOffset] := StartValues;
SavedRangeEndValues[VariantOffset] := EndValues;
end;

procedure RestoreSavedRangeValues(const aTable: TTable; const VariantOffset: integer);
var
IndexFieldsCount: integer;
StartValues, EndValues: variant;
RangeAssigned: boolean;
i: integer;
begin
IndexFieldsCount := aTable.IndexFieldCount;

if IndexFieldsCount = 0 then
EXIT;

StartValues := SavedRangeStartValues[VariantOffset];
EndValues := SavedRangeEndValues[VariantOffset];

RangeAssigned := False;
aTable.CancelRange;
aTable.SetRangeStart;

for i := 0 to IndexFieldsCount - 1 do
begin
if not VarIsNull(StartValues) then
begin
aTable.IndexFields.Value := StartValues;
RangeAssigned := True;
end;
end;

aTable.SetRangeEnd;

for i := 0 to IndexFieldsCount - 1 do
begin
if not VarIsNull(EndValues) then
begin
aTable.IndexFields.Value := EndValues;
RangeAssigned := True;
end
else
begin
if aTable.IndexFields is TIntegerField then
TIntegerField(aTable.IndexFields).AsInteger := High(integer)
else if aTable.IndexFields is TStringField then
TStringField(aTable.IndexFields).AsString := #255
else if aTable.IndexFields is TDateField then
TDateField(aTable.IndexFields).AsDateTime := Date + 100000
else if aTable.IndexFields is TDateTimeField then
TDateTimeField(aTable.IndexFields).AsDateTime := Date + 100000
else
raise Exception.Create('Index field is with unhandled TField type for setting range');
end;
end;
if RangeAssigned then
aTable.ApplyRange
else
aTable.CancelRange;
end;

initialization
ClosedTableList := TList.Create;
SavedRangeStartValues := VarArrayCreate([0, 0], varVariant);
SavedRangeEndValues := VarArrayCreate([0, 0], varVariant);
finalization
if ClosedTableList <> nil then
ClosedTableList.Free;

Let me know how it works.

Markus Rissmann
 
Markus - since posting my query I found a way to do this and it's quite similar to your implementation. My challenge is that I wanted to read the range keys without disturbing (cancelling) the range and reapplying it. I haven't really solved this, though the code below does work as a way of getting the keys. Can't see how to do it without Delphi exposing the key buffer, so this is where I wound up:

procedure TScreening.GetSegmentCacheRangeInfo( SegmentCache: TClientDataset;
var RangeStartKeys: string; var RangeEndKeys: string);

var
i,nRecs: integer;

begin

// Note: SegmentCache is a client dataset
// All range keys are known to be of type String
// The referenced strtran function just replaces the target delimiter character #7 with ''

RangeStartKeys :='';
RangeEndKeys :='';

// Derive setrange keys from current state of segmentcache and
// construct a #7-delimited list of key values

if SegmentCache.active = false then exit;

try
nRecs :=SegmentCache.recordcount;

SegmentCache.EditRangeStart;
for i:=0 to SegmentCache.indexfieldcount - 1 do
begin
RangeStartKeys :=RangeStartKeys +
SegmentCache.indexfields.asstring+#7;
end;

SegmentCache.EditRangeEnd;
for i:=0 to SegmentCache.indexfieldcount - 1 do
begin
RangeEndKeys :=RangeEndKeys +
SegmentCache.indexfields.asstring+#7;

end;

finally
try
if strtran(RangeStartKeys,#7,'')+strtran(RangeEndKeys,#7,'') = '' then
SegmentCache.CancelRange // if you don't do this when keys are empty you get a VCL exception
else
SegmentCache.ApplyRange;

except
on e:exception do
begin
showmessage(e.message+' Incoming rec count: '+inttostr(nRecs)+
' Current: '+inttostr(SegmentCache.recordcount)+CRLF+
' Range start keys returned: '+RangeStartKeys+CRLF+
' Range end keys returned: '+RangeEndKeys,3);

end;
end;
end;
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top