Hello,
This one really got me going. I went through the VCL source
and found indeed there is a bunch of undocumented multiselect features
for the DBGrid. I think the reason they are undocumented is because
they don't work.
For example the TDBGrid (found in DBGrids.pas) has a property
called SelectedRows which is of type TBookMarkList (also in
DBGrids.pas). It has an Items property which supposed to keep a list
of bookmark strings (I guess they wanted to name bookmarks with
strings and pointers) I dug deeper and wrote some code to see if I
could pull the SelectedRows.Items[cnt] text out. Guess what?
The strings came up empty. Yes, the Count property worked great,
but the strings were empty. Now, I'm getting real curious.
I look into how the thing actually works and find DBGrids has a
BookMark property of type TBookMarkStr (just a string). Well, I
decided I'd see what happens if I placed a string into this property.
It bombed in a big way. Some exception about being at the top of the
table. I think the exact call that is bombing in the VCL is:
{* this is Delphi 2.0 Code from its RTL Source released with Delphi
2.0 C/S. All copyrights are granted.*}
function TDataset.GetBookmarkStr: TBookmarkStr;
begin
Result := '';
if (State in [dsBrowse, dsEdit, dsInsert]) and (FRecordCount > 0)
and (ActiveBuffer[FBookmarkOfs] = #0) then
begin
SetString(Result, PChar(@ActiveBuffer[FBookmarkOfs + 1]),
FBookmarkSize);
end;
end;
procedure TDataSet.GotoBookmark(Bookmark: TBookmark);
begin
if Bookmark <> nil then
begin
CheckBrowseMode;
Check(DbiSetToBookmark(FHandle, Bookmark));
Resync([rmExact, rmCenter]);
end;
end;
==== Right here we pass in a type of TBookMarkStr and then pass
it as pointer to a string === Up above is where the error occurs.
procedure TDataset.SetBookmarkStr(const Value: TBookmarkStr);
begin
GotoBookmark(Pointer(Value));
end;
So I figure I am hosed, but then I actually look at what the VCL was
trying to do and it makes sense. When is multiselect mode they simply
create an anchor bookmark and then keep track of the number of rows.
With these two values you can goto the bookmark and then parse the
records using the count.
With this I made the following chunk of TEST code to see I could do it
and I could. It's ugly, and I probably would go into the VCL and make
it work correctly before I actually implemented this, but it works so
hey take it for what it's worth:
type
TGridSelection = record
bSelected : boolean;
iRows : integer;
bkAnchor : TBookMark;
end;
var
Form1: TForm1;
MyGridSel : TGridSelection;
implementation
{$R *.DFM}
{* this event procedure simply goes through the GridSelection based on
the *}
{* anchor and the number of rows. If rows is negative then they
selected *}
{* with the up arrow and if it is positive they selected with the down
*}
{*
*}
{* basic premise is to go from the anchor record and physically go
through *}
{* the table records. This mimics closely what the VCL tried to
accomplish*}
procedure TForm1.BitBtn1Click(Sender: TObject);
type TEnum_Dir = (UP,DOWN);
var cnt : integer;
eDir: TEnum_Dir;
begin
eDir := UP;
if (MyGridSel.bSelected) then
begin
memo1.lines.clear;
if MyGridSel.iRows > 0 then
eDir := DOWN;
cnt := ABS(MyGridSel.iRows);
table1.gotobookmark(MyGridSel.bkAnchor);
While cnt >= 0 do
begin
memo1.lines.add(table1.fields[1].asstring);
if eDir = UP then
table1.prior
else if eDir = DOWN then
table1.next;
dec(cnt);
end;
table1.gotobookmark(MyGridSel.bkAnchor);
memo1.lines.add('This is the end of the list...');
end;
end;
{************************************************************}
{* This event creates the bookmark for the current record *}
{* and begins the row counting. *}
{************************************************************}
procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
cnt : integer;
begin
if (ssShift in Shift) and ((Key = VK_DOWN) or (Key = VK_UP)) then
begin
try
{*if we do not have selection area yet, start one*}
if not MyGridSel.bSelected then begin
{* the shift arrow key moved the table record *}
if (Key = VK_DOWN) then
table1.prior
else if (Key = VK_UP) then
table1.next;
{*create the actual GridSelection record*}
MyGridSel.bkanchor := table1.getbookmark;
MyGridSel.bSelected := true;
MyGridSel.iRows := 0;
{* move the table record back *}
if (Key = VK_DOWN) then
table1.next
else if (Key = VK_UP) then
table1.prior;
end;
{* now increment or decrement the row counter *}
if MyGridSel.bSelected then
if Key = VK_UP then
dec(MyGridSel.iRows)
else
inc(MyGridSel.iRows);
except on E:EDBEngineError do
begin
Memo1.lines.clear;
for cnt := 0 to E.Errorcount-1 do
Memo1.lines.add(IntToStr(E.Errors[cnt].ErrorCode)
+ ' -- ' + E.Errors[cnt].Message);
Memo1.lines.add('There was an error with your
database modification request.');
Memo1.lines.add('Please try again later.');
end;
end;
end
{* if they just hit the arrow key without the shift held down, then
reinit*}
{* the GridSelection record
*}
else if (Key = VK_UP) or (Key = VK_DOWN) then
if MyGridSel.bSelected then
begin
MyGridSel.bSelected := false;
MyGridSel.iRows := 0;
table1.FreeBookMark(MyGridSel.bkAnchor);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyGridSel.bSelected := false;
MyGridSel.iRows := 0;
end;
end.
Boy, I hate these newsgroups, they really get me digging.
Hope this helps,
Jeff
-------------------
Quote
atroe...@nelly.mat.univie.ac.at (Andreas Troester) wrote:
>Frank Jiang (fji...@msn.com) wrote:
>: In delphi2 I use multiselect in dbgrid. When i use
>: gbgrid1.selectedrows.count, i can tell how many rows are selected,
>: but how can i know which rows are selected? Any ideas? Thanks for
>: any suggestions.
>: Frank at fji...@msn.com
>That's something I would like to know, too.
>In Delphi's readme file some lines are spent on how to use the
>multiselect property, but I could not make sense of them.
>To me it is a very important feature that should deserve (better)
>documentation (does anyone know if Steve Koterski is still with us?).
>Regards,
>Andy