Stringgrid in delphi - delphi

I am trying to write code to calculate sum of all numbers from cells that form main diagonal of stringgrid but the result I get is just number I typed in bottom right cell. Here's my code:
procedure TForm1.Button1Click(Sender: TObject);
var
i, j: Integer;
begin
with StringGrid1 do
begin
RowCount := StrToInt(Edit1.Text);
ColCount := StrtoInt(Edit2.Text);
for i := 0 to RowCount - 1 do
Cells[0, i] := IntToStr(i);
for j := 0 to ColCount - 1 do
Cells[j, 0] := IntToStr(j);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
i, j, s, z, n: Integer;
begin
z := 0;
with StringGrid1 do
begin
for i := 1 to RowCount - 1 do
for j := 1 to ColCount - 1 do
s := StrToInt(Cells[j, i]);
for n := RowCount - 1 to ColCount - 1 do
if i = j then
z := z + s;
Label1.Caption := IntToStr(z);
end;
end;
end.
What am I missing ? Thanks in advance

Your are not summing up any values. If your columcount is equal to your rowcount, then your for loop will only run once, for your last item.
procedure TForm1.Button2Click(Sender: TObject);
var
i, j, s, z, n: integer;
begin
z:=0;
for i:=1 to StringGrid1.rowcount-1 do
for j:=1 to StringGrid1.colcount-1 do
if i=j then
begin
s:=strtoint(cells[j, i]);
z:=z+s;
end;
Label1.Caption:=inttostr(z);
end;

Related

Delphi changing Chars in string - missunderstood behavior - XE3

I'm doing a code on some kind of encoder/decoder, and can't figure out the strange behavior for more than 2 days now... I hope someone might understand and explain to me why is happening, whatever it is...
Here's the main code, which does the thing (I removed the form's info, buttons etc, just the core to avoid garbage)
unit Encoder;
//
interface
//
var
Enc : array [1..71] of Record
Char: Char;
Encr: string;
Enc: array [1..5] of Char;
end;
EncodeBuffer: TStringList;
implementation
{$R *.dfm}
procedure TEncrypter.Encode;
var
s, t, u, h, h2, h3, h4, h5: integer;
begin
s := EncodeBuffer.Count;
h := 0;
h2 := 1;
h3 := 2;
h4 := 3;
h5 := 4;
while h < s do
begin
t := EncodeBuffer.Strings[h].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h] := EncodeBuffer.Strings[h].Replace(EncodeBuffer.Strings[h].Chars[u], EncodeChar(EncodeBuffer.Strings[h].Chars[u], 1));
end;
end;
h := h + 5;
end;
while h2 < s do
begin
t := EncodeBuffer.Strings[h2].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h2] := EncodeBuffer.Strings[h2].Replace(EncodeBuffer.Strings[h2].Chars[u], EncodeChar(EncodeBuffer.Strings[h2].Chars[u], 2));
end;
end;
h2 := h2 + 5;
end;
while h3 < s do
begin
t := EncodeBuffer.Strings[h3].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h3] := EncodeBuffer.Strings[h3].Replace(EncodeBuffer.Strings[h3].Chars[u], EncodeChar(EncodeBuffer.Strings[h3].Chars[u], 3));
end;
end;
h3 := h3 + 5;
end;
while h4 < s do
begin
t := EncodeBuffer.Strings[h4].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h4] := EncodeBuffer.Strings[h4].Replace(EncodeBuffer.Strings[h4].Chars[u], EncodeChar(EncodeBuffer.Strings[h4].Chars[u], 4));
end;
end;
h4 := h4 + 5;
end;
while h5 < s do
begin
t := EncodeBuffer.Strings[h5].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h5] := EncodeBuffer.Strings[h5].Replace(EncodeBuffer.Strings[h5].Chars[u], EncodeChar(EncodeBuffer.Strings[h5].Chars[u], 5));
end;
end;
h5 := h5 + 5;
end;
end;
procedure TEncrypter.Decode;
var
s, t, u, h, h2, h3, h4, h5: integer;
begin
s := EncodeBuffer.Count;
h := 0;
h2 := 1;
h3 := 2;
h4 := 3;
h5 := 4;
while h < s do
begin
t := EncodeBuffer.Strings[h].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h] := EncodeBuffer.Strings[h].Replace(EncodeBuffer.Strings[h].Chars[u], DecodeChar(EncodeBuffer.Strings[h].Chars[u], 1));
end;
end;
h := h + 5;
end;
while h2 < s do
begin
t := EncodeBuffer.Strings[h2].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h2] := EncodeBuffer.Strings[h2].Replace(EncodeBuffer.Strings[h2].Chars[u], DecodeChar(EncodeBuffer.Strings[h2].Chars[u], 2));
end;
end;
h2 := h2 + 5;
end;
while h3 < s do
begin
t := EncodeBuffer.Strings[h3].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h3] := EncodeBuffer.Strings[h3].Replace(EncodeBuffer.Strings[h3].Chars[u], DecodeChar(EncodeBuffer.Strings[h3].Chars[u], 3));
end;
end;
h3 := h3 + 5;
end;
while h4 < s do
begin
t := EncodeBuffer.Strings[h4].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h4] := EncodeBuffer.Strings[h4].Replace(EncodeBuffer.Strings[h4].Chars[u], DecodeChar(EncodeBuffer.Strings[h4].Chars[u], 4));
end;
end;
h4 := h4 + 5;
end;
while h5 < s do
begin
t := EncodeBuffer.Strings[h5].Length;
if t > 0 then
begin
for u := 0 to t-1 do
begin
EncodeBuffer.Strings[h5] := EncodeBuffer.Strings[h5].Replace(EncodeBuffer.Strings[h5].Chars[u], DecodeChar(EncodeBuffer.Strings[h5].Chars[u], 5));
end;
end;
h5 := h5 + 5;
end;
end;
function TEncrypter.EncodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
for t := 1 to 71 do
begin
if Sign = enc[t].Char then
begin
Result := enc[t].Enc[Encoder];
Break;
end
else
Result := Sign;
end;
end;
function TEncrypter.DecodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
for t := 1 to 71 do
begin
if Sign = enc[t].Enc[Encoder] then
begin
Result := enc[t].Char;
Break;
end
else
Result := Sign;
end;
end;
I have an Enc array created on FormCreate event, which fills Enc[1 to 71].Char and Enc[1 to 71].Enc[1 to 5] with random char order;
The code is made (or at least should be) so that it uses different encode list from array for each 5th line (line 1 enc[x].enc[1], line 2 enc[x].enc[2], and then line 5 enc[x].enc[5], and line 6 back to enc[x].enc[1] and so on...)
If I encode 5 lines from Memo1, which are:
Memo1
Memo2
Memo3
Memo4
Memo5
I get some random words with 5 chars each, however when decoding it back, I get returned
Memo1
Memo2
memo3
Memo4
Memo5
(notice the lower m letter in 3rd line);
If I then encode this again, I get the exact same encoded stringlist as in the first case, only that here, the 3rd line's 3rd char! (wtf?) is changed with the same as the firstone is.
so, for Memo3 I get q7M0e, and for memo3 I get q7q0e, which makes no sense to me, as the position of a char should be the same, as far as I understand by the code.
Is there anything I'm missing here, noticable in the code above??
Comment if there's a need for me to paste the complete form's (unit's) code and exe example, I'll give it on a web and link to that...
Edit:
Here's the "key", by which I'm encoding/decoding: http://txt.do/128b
There are several problems with your approach.
One problem is with your use of String.Replace(), which replaces ALL occurrences of one Char with another Char. Once you replace a given Char, you can potentially replace that same index with a different value later on in your loops, thus trashing your data while you are looping.
Another problem is your decoding logic. You are allowing each un-encoded Char to be encoded with one of 5 different Chars. If those encoded Char values are duplicated at all across your Enc[1..71].Enc array for the same value of Encoder, you will not be able to know which Enc[1..71].Char to use for decoding. It is not enough that your arrays are simply random, but they also need to be unique for the same value of Encoder.
Also, your loops are redundant and overly complicated. They can be greatly simplified.
Try something more like this instead:
function TEncrypter.EncodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
for t := 1 to 71 do
begin
if Sign = Enc[t].Char then
begin
Result := Enc[t].Enc[Encoder];
Exit;
end;
end;
Result := Sign;
end;
function TEncrypter.DecodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
for t := 1 to 71 do
begin
if Sign = Enc[t].Enc[Encoder] then
begin
Result := Enc[t].Char;
Exit;
end;
end;
Result := Sign;
end;
procedure TEncrypter.Encode;
var
t, u, h: integer;
s: String;
begin
for h := 0 to EncodeBuffer.Count-1 do
begin
s := EncodeBuffer.Strings[h];
t := Length(s);
if t > 0 then
begin
for u := 0 to t-1 do
begin
s[u+1] := EncodeChar(s[u+1], (h mod 5) + 1);
end;
EncodeBuffer.Strings[h] := s;
end;
end;
end;
procedure TEncrypter.Decode;
var
t, u, h: integer;
s: String;
begin
for h := 0 to EncodeBuffer.Count-1 do
begin
s := EncodeBuffer.Strings[h];
t := Length(s);
if t > 0 then
begin
for u := 0 to t-1 do
begin
s[u+1] := DecodeChar(s[u+1], (h mod 5) + 1);
end;
EncodeBuffer.Strings[h] := s;
end;
end;
end;
// FormCreate
var
I, J, K, L: Integer;
Temp: Array[1..71] of Char;
NumInTemp: Integer;
begin
...
// initialize Enc[].Char as needed...
for I := 1 to 71 do
begin
Enc[I].Char := ...;
end;
// uniquely initialize each Enc[].Enc array for one value of Encoder...
for I := 1 to 5 do
begin
for J := 1 to 71 do
Temp[J] := ...; // must be unique for this iteration of I...
NumInTemp := 71;
// randomly assign Temp array to Enc[I].Enc array
for J := 1 to 71 do
begin
K := 1 + Random(NumInTemp);
Enc[J].Enc[I] := Temp[K];
for L := K+1 to NumInTemp do
Temp[L-1] := Temp[L];
Dec(NumInTemp);
end;
end;
...
end;
If you then expand your arrays to allow all printable ASCII characters, not just 71 of them, then the code gets a little simpler:
var
Enc : array [32..126] of Record
Char: Char;
Encr: string;
Enc: array [1..5] of Char;
end;
EncodeBuffer: TStringList;
function TEncrypter.EncodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
if (Sign >= #32) and (Sign <= #126) then
Result := Enc[Ord(Sign)].Enc[Encoder]
else
Result := Sign;
end;
function TEncrypter.DecodeChar(Sign: Char; Encoder: integer) : Char;
var
t: integer;
begin
for t := Low(Enc) to High(Enc) do
begin
if Sign = Enc[t].Enc[Encoder] then
begin
Result := Enc[t].Char;
Exit;
end;
end;
Result := Sign;
end;
procedure TEncrypter.Encode;
var
u, h: integer;
s: String;
begin
for h := 0 to EncodeBuffer.Count-1 do
begin
s := EncodeBuffer.Strings[h];
for u := Low(s) to High(s) do
s[u] := EncodeChar(s[u], (h mod 5) + 1);
EncodeBuffer.Strings[h] := s;
end;
end;
procedure TEncrypter.Decode;
var
u, h: integer;
s: String;
begin
for h := 0 to EncodeBuffer.Count-1 do
begin
s := EncodeBuffer.Strings[h];
for u := Low(s) to High(s) do
s[u] := DecodeChar(s[u], (h mod 5) + 1);
EncodeBuffer.Strings[h] := s;
end;
end;
// FormCreate
var
I, J, K, L: Integer;
Temp: Array[32..126] of Char;
NumInTemp: Integer;
begin
...
for I := Low(Enc) to High(Enc) do
Enc[I].Char := Char(I);
for I := 1 to 5 do
begin
for J := Low(Temp) to High(Temp) do
Temp[J] := Char(J);
NumInTemp := Length(Temp);
for J := Low(Enc) to High(Enc) do
begin
K := Low(Temp) + Random(NumInTemp);
Enc[J].Enc[I] := Temp[K];
for L := K+1 to (Low(Temp)+NumInTemp) do
Temp[L-1] := Temp[L];
Dec(NumInTemp);
end;
end;
end;
And if you set up a separate decoder table instead of using Enc[].Enc, you can simplify TEncrypter.DecodeChar() to a similar lookup that TEncrypter.EncodeChar() uses, without having to use a loop at all. I will leave that as an exercise for you.
Let's say I start with a string "abc".
I want to replace all "a" with "c", replace all "b" with "Q", and replace all "c" with "7".
Perhaps I would write:
S := 'abc';
S := S . Replace ( 'a', 'c' );
S := S . Replace ( 'b', 'Q' );
S := S . Replace ( 'c', '7' );
The result is '7Q7'. Oops! Why wasn't "a" replaced with "c"?
Well, it was.
After the first call to Replace, S was 'cbc'.
After the second call, S was 'cQc'.
The last call replaced both 'c's with '7'.
I imagine you are doing the same sort of thing here.
I couldn't give you the specific results of your code character for character unless we saw how you were populating the Enc structure.

Adjust Column width DBGrid

I have a TDBGrid. It works, but the columns shown are very large.
How can I set an "auto-fix column width"?
The needed Columnwidth is depended of the settings of the Grids canvas and the mamimum length of the displaytext of each field.
procedure FitGrid(Grid: TDBGrid);
const
C_Add=3;
var
ds: TDataSet;
bm: TBookmark;
i: Integer;
w: Integer;
a: Array of Integer;
begin
ds := Grid.DataSource.DataSet;
if Assigned(ds) then
begin
ds.DisableControls;
bm := ds.GetBookmark;
try
ds.First;
SetLength(a, Grid.Columns.Count);
while not ds.Eof do
begin
for I := 0 to Grid.Columns.Count - 1 do
begin
if Assigned(Grid.Columns[i].Field) then
begin
w := Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName).DisplayText);
if a[i] < w then
a[i] := w ;
end;
end;
ds.Next;
end;
for I := 0 to Grid.Columns.Count - 1 do
Grid.Columns[i].Width := a[i] + C_Add;
ds.GotoBookmark(bm);
finally
ds.FreeBookmark(bm);
ds.EnableControls;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FitGrid(DBgrid1)
end;
Minor modification of bummi's answer to insure that Title Row (Row 0) is not truncated, and excess space will be allocated on each column
procedure FitGrid(Grid: TDBGrid);
const
C_Add = 3;
var
ds: TDataSet;
bm: TBookmark;
i: Integer;
w: Integer;
a: array of Integer;
begin
ds := Grid.DataSource.DataSet;
if not Assigned(ds) then
exit;
if Grid.Columns.Count = 0 then
exit;
ds.DisableControls;
bm := ds.GetBookmark;
try
ds.First;
SetLength(a, Grid.Columns.Count);
for i := 0 to Grid.Columns.Count - 1 do
if Assigned(Grid.Columns[i].Field) then
a[i] := Grid.Canvas.TextWidth(Grid.Columns[i].FieldName);
while not ds.Eof do
begin
for i := 0 to Grid.Columns.Count - 1 do
begin
if not Assigned(Grid.Columns[i].Field) then
continue;
w := Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName).DisplayText);
if a[i] < w then
a[i] := w;
end;
ds.Next;
end;
w := 0;
for i := 0 to Grid.Columns.Count - 1 do
begin
Grid.Columns[i].Width := a[i] + C_Add;
inc(w, a[i] + C_Add);
end;
w := (Grid.ClientWidth - w - 20) div (Grid.Columns.Count);
if w > 0 then
for i := 0 to Grid.Columns.Count - 1 do
Grid.Columns[i].Width := Grid.Columns[i].Width + w;
ds.GotoBookmark(bm);
finally
ds.FreeBookmark(bm);
ds.EnableControls;
end;
end;
Minor modification of bummi's answer to insure that Title Row (Row 0) is not truncated
procedure FitGrid(Grid: TDBGrid);
const
C_Add=3;
var
ds: TDataSet;
bm: TBookmark;
i: Integer;
w: Integer;
a: Array of Integer;
begin
ds := Grid.DataSource.DataSet;
if Assigned(ds) then
begin
ds.DisableControls;
bm := ds.GetBookmark;
try
ds.First;
SetLength(a, Grid.Columns.Count);
while not ds.Eof do
begin
for I := 0 to Grid.Columns.Count - 1 do
begin
if Assigned(Grid.Columns[i].Field) then
begin
w := Grid.Canvas.TextWidth(ds.FieldByName(Grid.Columns[i].Field.FieldName.).DisplayText);
if a[i] < w then
a[i] := w ;
end;
end;
ds.Next;
end;
//if fieldwidth is smaller than Row 0 (field names) fix
for I := 0 to Grid.Columns.Count - 1 do
begin
w := Grid.Canvas.TextWidth(Grid.Columns[i].Field.FieldName);
if a[i] < w then
a[i] := w ;
end;
for I := 0 to Grid.Columns.Count - 1 do
Grid.Columns[i].Width := a[i] + C_Add;
ds.GotoBookmark(bm);
finally
ds.FreeBookmark(bm);
ds.EnableControls;
end;
end;
end;
Minor modification of bummi's, TheSteven's and Jens' answers to insure that Title Row (Row 0) is not truncated, and excess space will be allocated on each column, and take visibility of columns into account.
procedure FitGrid(const Grid: TDBGrid; const CoverWhiteSpace: Boolean = True);
const
C_Add=3;
var
DS: TDataSet;
BM: TBookmark;
I, W, VisibleColumnsCount: Integer;
A: array of Integer;
VisibleColumns: array of TColumn;
begin
DS := Grid.DataSource.DataSet;
if Assigned(DS) then
begin
VisibleColumnsCount := 0;
SetLength(VisibleColumns, Grid.Columns.Count);
for I := 0 to Grid.Columns.Count - 1 do
if Assigned(Grid.Columns[I].Field) and (Grid.Columns[I].Visible) then
begin
VisibleColumns[VisibleColumnsCount] := Grid.Columns[I];
Inc(VisibleColumnsCount);
end;
SetLength(VisibleColumns, VisibleColumnsCount);
DS.DisableControls;
BM := DS.GetBookmark;
try
DS.First;
SetLength(A, VisibleColumnsCount);
while not DS.Eof do
begin
for I := 0 to VisibleColumnsCount - 1 do
begin
W := Grid.Canvas.TextWidth(DS.FieldByName(VisibleColumns[I].Field.FieldName).DisplayText);
if A[I] < W then
A[I] := W;
end;
DS.Next;
end;
//if fieldwidth is smaller than Row 0 (field names) fix
for I := 0 to VisibleColumnsCount - 1 do
begin
W := Grid.Canvas.TextWidth(VisibleColumns[I].Field.FieldName);
if A[I] < W then
A[I] := W;
end;
W := 0;
if CoverWhiteSpace then
begin
for I := 0 to VisibleColumnsCount - 1 do
Inc(W, A[I] + C_Add);
W := (Grid.ClientWidth - W - 20) div VisibleColumnsCount;
if W < 0 then
W := 0;
end;
for I := 0 to VisibleColumnsCount - 1 do
VisibleColumns[I].Width := A[I] + C_Add + W;
DS.GotoBookmark(BM);
finally
DS.FreeBookmark(BM);
DS.EnableControls;
end;
end;
end;
I found 'Columns' in the properties of my TDBGrid and new window is opened. In that window I add new FieldNames - you can choose columnNames only from the result of your SQL string of your TADOQuery and then when you click to the exact column you can find 'Width' in the properties of selected column so change it whatever you like. It works for me.
I'm afraid the last adaptation of the code is no correct.
I would rather write something like :
const
C_Add=20;
//.....
Sup := 0;
if CoverWhiteSpace then
sup :=C_Add;
for I := 0 to VisibleColumnsCount - 1 do
VisibleColumns[I].Width := A[I] + sup;
DS.GotoBookmark(BM);

Randomize array? [duplicate]

This question already has answers here:
Randomize StringList
(3 answers)
Closed 9 years ago.
In pascal, i want to randomly organize an array. Therefore the majority of the time the array should be organized differently.
Consider this array setup
const
ARRAY_ELEMENTS = 3;
SetLength(iIndex, ARRAY_ELEMENTS);
for i := Low(iIndex) to High(iIndex) do
begin
case i of
0: iIndex[i] := 0;
1: iIndex[i] := 1;
2: iIndex[i] := 2;
end;
end;
How is it possible for the iIndex[] that contains the value 0 not to always be in the first element of the array and for the iIndex[] that contains value 2 not to be always the last value of the array but generate the order of the array randomly so that the order of the array is not always the same on initialization?
This code permutes an integer array, but I'm not sure if it is optimal (probably isn't).
type
TDynIntegerArray = array of integer;
procedure PermuteArray(A: TDynIntegerArray);
var
B: TDynIntegerArray;
Z: TDynIntegerArray;
π: TDynIntegerArray;
i: Integer;
j: Integer;
k: Integer;
begin
B := Copy(A);
SetLength(Z, Length(A));
SetLength(π, Length(A));
for i := 0 to High(Z) do
Z[i] := i;
for i := 0 to High(π) do
begin
π[i] := RandomFrom(Z);
for j := 0 to High(Z) do
begin
if Z[j] = π[i] then
begin
for k := j to High(Z) - 1 do
Z[k] := Z[k+1];
SetLength(Z, length(Z) - 1);
break;
end;
end;
end;
for i := 0 to High(A) do
A[i] := B[π[i]];
end;
A much faster, but less cool approach is simply to randomly swap the items, one pair at a time:
procedure FastPermuteArray(A: TDynIntegerArray);
procedure Swap(n, m: integer);
var
tmp: integer;
begin
tmp := A[n];
A[n] := A[m];
A[m] := tmp;
end;
var
i: Integer;
begin
for i := High(A) downto 1 do
Swap(i, RandomRange(0, i));
end;
Try something like this:
uses
System.Generics.Collections;
const
ARRAY_ELEMENTS = 3;
var
iArray: array of Integer;
iIndex: TList<Integer>;
I, j: Integer;
begin
Randomize;
SetLength(iArray, ARRAY_ELEMENTS);
iIndex := TList<Integer>.Create;
try
iIndex.Count := ARRAY_ELEMENTS;
for i := 0 to Pred(ARRAY_ELEMENTS) do
iIndex[i] := i;
for i := Low(iArray) to High(iArray) do
begin
j := Random(iIndex.Count);
iArray[iIndex[j]] := i;
iIndex.Delete(j);
end;
finally
iIndex.Free;
end;
end;
If you don't have TList<T> available in your version of Delphi, you can use a plain TList instead:
uses
Classes;
const
ARRAY_ELEMENTS = 3;
var
iArray: array of Integer;
iIndex: TList;
I, j: Integer;
begin
Randomize;
SetLength(iArray, ARRAY_ELEMENTS);
iIndex := TList.Create;
try
iIndex.Count := ARRAY_ELEMENTS;
for i := 0 to Pred(ARRAY_ELEMENTS) do
iIndex[i] := Pointer(I);
for i := Low(iArray) to High(iArray) do
begin
j := Random(iIndex.Count);
iArray[Integer(iIndex[j])] := i;
iIndex.Delete(j);
end;
finally
iIndex.Free;
end;
end;

How to get the length of the longest entry on TDBGrid Columns

I have a TDBGrid component called grMain. I need to know length of value of the longest entries of Column which retrivied on grMain to adjust the minimal width of the form holding the grMain.
How to get the length of the longest entry on TDBGrid Columns?
Thanks in advance.
Something like that ...
Procedure FitGrid(Grid:TDBGrid);
Const
C_Add=3;
var
ds:TDataset;
bm:TBookmark;
i:Integer;
w:Integer;
a:Array of Integer;
begin
ds := Grid.DataSource.DataSet;
if Assigned(ds) then
begin
ds.DisableControls;
bm := ds.GetBookmark;
try
ds.First;
SetLength(a,Grid.Columns.Count);
ZeroMemory(#a[0],SizeOf(Integer)*Length(a));
while not ds.Eof do
begin
for I := 0 to Grid.Columns.Count - 1 do
begin
if Assigned( Grid.Columns[i].Field) then
begin
w := Grid.Canvas.TextWidth( ds.FieldByName( Grid.Columns[i].Field.FieldName).DisplayText);
if a[i] < w then a[i] := w + C_Add;
end;
end;
ds.Next;
end;
for I := 0 to Grid.Columns.Count - 1 do Grid.Columns[i].Width := a[i];
ds.GotoBookmark(bm);
finally
ds.FreeBookmark(bm);
ds.EnableControls;
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
FitGrid(DBgrid1)
end;

Delphi - Get line index and data index from given data

So I have list of data that may contain different charters:
1dAAbt54
agFlE9dA
1295RTdd
First line data contains: 1d, AA, bt, 54. All I need is function that gives me the data of given index. Example: data of index 6 is Fl (Line - 2, Index in line is 2). Every line lenght is 8 and data length is 2;
How can I make such function in Delphi?
The result function should be something like this:
procedure (DataList: TStringList; DataIndex: Integer; var LineIndex: Integer; var PosInLine: Integer);
begin
//do the algorithm
end;
Sorry for my bad english...
Answer to the first version of your question
The following is an answer to the first version of your question (before you edited it):
function GetIndexOfInteger(DataList: TStringList; DataIndex: Integer;
out LineIndex: Integer; out PosInLine: Integer): boolean;
var
x, y: Integer;
InNum: boolean;
NumStart: integer;
ValIndex: integer;
begin
result := false;
for y := 0 to DataList.Count - 1 do
begin
InNum := false;
ValIndex := 0;
for x := 1 to Length(DataList[y]) do
begin
if (DataList[y][x] <> chr(32)) and not InNum then
begin
NumStart := x;
InNum := true;
inc(ValIndex);
end;
if InNum and ((DataList[y][x] = chr(32)) or
(x = Length(DataList[y]))) then
begin
if StrToInt(Copy(DataList[y], NumStart, x - NumStart +
IfThen(x = Length(DataList[y]), 1))) = DataIndex then
begin
LineIndex := y + 1;
PosInLine := ValIndex;
result := true; // Roberts is on D7.
Exit; //
end;
InNum := false;
Continue;
end;
end;
end;
end;
Try it:
procedure TForm4.FormCreate(Sender: TObject);
var
SR: TStringList;
line, col: integer;
begin
SR := TStringList.Create;
SR.Add('1 2 3');
SR.Add('4 5 6');
SR.Add('7 8 9');
SR.Add('10 11 12 13');
if GetIndexOfInteger(SR, 13, line, col) then
ShowMessage(Format('%d, %d', [line,col]));
end;
Answer to the second version of your question
(And this is so easy you could've done it yourself! :)
function GetIndexOfItemInListOfPairs(DataList: TStringList; Data: String; out LineIndex: Integer; out PosInLine: Integer): boolean;
var
x, y: Integer;
begin
result := false;
for y := 0 to DataList.Count - 1 do
for x := 0 to Length(DataList[y]) div 2 - 1 do
if Copy(DataList[y], 2*x + 1, 2) = Data then
begin
LineIndex := y + 1;
PosInLine := x + 1;
Exit(true);
end;
end;
procedure TForm4.FormCreate(Sender: TObject);
var
SR: TStringList;
line,col:integer;
begin
SR := TStringList.Create;
SR.Add('1dAAbt54');
SR.Add('agFlE9dA');
SR.Add('1295RTdd');
if GetIndexOfItemInListOfPairs(SR, 'RT', line, col) then
ShowMessage(Format('%d, %d', [line,col]));
end;
Answer to the third version of your question
procedure TForm4.FormCreate(Sender: TObject);
var
RowIndex, ColIndex: Word;
begin
DivMod(6 {index} - 1, 4 {items per row}, RowIndex, ColIndex);
inc(RowIndex);
inc(ColIndex);
ShowMessageFmt('%d, %d', [RowIndex, ColIndex]);
end;
How about ...
procedure Search( DataList: TStringList; DataIndex: Integer; var LineIndex: Integer; var PosInLine: Integer);
var
j, LineLen: integer;
Line: string;
begin
LineIndex := 0;
PosInLine := 0;
for j := 0 to DataList.Count - 1 do
begin
Line := DataList[j];
Inc( LineIndex);
LineLen := Length( Line) div 2;
if DataIndex >= LineLen then
begin
Dec( DataIndex, LineLen);
continue
end;
PosInLine := LineLen;
break
end;
if PosInLine = 0 then // No find
LineIndex := 0
end;
Oops. The question has been changed completely. Here is the procedure for the question v 1.0 :)
procedure FindIndex(Data:TStringList; Index:integer;var LineIndex,PosInLine:Integer);
var i:integer;
CurrentStr:String;
StrToFind:String;
begin
LineIndex:=0;
PosInLine:=0;
StrToFind:=intToStr(Index)+' ';
for i:=0 to Data.Count-1 do
begin
CurrentStr:=' '+Data.Strings[i]+' ';
IF POS(' '+StrToFind,CurrentStr)>0 then
begin
LineIndex:=i+1;
//now we need to find PosInLine
PosInLine:=1;
repeat
CurrentStr:=Trim(CurrentStr)+' ';
IF Pos(StrToFind,CurrentStr)=1 then exit; //we found it
CurrentStr:=copy(CurrentStr,POS(' ',CurrentStr),length(CurrentStr));
inc(PosInLine);
until (CurrentStr='');
exit;
end;
end;
end;
Tested with this code
var T:TStringList;
Li,Pi:integer;
i:integer;
begin
T:=TStringList.Create();
T.Add('1 2 3');
T.Add(' 4 5 6');
T.Add('7 8 9');
T.Add('10 11 12 ');
for i:=0 to 13 do
begin
FindIndex(T,i,Li,Pi);
Memo1.Lines.Add(IntToStr(i)+':'+IntToStr(Li)+'-'+IntToStr(Pi))
end;
end;

Resources