mike@sentex.net (Mike Tancsa)
There is an example in the Delphi TIs... Have a look at
http://loki.borland.com/winbin/bds.exe?getdoc+2976+Delphi
{* 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;
Robert Vivrette - 76416.1373@compuserve.com
Many professional applications will display data in grid fields and allow you to sort on any one of the columns simply by clicking on the column header. Although what is proposed here is not the best way to accomplish this, it is a fairly simple way to mimic the same behavior.
The key hurdle in this problem is the DBGrid itself. It has no OnClick or OnMouseDown events, so it really was not designed to capture this kind of input. It does provide an OnDoubleClick, but this really doesn't work too well. What we need is a way to make the column headers clickable. Enter the THeaderControl component.
THeaderControl is a component that comes in Delphi 2.0 and provides the basic functions that we want. It can detect clicks on its individual panels, and the panels even go up and down when pressed (like a button). The key is to connect the THeaderControl to the DBGrid. Here is how it is done:
First, start a new application. Drop a THeaderControl on the form. It will automatically align to the top edge of the form. Now drop a DBGrid on the form and set its Align property to alClient. Next, add a TTable, and TDataSource component. Set the Tables DatabaseName property to DBDEMOS and its TableName to EVENTS.DB. Set the DataSource's DataSet property to point at Table1 and the DBGrid's DataSource property to point to DataSource1. Set Table's Active property to False in case it has been turned on. Now the fun begins!
Now we need to setup the THeaderControl component to look like the DBGrid's column headers. his will be done in code in the Form's FormCreate method. DoubleClick on Form1's OnCreate event and enter the following code:
procedure TForm1.FormCreate(Sender: TObject); var TheCap : String; TheWidth,a : Integer; begin DBGrid1.Options := DBGrid1.Options - [dgTitles]; HeaderControl1.Sections.Add; HeaderControl1.Sections.Items[0].Width := 12; Table1.Exclusive := True; Table1.Active := True; For a := 1 to DBGrid1.Columns.Count do begin with DBGrid1.Columns.Items[a-1] do begin TheCap := Title.Caption; TheWidth := Width; end; with HeaderControl1.Sections do begin Add; Items[a].Text := TheCap; Items[a].Width := TheWidth+1; Items[a].MinWidth := TheWidth+1; Items[a].MaxWidth := TheWidth+1; end; try Table1.AddIndex(TheCap,TheCap,[]); except HeaderControl1.Sections.Items[a].AllowClick := False; end; end; Table1.Active := False; Table1.Exclusive := False; Table1.Active := True; end;
Since the THeaderControl will be taking the place of the Grid's column headers, we first remove (set to False) the dgTitles option in the DBGrid's Options property. Then, we add a column to the HeaderControl and set its width to 12. This will be a blank column that is the same width as the Grid's status area on the left.
Next we need to make sure the Table is opened for Exclusive use (no other users can be using it). I will explain why in just a bit.
Now we add the HeaderControl sections. For each one we add, we will be giving it the same text as the caption of that column in the DBGrid. We loop through the DBGrid columns, and for each one we copy over the column's caption and width. We also set the HeaderControl's MinWidth and MaxWidth properties to the same as the column width. This will prevent the column from being resized. If you need resizeable columns, you will need a bit more code, and I wanted to keep this short and sweet.
Now comes the interesting part. We are going to create an index for each column in the DBGrid. The name of the index will be the same as the columns title. This step is in a try..finally structure because there are some fields that cannot be indexed (Blobs & Memos for example). When it tries to index on these fields, it will generate an exception. We catch this exception and turn off the ability to click that column. This means that non-indexed columns will not respond to mouse clicks. The creation of these indexes is why we had to open the table in Exclusive mode. After we are all done, we close the table, set Exclusive off and reopen then table.
One last step. When the HeaderControl is clicked, we need to turn on the correct index for the Table. The HeaderControl's OnSectionClick method should be as follows:
procedure TForm1.HeaderControl1SectionClick( HeaderControl: THeaderControl; Section: THeaderSection); begin Table1.IndexName := Section.Text; end;
That's it! When the column is clicked, the Table's IndexName property is set to the same as the HeaderControl's caption.
Pretty simple, huh? There is a lot of room for improvement however. It would be nice if clicking on a column a second time would reverse the sort order. Also, column resizing would be a nice added touch. I am going to leave these to you folks!
The Graphical Gnome <uddf@gnomehome.demon.nl>
The improvement over the previous version is in the usage of the fieldname as indexname instead of the caption.
This improves the flexibility. Changes are indicated as italics
procedure TfrmDoc.FormCreate(Sender: TObject); Var TheCap : String; TheFn : String; TheWidth : Integer; a : Integer; begin Dbgrid1.Options := DBGrid1.Options - [DGTitles]; Headercontrol1.sections.Add; Headercontrol1.Sections.Items[0].Width := 12; For a := 1 to DBGRID1.Columns.Count do begin with DBGrid1.Columns.Items[ a - 1 ] do begin TheFn := FieldName; TheCap := Title.Caption; TheWidth := Width; end; With Headercontrol1.Sections DO BEGIN Add; Items[a].Text := TheCap; Items[a].Width := TheWidth + 1; Items[a].MinWidth := TheWidth + 1; Items[a].MaxWidth := TheWidth + 1; END; (* WITH Headercontrol1.Sections *) try (* except *) { Use indexes with the same name as the fieldname } (DataSource1.Dataset as TTable).IndexName := TheFn; { Try to set the index name } except HeaderControl1.Sections.Items[a].AllowClick := False; { Index not Available } end; (* EXCEPT *) END; (* FOR *) END; (* PROCEDURE *)
Use the fieldname property of the DBGrid to set an index with the same name as the fieldname.
procedure TfrmDoc.HeaderControl1SectionClick(HeaderControl: THeaderControl; Section: THeaderSection); begin (DataSource1.Dataset as TTable).IndexName := DBGrid1.Columns.Items[ Section.Index - 1 ].FieldName; end;
Ed_P._Hillmann@mail.amsinc.com (Ed Hillmann)
I don't know if this helps, but I could color individual cells in a DBGrid without having to make a new DBGrid component. This is what I just tested....I created a form, dropped a TTable component it, and pointed it to the EMPLOYEE.DB database in the DBDEMOS database. I dropped a Datasource and DBGrid on the form so that it showed on the form.
I thought a simple test would be, for the employee number in the EMPLOYEE.DB table, check if it's an odd number. If it's an odd number, then turn that cell green.
Then, the only code I attached was to the DBGrid's OnDrawColumnCell event, which looks as follows....
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var holdColor: TColor; begin holdColor := DBGrid1.Canvas.Brush.Color; {store the original color} if Column.FieldName = 'EmpNo' then {only do for the cell displaying EmpNo} if (Column.Field.AsInteger mod 2 <> 0) then begin DBGrid1.Canvas.Brush.Color := clGreen; DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State); DBGrid1.Canvas.Brush.Color := holdColor; end; end;
From: sraike@iconz.co.nz (Bill Raike)
I've had second thoughts and decided to post my DBGrid descendant that shows images, since it's such a small amount of code.Here it is:
{ // DBPICGRD.PAS (C) 1995 W. Raike // ALL RIGHTS RESERVED. // // DESCRIPTION: // Data-aware grid that can display graphic fields. // REVISION HISTORY: // 15/04/95 Created. W. Raike } unit DBPicGrd; interface uses DBGrids, DB, DBTables, Grids, WinTypes, Classes, Graphics; type TDBPicGrid = class(TDBGrid) protected procedure DrawDataCell(const Rect: TRect; Field: TField; State: TGridDrawState); override; public constructor Create(AOwner : TComponent); override; published property DefaultDrawing default False; end; procedure Register; implementation constructor TDBPicGrid.Create(AOwner : TComponent); begin inherited Create(AOwner); DefaultDrawing := False; end; procedure TDBPicGrid.DrawDataCell(const Rect: TRect; Field: TField; State: TGridDrawState); var bmp : TBitmap; begin with Canvas do begin FillRect(Rect); if Field is TGraphicField then try bmp := TBitmap.Create; bmp.Assign(Field); Draw(Rect.Left, Rect.Top, bmp); finally bmp.Free; end else TextOut(Rect.Left, Rect.Top, Field.Text); end; end; procedure Register; begin RegisterComponents('Custom', [TDBPicGrid]); end; end.
Is their a way to save the column order of a grid after the user reorders the columns via drag n drop.I resolved this problem time ago for my one application. Following code is adapted for you, not tested, but I think it works fine. It create, save and load configuration's file for order AND SIZE too of fields. I'm at your disposal for further into something.
procedure TMainForm.NewIni(const NomeIni: string); var F: System.Text; i: Byte; begin System.Assign(F, NomeIni); System.ReWrite(F); System.WriteLn(F, '[Campi_Ordine]'); for i:=1 to Table1.FieldCount do System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].FieldName); System.WriteLn(F, ''); System.WriteLn(F, '[Campi_Size]'); for i:=1 to Table1.FieldCount do System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].DisplayWidth); System.Close(F); end; procedure TMainForm.SaveIni(const FN: String); var Ini: TIniFile; i: Integer; begin NewIni(FN); Ini := TIniFile.Create(FN); with Ini do begin for i:=1 to Table1.FieldCount do begin S:= Table1.Fields[i-1].FieldName; WriteString('Campi_Ordine', 'Campo'+IntToStr(i), Table1.Fields[i-1].FieldName); WriteInteger('Campi_Size', 'Campo'+IntToStr(i), Table1.Fields[i-1].DisplayWidth); end; end; Ini.Free; end; procedure TMainForm.LoadIni(const FN: String); var Ini: TIniFile; i: Integer; j: Longint; S: String; function MyReadInteger(const Section, Ident: string): Longint; begin result := Ini.ReadInteger(Section, Ident, -1); if result=-1 then raise Exception.Create('Errore nel file di configurazione.'); end; function MyReadString(const Section, Ident: string): String; begin result := Ini.ReadString(Section, Ident, ''); if result='' then raise Exception.Create('Errore nel file di configurazione.'); end; begin Ini := TIniFile.Create(FN); try with Ini do begin for i:=1 to Table1.FieldCount do begin S:= MyReadString('Campi_Ordine', 'Campo'+IntToStr(i)); j:= MyReadInteger('Campi_Size', 'Campo'+IntToStr(i)); Table1.FieldByName(S).Index := i-1; Table1.FieldByName(S).DisplayWidth := j; end; end; finally Ini.Free; end; end;
I have a form. In that an Edit field, an SQL Query, a DBGrid and a Button. I can write into the edit, and the Query result will put into the grid. How can I resize the grid and the form to the fields size which appears in the grid. The fields Which I select with the query does not fill the full size of the grid or does not fit into it.You can change the size of a column at run-time by changing the DisplayWidth property of the underlying field object...
MyTableMyField.DisplayWidth := Length(MyTableMyField.value);
function NewTextWidth(fntFont : TFont; const sString : OpenString) : integer; var fntSave : TFont; begin result := 0; fntSave := Application.MainForm.Font; Application.MainForm.Font := fntFont; try result := Application.MainForm.Canvas.TextWidth(sString); finally Application.MainForm.Font := fntSave; end; end; { calculate the width of the grid needed to exactly display with no } { horizontal scrollbar and with no extra space between the last } { column and the vertical scrollbar. The grid's datasource must be } { properly set and the datasource's dataset must be properly set, } { though it need not be open. Note: this width includes the width } { of the vertical scrollbar, which changes based on screen } { resolution. These changes are compensated for. } function iCalcGridWidth ( dbg : TDBGrid { the grid to meaure } ) : integer; { the "exact" width } const cMEASURE_CHAR = '0'; iEXTRA_COL_PIX = 4; iINDICATOR_WIDE = 11; var i, iColumns, iColWidth, iTitleWidth, iCharWidth : integer; begin iColumns := 0; result := GetSystemMetrics(SM_CXVSCROLL); iCharWidth := NewTextWidth(dbg.Font, cMEASURE_CHAR); with dbg.dataSource.dataSet do for i := 0 to FieldCount - 1 do with Fields[i] do if visible then begin iColWidth := iCharWidth * DisplayWidth; if dgTitles in dbg.Options then begin iTitleWidth := NewTextWidth(dbg.TitleFont, DisplayLabel); if iColWidth < iTitleWidth then iColWidth := iTitleWidth; end; inc(iColumns, 1); inc(result, iColWidth + iEXTRA_COL_PIX); end; if dgIndicator in dbg.Options then begin inc(iColumns, 1); inc(result, iINDICATOR_WIDE); end; if dgColLines in dbg.Options then inc(result, iColumns) else inc(result, 1); end;
Has someone achieved dragging things from a DbGrid ? You can create your own descendant of TDBGrid (or TDBCustomGrid) and customize it to your needs.[Demian, demian@unix.horizontes.com.br]
Do an Origami with the code at the end of this message, save it as DBGrid.pas, and install it. You'll have a new component EDBGrid with two new events: OnMouseDown and OnMouseUp. I don't consider it a 'proprietary information': it's a Delphi Bug! Originally, this two events should have been part of the DBGrid component.
unit Dbgrid; interface uses DBGrids, Controls, Classes; type TEDBGrid = class(TDBGrid) private FOnMouseDown: TMouseEvent; FOnMouseUp: TMouseEvent; protected procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; published Property OnMouseDown : TMouseEvent read FOnMouseDown write FOnMouseDown ; Property OnMouseUp : TMouseEvent read FOnMouseUp write FOnMouseUp ; end; procedure Register; implementation procedure Register; begin RegisterComponents('Data Controls',[TEDBGrid]); end; procedure TEDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Assigned(FOnMouseDown) then FOnMouseDown(Self,Button,Shift,X,Y); inherited MouseDown(Button,Shift,X,Y); end; procedure TEDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if Assigned(FOnMouseUp) then FOnMouseUp(Self,Button,Shift,X,Y); inherited MouseUp(Button,Shift,X,Y); end; end.