📄 dbgrid.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>UDDF - DBGRID</TITLE>
<META NAME="Description" CONTENT="DBGrid section of the Delphi Developers FAQ" >
<META NAME="KeyWords" CONTENT="" >
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
<CENTER>
<IMG SRC="../images/uddf.jpg"> </CENTER>
<H1><A NAME="dbgrid0">Using the Multi Select option of a DBGRID</A></H1>
<I><P>mike@sentex.net (Mike Tancsa)</P>
</I><P>There is an example in the Delphi TIs... Have a look at</P>
<P><A HREF="http://loki.borland.com/winbin/bds.exe?getdoc+2976+delphi">http://loki.borland.com/winbin/bds.exe?getdoc+2976+Delphi</A> </P>
<P><HR></P>
<PRE>{*
This example iterates through the selected rows
of the grid and displays the second field of
the dataset.
The Method DisableControls is used so that the
DBGrid will not update when the dataset is changed.
The last position of the dataset is saved as
a TBookmark.
The IndexOf method is called to check whether or
not the bookmark is still existent.
The decision of using the IndexOf method rather
than the Refresh method should be determined by the
specific application.
*}
procedure TForm1.SelectClick(Sender: TObject);
var
x: word;
TempBookmark: TBookMark;
begin
DBGrid1.Datasource.Dataset.DisableControls;
with DBgrid1.SelectedRows do
if Count <> 0 then
begin
TempBookmark:= DBGrid1.Datasource.Dataset.GetBookmark;
for x:= 0 to Count - 1 do
begin
if IndexOf(Items[x]) > -1 then
begin
DBGrid1.Datasource.Dataset.Bookmark:= Items[x];
showmessage(DBGrid1.Datasource.Dataset.Fields[1].AsString);
end;
end;
end;
DBGrid1.Datasource.Dataset.GotoBookmark(TempBookmark);
DBGrid1.Datasource.Dataset.FreeBookmark(TempBookmark);
DBGrid1.Datasource.Dataset.EnableControls;
end;</PRE>
<H1><A NAME="dbgrid1">Dropdownlist in a DBGrid, HOW ?</A></H1>
<I><P>From: Susan <terminal@meinc.com></P>
</I><P>Q: How do I put components into a TDBGrid?</P>
<P>A: I saw this on compuserve and it is great!</P>
<H2>HOW TO PUT COMPONENTS INTO A GRID</H2>
<P>This article and the accompanying code shows how to put just about any component into a cell on a grid.
By component I mean anything from a simple combobox to a more complicated dialog box.
The techniques described below to anything that is termed a visual component. If you can put it into a form you can probably put it into a grid.</P>
<P>There are no new ideas here, in fact, the basic technique simply mimics what the DBGrid does internally.
The idea is to float a control over the grid. Inside DBGrid is a TDBEdit that moves around the grid. It's that TDBEdit that you key you data into.
The rest of the unfocused cells are really just pictures. What you will learn here, is how to float any type of visual control/component around the grid.</P>
<H3>COMPONENT #1 - TDBLOOKUPCOMBO</H3>
<P>You need a form with a DBGrid in it. So start an new project and drop a DBGrid into the main form.</P>
<P>Next drop in a TTable and set it's Alias to DBDEMOS, TableName to GRIDDATA.DB and set the Active property to True.
Drop in a DataSource and set it's DataSet property to point to Table1. Go back to the grid and point it's DataSource property to DataSource1.
The data from GRIDDATA.DB should appear in your grid..</P>
<P>The first control we are going to put into the grid is a TDBLookupCombo so we need a second table for the lookup.
Drop a second TTable into the form. Set it's Alias also to DBDEMOS, TableName to CUSTOMER.DB and Active to True.
Drop in a second data source and set its DataSet to Table2.</P>
<P>Now go get a TDBLookupCombo from the Data Controls pallet and drop it any where on the form, it doesn't matter where since it will
usually be invisible or floating over the grid. Set the LookuoCombo's properties as follows.</P>
<P><HR></P>
<PRE> DataSource DataSource1
DataField CustNo
LookupSource DataSource2
LookupField CustNo
LookupDisplay CustNo {you can change it to Company later but keep it custno for now)</PRE>
<P><HR></P>
<P>So far it's been nothing but boring point and click. Now let's do some coding.</P>
<P>The first thing you need to do is make sure that DBLookupCombo you put into the form is invisible when you run the app.
So select Form1 into Object Inspector goto the Events tab and double click on the onCreate event. You should now have the shell for the
onCreate event displayed on your screen.</P>
<P><HR></P>
<PRE>procedure TForm1.FormCreate(Sender: TObject);
begin
end;</PRE>
<P><HR></P>
<P>Set the LookupCombo's visible property to False as follows.</P>
<P><HR></P>
<PRE>procedure TForm1.FormCreate(Sender: TObject);
begin
DBLookupCombo1.Visible := False;
end;</PRE>
<P><HR></P>
<P>Those of you who are paying attention are probably asking why I didn't just set this in the Object Inspector for the component.
Actually, you could have. Personally, I like to initialize properties that change at run time in the code. I set static properties that don't change
as the program runs in the object inspector. I think it makes the code easier to read. </P>
<P>Now we to be able to move this control around the grid. Specifically we want it to automatically appear as you either cursor or
click into the column labeled DBLookupCombo. This involves defining two events for the grid, OnDrawDataCell and OnColExit.
First lets do OnDrawDataCell. Double click on the grid's OnDrawDataCell event in the Object Inspector and fill in the code as follows. </P>
<P><HR></P>
<PRE>procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
begin
if (gdFocused in State) then
begin
if (Field.FieldName = DBLookupCombo1.DataField) then
begin
DBLookupCombo1.Left := Rect.Left + DBGrid1.Left;
DBLookupCombo1.Top := Rect.Top + DBGrid1.top;
DBLookupCombo1.Width := Rect.Right - Rect.Left;
{ DBLookupCombo1.Height := Rect.Bottom - Rect.Top; }
DBLookupCombo1.Visible := True;
end;
end;
end;</PRE>
<P><HR></P>
<P>The reasons for the excessive use begin/end will become clear later in the demo.
The code is saying that if the State parameter is gdFocused then this particular cell is
the one highlighted in the grid. Further if it's the highlighted cell and the cell
has the same field name as the lookup combo's datafield then we need to move the LookupCombo over
that cell and make it visible. Notice that the position is determined relative to the form
not to just the grid. So, for example, the left side of LookupCombo needs to be the offset of the
grid ( DBGrid1.Left) into the form plus the offset of the cell into the grid (Rect.Left).</P>
<P>Also notice that the Height of the LookupCombo has been commented out above.
The reason is that the LookupCombo has a minimum height.
You just can't make it any smaller.
That minimum height is larger than the height of the cell.
If you un-commented the height line above.
Your code would change it and then Delphi would immediately change it right back.
It causes an annoying screen flash so don't fight it.
Let the LookupCombo be a little larger than the cell. It looks a little funny but it works.</P>
<P>Now just for fun run the program.
Correct all you missing semi-colons etc.
Once its running try moving the cursor around the grid.
Pretty cool, hu? Not! We're only part of the way there.
We need to hide the LookupCombo when we leave the column.
So define the grid's onColExit. It should look like this;</P>
<P><HR></P>
<PRE>procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
If DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField then
DBLookupCombo1.Visible := false;
end;</PRE>
<P><HR></P>
<P>This uses the TDBGrids SelectedField property to match up the FieldName
associated with the cell with that of the LookupCombo. The code says,
"If the cell you are leaving was in the DBLookupCombo column then make it invisible".</P>
<P>Now run it again. Was that worth the effort or what?</P>
<P>Now things look right but we're still missing one thing.
Try typing a new customer number into one of the LookupCombo.
The problem is that the keystrokes are going to the grid, not to the LookupCombo.
To fix this we need to define a onKeyPress event for the grid. It goes like this;</P>
<P><HR></P>
<PRE>procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if (key <> chr(9)) then
begin
if (DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField) then
begin
DBLookupCombo1.SetFocus;
SendMessage(DBLookupCombo1.Handle, WM_Char, word(Key), 0);
end;
end;
end;</PRE>
<P><HR></P>
<P>This code is saying that if the key pressed is not a tab key (Chr(9)) and the current field in the grid is the
LookupCombo then set the focus to the LookupCombo and then pass the
keystroke over to the LookupCombo. OK so I had to use a WIN API function.
You don't really need to know how it works just that it works.</P>
<P>But let me explain a bit anyway.
To make Window's SendMessage function work you must give it the handle of the component you want to send the message to.
Use the component's Handle property.
Next it wants to know what the message is.
In this case it's Window's message WM_CHAR which says I'm sending the LookupCombo a character.
Finally, you need to tell it which character, so word(Key). That's a typecast to type word of the events Key parameter.
Clear as mud, right? All you really need to know is to replace the DBLookupCombo1 in the call to the
name of the component your putting into the grid. If you want more info on SendMessage do a search in Delphi's on-line help.</P>
<P>Now run it again and try typing. It works! Play with it a bit and see how the tab key gets you out of "edit mode" back into
"move the cell cursor around mode".</P>
<P>Now go back to the Object Inspector for the DBLookupCombo component and change the LookupDIsplay property to Company. Run it. Imagine the possibilities.</P>
<H3>COMPONENT #2 - TDBCOMBO</H3>
<P>I'm not going to discuss installing the second component, a DBCombo,
because I don't really have anything new to say. It's really the same as #1. Here's the incrementally developed code for your review.</P>
<P><HR></P>
<PRE>procedure TForm1.FormCreate(Sender: TObject);
begin
DBLookupCombo1.Visible := False;
DBComboBox1.Visible := False;
end;
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
begin
if (gdFocused in State) then
begin
if (Field.FieldName = DBLookupCombo1.DataField) then
begin
DBLookupCombo1.Left := Rect.Left + DBGrid1.Left;
DBLookupCombo1.Top := Rect.Top + DBGrid1.top;
DBLookupCombo1.Width := Rect.Right - Rect.Left;
DBLookupCombo1.Visible := True;
end
else if (Field.FieldName = DBComboBox1.DataField) then
begin
DBComboBox1.Left := Rect.Left + DBGrid1.Left;
DBComboBox1.Top := Rect.Top + DBGrid1.top;
DBComboBox1.Width := Rect.Right - Rect.Left;
DBComboBox1.Visible := True;
end
end;
end;
procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
If DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField then
DBLookupCombo1.Visible := false
else If DBGrid1.SelectedField.FieldName = DBComboBox1.DataField then
DBComboBox1.Visible := false;
end;
procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char);
begin
if (key <> chr(9)) then
begin
if (DBGrid1.SelectedField.FieldName = DBLookupCombo1.DataField) then
begin
DBLookupCombo1.SetFocus;
SendMessage(DBLookupCombo1.Handle, WM_Char, word(Key), 0);
end
else if (DBGrid1.SelectedField.FieldName = DBComboBox1.DataField)
then
begin
DBComboBox1.SetFocus;
SendMessage(DBComboBox1.Handle, WM_Char, word(Key), 0);
end;
end;
end;</PRE>
<P><HR></P>
<H3>COMPONENT #3 - TDBCHECKBOX</H3>
<P>The DBCheckBox gets even more interesting.
In this case it seems appropriate to leave something in the non-focused checkbox cells to indicate that there's a check box there.
You can either draw the "stay behind" image of the checkbox or you can blast in a picture of the checkbox.
I chose to do the latter. I created two BMP files one that's a picture of the box checked (TRUE.BMP) and one that's a picture of the box unchecked (FALSE.BMP).
Put two TImage components on the form called ImageTrue and ImageFalse and attach the BMP files to there respective Picture properties.
Oh yes you also need to put a DBCheckbox component on the form.
Wire it to the CheckBox field in DataSource1 and set the Color property to clWindow. First edit the onCreate so it reads as follows;</P>
<P><HR></P>
<PRE>procedure TForm1.FormCreate(Sender: TObject);
begin
DBLookupCombo1.Visible := False;
DBCheckBox1.Visible := False;
DBComboBox1.Visible := False;
ImageTrue.Visible := False;
ImageFalse.Visible := False;
end;</PRE>
<P><HR></P>
<P>Now we need to modify the onDrawDataCell to do something with cells that do not have the focus. Here comes the code.</P>
<P><HR></P>
<PRE>procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect;
Field: TField; State: TGridDrawState);
begin
if (gdFocused in State) then
begin
if (Field.FieldName = DBLookupCombo1.DataField) then
begin
...SEE ABOVE
end
else if (Field.FieldName = DBCheckBox1.DataField) then
begin
DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 1;
DBCheckBox1.Top := Rect.Top + DBGrid1.top + 1;
DBCheckBox1.Width := Rect.Right - Rect.Left{ - 1};
DBCheckBox1.Height := Rect.Bottom - Rect.Top{ - 1};
DBCheckBox1.Visible := True;
end
else if (Field.FieldName = DBComboBox1.DataField) then
begin
...SEE ABOVE
end
end
else {in this else area draw any stay behind bit maps}
begin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -