Вы находитесь на странице: 1из 61

How to Display Only Selected Fields in the DbGrid Component From your Delphi Programming Guide See how

use the Fields Editor to define a list of some of the fields in a table that are to be displayed in the Grid. Even more: see how to specify the field order. Difficulty Level: Average Time Required: 10 minutes Here's How: 1. Start Delphi and select File | New Application... 2. In order to use this how to we need the form from one of the How Tos named: 'How To Create a Simple Database Display Form' (see: Related Features) 3. Open the Fields Editor by double-clicking the TTable component. By default, the list of fields is empty (all the fields are displayed) 4. Click Add to open a dialog box listing the fields in the Customer table. By default, all fields are selected. 5. Click CustNo to select it, then control-click to select the Company, Phone, and LastInvoiceDate fields. 6. Click OK to confirm your choices and close the dialog box. 7. In the form, the grid changes: instead of displaying all fields, it displays only the fields you selected. 8. Use the Fields Editor to change the field order as follows: Click LastInvoiceDate in the list of fields, then drag it to the third place in the list, between Company and Phone. 9. In the form, the grid changes to display columns in their new order. 10. Close the Fields Editor by choosing Close from the Control menu. 11. Press F9 to run the project. 12. Browse through the records. Tips: 1. By choosing fields in the Field Editor, we can tell a TTable component which fields to make available to the components that are linked to it. 2. For each field, delphi creates a corresponding TField component. TField components are invisible components that provide access to field values and display attributes. How to Prevent CTRL+DELETE INSIDE A DBGrid If you need to disable keyboard delete operation inside a DBGrid you'll need to disable the CTRL+DELETE key press: ~~~~~~~~~~ procedure Form1.DBGrid1KeyDown (Sender: TObject; var Key: Word; Shift:TShiftState) ; begin if (Shift = [ssCtrl]) and (Key = VK_DELETE) then Key := 0; {ignore} end; Get the line number of a selected row in a DBGrid If you want to get the line number of a selected row in a DBGrid component, here's what to do... Note: just substitute the Table1 component for whatever dataset you are using to provide data to your DBGrid. ~~~~~~~~~~~~~~~~~~~~~~~~~ ShowMessage(IntToStr(Table1.RecNo)) ; ~~~~~~~~~~~~~~~~~~~~~~~~~ Master-detail relationships ADO and Delphi Page 1: One-to-Many relationships - why and when?

More of this Feature Page 2: Realtions in Access

Master-detail data relationships are a fact of life for every Delphi Page 3: ADO masterdatabase developer; just as data relationships are a fundamental detail feature of relational databases. In the previous chapters of this course, we've invariably used only one table from our "demo" aboutdelphi.mdb MS Access database. Join the Discussion In real time database programming, the data in one table is related to the data in other tables. In general, tables can be related in one "Post your views and comments to this chapter of of three different ways: one-to-one, one-to-many or many-tothe free Delphi database many. This chapter will show you how to use one-to-many Programming Course" database relationships to deal effectively with the problem of Discuss! joining two database tables to present information. A one-to-many relationship, often referred to as a "master-detail" or "parent-child" relationship, is the most usual relationship Related Resources between two tables in a database. Common scenarios include customer/purchase data, free DB Course.TOC patient/medical-record data, and student/course-result data. For more Database articles example, each customer is associated with at least one order record. Valued customers have many order records involving significant sums and often a user needs to view one in connection with the other. In a one-to-many relationship, a record in Table A can have (none or one or) more than one matching record in Table B, but for every record in Table B there is exactly one record in Table A. A typical master-detail data browsing form displays the results of a one-to-many relationship, where one DBGrid displays (or set of data enabled controls) the results of the first or master table. It then tracks a selection in the first DBGrid to filter the results of a second table used to display the details of the selection in the second DBGrid. When working with the BDE and Delphi, the simplest way to assemble a master-detail form is to use the Database Form Wizard. Wizard simplifies the steps needed to create a tabular or dataentry form by use of an existing database, unfortunately it is designed to use the BDE versions of TTable and TQuery components. Everything the wizard does, we can do by hand. Since, through this course, we are working with the ADOExpress set of Delphi components, we'll need to set all the components step by step. Firstly we have to make sure that we have two tables in a master-detail relationship. Page 2: MS Access relationships and referential integrity

MS Access relationships Our focus will be on the following two tables: Customers and Orders. Both tables are a part of the DBDEMOS database that comes with Delphi. Since both tables are Paradox tables, we'll use the code from the previous article to port them to our working aboutdelphi.mdb MS Access database. Notice that when you port those tables to Access both of them have no index or primary key nor are they linked in any way in Access. The power in a relational database management system such as MS Access comes from its ability to quickly find and bring together information stored in separate tables. In order for MS Access to work most efficiently, each table in your database should include a field or set of fields that uniquely identifies each individual record stored in the table. If two tables are liked in a relation (of any kind) we should set that relation with the MS Access. Customers-Orders relation To set up the relationship, you add the field or fields that make up the primary key on the "one" side of the relationship to the table on the "many" side of the relationship. In our case, you would add the CustNo field from the Customers table to the Orders table, because one customer has many orders. Note that you have to set the CustNo in Customers to be the primary key for the table.

More of this Feature Page 1: Data relationships Page 3: ADO masterdetail

Join the Discussion "Post your views and comments to this chapter of the free Delphi database Programming Course" Discuss!

Related Resources free DB Course.TOC more Database articles

When creating a relation between two tables MS Access provides us with the Referential Integrity feature. This feature prevents adding records to a detail table for which there is no matching record in the master table. It will also cause the key fields in the detail table to be changed when the corresponding key fields in the master are changed - this is commonly referred to as a cascading update. The second options is to enable cascading deletes. This causes the deletion of all the related records in a detail table when the corresponding record in the master table gets deleted. These events occur automatically, requiring no intervention by a Delphi application using these tables. Now, when we have all the relations set up, we simply link few data components to create a master-detail data browsing Delphi form. Page 3: Master-detail with AdoExpress components and Delphi

More of this Feature Page 1: Data relationships Page 2: Realtions in Access Printer friendly version

Join the Discussion "Post your views and comments to this chapter of the free Delphi database Programming Course" Discuss!

Related Resources free DB Course.TOC more Database articles

About Poll This [071001] article is Awesome (5) Great (4) Ok (3) Not bad (2)

Setting up Master/Detail with ADOExpress Poor (1) Creating a master-detail data form is not to much complicated. Have an empty Delphi form, and just follow the steps: Submit Vote 1. Select the ADO page on the Component palette. Add two TADOTable components and one TADOConnection to a form. 2. Select the Data Access page on the Component palette. Add two TDataSource components to a form. Current Results 3. Select Data Controls page on the Component palette. Place two TDbGrid components on a form. Add two DBNavigator components, too. 4. Use the ADOConnection, the ConnectionString property, to link to the aboutdelphi.bdb MS Access database, as explained in the first chapter of this course. 5. Connect DBGrid1 with DataSource1, and DataSource1 with ADOTable1. This will be the master table. Connect DBNavigator1 with DataSource1. 6. Connect DBGrid2 with DataSource2, and DataSource2 with ADOTable2. This will be the detail table. Connect DBNavigator2 with DataSource2. 7. Set ADOTable1.TableName to point to the Customers table (master). 8. Set ADOTable2.TableName to point to the Orders table (detail). If you, at this moment, set the Active property of both ADOTable components to true, you'll notice that the entire Orders table is displayed - this is because we haven't set up the master-detail relationship yet. Your form should look something like:

MasterSource and MasterFields The MasterSource and MasterFields properties of the TADOTable component define masterdetail relationships in Delphi/ADO database applications. To create a master-detail relationships with Delphi, you simply need to set the detail table's MasterSource property to the DataSource of the master table and its MasterFields property to the chosen key field in the master table. In our case, first, set ADOTable2.MasterSource to be DataSource1. Second, activate the Field Link Designer window to set the MasterFields property: in the Detail Fields list box and the Master Fields list box select the CustNo field. Click Add and OK. These properties keep both tables in synchronization, so as you move through the Customers table, the Orders table will only move to records which match the key field (CustNo) in the Customers table. Each time you highlight a row and select a new customer, the second grid displays only the orders pertaining to that customer.

When you delete a record in a master table - all the corresponding record in the detail table are deleted. When you change a linked field in a record in a master table - the corresponding field in the detail table gets changed to (in as many records as needed). Simple as that! Stop. Note that creating a master-detail form with Delphi is not enough to support referential integrity features on two tables. Even though we can use methods described here to display two tables in a parent-child relation; if those two tables are not linked (one-to-many) within MS Access - cascading updates and deletes won't take place if you try to delete or update the "master" record. ADO Shaping Shaped recordsets are an alternative to master-detail relationships. Beginning with ADO 2.0, this method is available. Shaped recordsets allow the developer to retrieve data in a hierarchical fashion. The shaped recordset adds a special "field" that is actually a recordset unto itself. Essentially, data shaping gives you the ability to build hierarchical recordsets. For instance, a typical hierarchical recordset might consist of a parent recordset with several fields, one of which might be another recordset. For an example of the SHAPE command take a look at the shapedemo project that shiped with Delphi (in the Demos\Ado directory). You must specify the shaping provider in your connection string, by adding Provider=MSDataShape; to the beginning. SHAPE {select * from customer} APPEND ({select * from orders} AS Orders RELATE CustNo TO CustNo) Although it takes some time to master the SHAPE command that's used to create these queries, it can result in significantly smaller resultsets. Data shaping reduces the amount of traffic crossing a network, provides more flexibility when using aggregate functions, and reduces overhead when interfacing with leading-edge tools like XML. To the next chapter If you need any kind of help so far, please post to the Delphi Programming Forum where all the questions are answered and beginners are treated as experts . First page > Intro to master-detail relations > Page 1, 2, 3 DB Course Next Chapter >> >> New ... Access datatabse with Delphi Adjusting DBGrid column widths automatically Here's a handy method to automatically fix the size of TDBGrid columns (at run-time) to fit the DBGrid width (remove the unfilled space at the right edge of the grid; and consequently remove the horizontal scroll bar) when the user resizes the container containing the grid.

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Designed to enable a user to view and edit data in a tabular grid, the DBGrid provides various ways of customizing the way it Related Resources represents "its" data. I've already written a dozens of articles and DBGrid to the MAX tips that enable you to extend the functionality of the TDBGrid TDBGrid related articles component to the Max. and tips With so flexible component, a Delphi developer can always find new ways to make it more powerful - this article provides one handy customization. The Problem: One of the missing features of TDBGrid is that there is no option to automatically adjust the widths of specific columns to completely fit the grid's client width. When the user resizes the DBGrid component at run-time the column widths are not resized. Thus, if the width of the DBGrid is larger than the total width of all the columns, you'll get an empty area right after the last column. On the other hand, if the total width of all the columns is larger than the width of the DBGrid a horizontal scroll bar will appear.

The Solution In this article I'll present you with one handy procedure that fixes the widths of selective DBGrid columns when the grid is resized at run-time.

While writing code for my solution to the "problem", I was guided with the fact that in most cases (in my experience) only two-three columns in a DBGrid actually need to be auto-resized; all the other columns display some "static-width" data. For example, you can always specify fixed width for columns displaying values from data fields that are represented with TDateTimeField, TFloatField, TIntegerField and similar. What's more, you'll probably create (at design-time) persistent field components using the Fields editor, to specifies the fields in the dataset, their properties, and their ordering. Having a TField descendant object, you can use the Tag property to indicate that a particular column displaying values for that field must be auto-sized. This is the idea: if we want a column to auto-fit available space, assign an integer value for the TField descendant's Tag property that indicates the corresponding column's minimum width. Here's the code: "Smart" columns and the FixDBGridColumnsWidth procedure First, in the OnCreate event for the Form object containing the DBGrid, specify what columns need to be auto-resized by assigning a non-zero value for the Tag property of the corresponding TField object. procedure TForm1.FormCreate(Sender: TObject); begin //setup autoresizable columns by asigning //Minimm Width in the Tag property. //using fixed value: 40 px Table1.FieldByName('FirstName').Tag := 40;

//using variable value: width of the //default Column title text Table1.FieldByName('LastName').Tag := 4 + Canvas.TextWidth( Table1.FieldByName('LastName').DisplayName); end; In the above code, Table1 is a TTable component linked to a DataSource component which is linked with the DBGrid. The Table1.Table property points to the DBDemos Employee table. We have marked the columns displaying the values for FirstName and LastName fields to be auto-resizable. Next, call our FixDBGridColumnsWidth in the OnResize event handler for the Form: procedure TForm1.FormResize(Sender: TObject); begin FixDBGridColumnsWidth(DBGrid1); end; Note: this all makes sense if the Align property of the DBGrid includes one of following values: alTop, alBottom, alClient or alCustom. Finally, here's the FixDBGridColumnsWidth procedure's code: procedure FixDBGridColumnsWidth(const DBGrid: TDBGrid); var i : integer; TotWidth : integer; VarWidth : integer; ResizableColumnCount : integer; AColumn : TColumn; begin //total width of all columns before resize TotWidth := 0; //how to divide any extra space in the grid VarWidth := 0; //how many columns need to be auto-resized ResizableColumnCount := 0; for i := 0 to -1 + DBGrid.Columns.Count do begin TotWidth := TotWidth + DBGrid.Columns[i].Width; if DBGrid.Columns[i].Field.Tag <> 0 then Inc(ResizableColumnCount); end; //add 1px for the column separator line if dgColLines in DBGrid.Options then TotWidth := TotWidth + DBGrid.Columns.Count; //add indicator column width if dgIndicator in DBGrid.Options then TotWidth := TotWidth + IndicatorWidth; //width vale "left" VarWidth := DBGrid.ClientWidth - TotWidth; //Equally distribute VarWidth //to all auto-resizable columns if ResizableColumnCount > 0 then VarWidth := varWidth div ResizableColumnCount; for i := 0 to -1 + DBGrid.Columns.Count do begin

AColumn := DBGrid.Columns[i]; if AColumn.Field.Tag <> 0 then begin AColumn.Width := AColumn.Width + VarWidth; if AColumn.Width < AColumn.Field.Tag then AColumn.Width := AColumn.Field.Tag; end; end; end; (*FixDBGridColumnsWidth*) That's it. Once again: simple, tricky and powerful - as only Delphi can be! How to AutoFit the Columns in the TDBGrid Adjusting Column Widths to Fit the Widest Entry in a DBGrid Column By Zarko Gajic, About.com Guide See More About: tdbgrid delphi database programming drag and drop operations free dbgrid controls records in delphi Sponsored Links Delphi Programmers WantedThe Only Web IDE Custom Designed For Delphi Programmers. IDE is Freewww.morfik.com Winforms Grid ControlFast and flexible Grid and TreeList easy to use, plugable renderingwinforms.pfgrid.com Native Delphi PDF SDKBuilt by Delphi developers 500+ functions, royaltyfree!www.quickpdflibrary.com Delphi Ads Delphi DBGrid in Delphi Delphi Web Developer Delphi String Grid Delphi Software Support Sponsored Links Calendar for SilverlightCalendar and scheduling components for Silverlight applicationswww.mindfusion.eu Delphi RichEditDelphi RichEdit Replacement Native VCL - No Dlls or ActiveX!www.trichview.com Users accustomed to working with Microsoft Excel operate on data displayed in rows and columns. One of the Excel formatting features is the ability to automatically change the width of a column to fit the contents by double-clicking on the boundary to the right of a selected column title. Data manipulation using Microsoft Excel is similar to using Delphi's TDBGrid in database applications - where DBGrid is used to display and edit data from any kind of (supported) data source. AutoFit for DBGrid Columns Let's see how to add the "AutoFit" functionality to the Columns of a TDBGrid component. The discussion that follows explains adding Auto-Fit on DBGrid's Column Title Double-Click event. Note: to follow along, create a new Delphi project, drop a TDBGrid component (named "DBGrid1") on a form ("Form1") and setup any kind of data source to be displayed in the grid.
In most cases a column in a DBGrid is "connected" to a field in a dataset (through the DataSource property) - thus displaying the field's data for a particular dataset's "row". The width of a particular column in a DBGrid can be set at design-time using the Columns Editor. By default, a user can customize (at run-time) the width and the order of the columns by dragging and dropping column titles. To prevent a user from rearranging columns at run-time, you can either remove the goColResizing flag from the Options property or set the DragMode property to dmAutomatic (dmManual by default). Note: here's how to allow column resize but disable movement Here's how to to automatically fix the size of TDBGrid columns (at run-time) to fit the DBGrid width (remove the unfilled space at the right edge of the grid; and consequently remove the horizontal scroll bar) when the user resizes the container containing the grid. Here's how to Store and Retrieve DBGrid's Columns Order and Visibility

Adding "AutoFit" to DBGrid involves solving 3 problems: 1. Handling the OnDblClick event of the DBGrid for Column Titles, 2. Finding the double-clicked Column, 3. Calculating the widest entry and setting the Column Width. Note: To hold the reference to the column being selected for "autofit" a form-level record type variable is required, defined as:

type TColumnWidthHelper = record Index : integer; MaxWidth : integer; end; The "Index" field holds the index of the column in the Columns array. MaxWidth stores the width of the widest cell for the current "view". Column Title DoubleClicked? First we need to react on the Column Title double click event. While DBGrid exposes the OnTitleClick that fires on a single click, we cannot use it as we are interested in double-clicks :( The OnDblClick event looks promising - unfortunately it will fire even if user double clicks anywhere inside the Grid. Here's how to handle the OnDblClick event and make sure the double-click has occurred on a Column Title: procedure TForm1.DBGrid1DblClick(Sender: TObject) ; var mouseInGrid : TPoint; gridCoord: TGridCoord; begin //Convert "Screen" Mouse to "Grid" Mouse mouseInGrid := DBGrid1.ScreenToClient(Mouse.CursorPos) ; gridCoord := DBGrid1.MouseCoord(mouseInGrid.X, mouseInGrid.Y) ; //Column titles NOT displayed? if not (dgTitles in DBGrid1.Options) then Exit; //Titles displayed but we double-clicked on "regular" row? if gridCoord.Y <> 0 then Exit; //find Column index if dgIndicator in DBGrid1.Options then ColumnWidthHelper.Index := -1 + gridCoord.x else ColumnWidthHelper.Index := gridCoord.x; //Indicator Column? if ColumnWidthHelper.Index < 0 then Exit; ... // continues below When we are sure that a column title was double clicked we can find the widest entry in that column and set column width ... //continues from above ... ColumnWidthHelper.MaxWidth := -1; //"recalculate" ColumnWidthHelper.MaxWidth DBGrid1.Repaint; //"auto size" Column width DBGrid1.Columns[ColumnWidthHelper.Index].Width := 4 + ColumnWidthHelper.MaxWidth; end; The trick is in the DBGrid1.Repaint call. This forces the grid to repaint itself thus fireing the DrawColumnCell event (for all the visible rows). procedure TForm1.DBGrid1DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn;

State: TGridDrawState) ; begin //is this is the column we want to auto-size? if DataCol = ColumnWidthHelper.Index then begin //Column has field? if Assigned(Column.Field) then begin //find the widest string ColumnWidthHelper.MaxWidth := Max(ColumnWidthHelper.MaxWidth, DBGrid1.Canvas.TextWidth(Column.Field.DisplayText)) ; end; end; end; That's it! AutoFit in DBGrid! How to Highlight Non-Focused DBGrid Selected Row By Zarko Gajic, About.com Guide See More About: tdbgrid delphi database programming owner drawing programming master-detail database structures

Highlighted Selected Row in a Non-Focused DBGrid Sponsored Links Delphi Programmers WantedThe Only Web IDE Custom Designed For Delphi Programmers. IDE is Freewww.morfik.com Native Delphi PDF SDKBuilt by Delphi developers 500+ functions, royaltyfree!www.quickpdflibrary.com Winforms Grid ControlFast and flexible Grid and TreeList easy to use, plugable renderingwinforms.pfgrid.com In Delphi database applications one of the mostly used controls is the TDBGrid. TDBGrid displays and manipulates records from a dataset in a tabular grid. In scenarios where you have two DBGrid controls on the same form displaying data for a master-detail database relationship like Customer -> Orders, for a selected record/row in the "master" grid only related records are displayed in the "child" grid. When "dgRowSelect" is included in the Options property, when a user selects a row in a grid, the entire row gets selected. As soon as the grid looses the focus, the selected row does no longer appear selected (beside the little black triangle in the first column - if dgIndicator is included in Options). This leads to the next problem: when the user selected a child row in the "child" dbgrid, the related record in the "master" grid is no more selected - highlighted. To enhance user experience you might want to highlight the selected row in the DBGrid even when the DBGrid does NOT have the focus. You need to handle the OnDrawColumnCell event to make the selected record appear highlighted: //DBGrid1.OnDrawColumnCell event handler procedure TDBGridForm.DBGrid1DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer;

Column: TColumn; State: TGridDrawState) ; begin if NOT DBGrid1.Focused then begin if (gdSelected in State) then begin with DBGrid1.Canvas do begin Brush.Color := clHighlight; Font.Style := Font.Style + [fsBold]; Font.Color := clHighlightText; end; end; end; DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State) ; end; When the DBGrid1 (presume "master" grid) does not have the focus (presume switched to the "child" grid), we "paint" the selected row (gdSelected in State) as if the grid has the focus using the predefined clHighlight color for row background and clHighlightText for the font color. To make it even more visible, we also apply "Bold" for the font. Yes, simpler than it sounded :) How to Store and Retrieve DBGrid's Columns Order and Visibility ...and programmatically change Columns order By Zarko Gajic, About.com Guide See More About: tdbgrid delphi database programming drag and drop operations free dbgrid controls tstream descendants

Sponsored Links Delphi Apps For The WebBest Pascal Visual IDE & Compiler For Delphi Programmers. Free IDE.www.morfik.com Native Delphi PDF SDKBuilt by Delphi developers 500+ functions, royaltyfree!www.quickpdflibrary.com Components for DelphiMaking Data Entry & Manipulation Easier for Developers & EndUserswww.esbconsult.com Delphi Ads Delphi Delphi String Grid DBGrid in Delphi Delphi Web Developer Delphi Software Support Sponsored Links X-ray GridsIn Stock X-ray Grids, CR Cassettes, Call today Toll Free 855-853XRAYwww.xrayaccessorycorp.com Delphi RichEditDelphi RichEdit Replacement Native VCL - No Dlls or ActiveX!www.trichview.com In Delphi database applications, the TDBGrid component is commonly used to display (and optionally edit) the data displayed in the Grid.

DBGrid has a Columns property - a collection of TColumn objects representing all of the columns in a grid control. You can use the Columns editor to set up column attributes at design time, or use the Columns property of the grid to access the properties, events, and methods of TDBGridColumns at runtime. By default, a user can customize (at run-time) the width and the order of the columns by dragging and dropping column titles. Of course, reordering the columns of a grid does not affect the physical order of the fields in the dataset connected to the grid. The Options property of a DBGrid component has the goColResizing flag set on by default thus, allowing the user to resize and reorder the columns of a Grid. To prevent a user from rearranging columns at run-time, you can either remove the goColResizing flag from Options or set the DragMode property to dmAutomatic (dmManual by default). Note: here's how to allow column resize but disable movement Save and Load DBGrid Column Position and Width If you allow a user to customize DBGrid's appearance at run time (Columns width and position), when the form is closed and reopened, all the user changes are lost. For a better user experience, it would be ideal if your application could somehow store (and load when needed) all the changes the user has made to the Grid appearance. Two simple functions exposed by the TDBGridColumns class: LoadFromFile and SaveToFile help you save and restore the position and width of all the columns in the grid. Let's say you have a TDBGrid component named "DBGrid1" on a Delphi form, with resizing and reordering of columns enabled at run-time (again, this is by default). Saving Columns To save the columns order and width (along with other properties like Color, Visible, Title, etc) you simply make the following call: DBGrid1.Columns.SaveToFile('columns_settings') ; The SaveToFile method expects one parameter: the name of the file where TDBGridColumns object is saved. Note: a FileStream class is used internally by Delphi to save the TDBGridColumns object. Note: you could call this method before the Form hosting the grid is closed, ideally in the OnClose event. Restoring Columns When a Form is opened hosting a DBGrid, you could read the saved TDBGridColumns and "fix" the display. Only one line of code is required: DBGrid1.Columns.LoadFromFile('columns_settings') ; Note: In real world applications, you would need to take sure that the file named passed to both the SaveToFile and ReadFromFile is unique for each grid in the application. One idea is to construct the file named by concatenating a Form's name, DataSet's name and DBGrid's name. For example : "InvoicesForm_InvoicesQuery_DisplayGrid", where "InvoicesForm" is the name (Name property) of the Form, "InvoicesQuery" is the name od the TDataset object connected to "DisplayGrid" DBGrid. Programmatically Arranging and Hiding/Unhiding Grid Columns A sample application accompanying this article shows how to display all the columns in a CheckBoxList Delphi component to allow a user to hide or show a column by setting the Visible property of a TColumn object. Also, the code shows how to programmatically change the order of columns by rearranging the Items ("connected" to DBGird columns) of the Check Box List. As you will see, you just need to set the column's Index property to change its position at runtime. In the sample project, a two way sync is accomplished. When a user moves a column in the Grid, the item in the CheckBox list moves also. The OnColumnMoved event of a DBGrid is used here. Also, a user can rearrange the columns by dragging and dropping CheckBoxList items. Drawing an image in a cell of a Delphi DBGrid Here's how to place an image into a cell of a TDBGrid. Enrich the visual presentation of data in Delphi database applications.

Join the Discussion

Designed to enable a user to view and edit data in a tabular grid, the TDBGrid component provides various ways of customizing its appearance. With so flexible component, a Delphi developer can always find new ways to make data more attractive. A picture is worth a thousand words While adding colors to a DBGrid can produce some neat effects, if you really want to improve the visual representation of data in you applications, you might consider using images instead of the "boring" text. Here's a colored DBGrid, result of the code in the "Coloring DBGrid" article.

"Post your views, comments, questions and doubts to this article." Discuss!

Related Resources TDBGrid to the MAX Coloring DBGrid Graphics programming

What if you want to add some custom drawings (images) to a particular cell (for a particular data field) to provide even more attractive user interface:

Images in DBGrid Following the ideas set in the "Coloring DBGrid" article; let's see how to paint an image in a cell of a TDBGrid. Here's how the OnDrawColumnCell event handler for the DBGrid1 component looks: procedure TForm1.DBGrid1DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var bitmap : TBitmap; fixRect : TRect; bmpWidth : integer; imgIndex : integer; begin fixRect := Rect;

// customizing the 'LastName' field if Column.Field = EmployeeTableLastName then begin //adding some logic to grab the required image if EmployeeTableSalary.Value > 50000 then imgIndex := 0 else if EmployeeTableSalary.Value > 25000 then imgIndex := 1 else imgIndex := 2;

bitmap := TBitmap.Create; try //grab the image from the ImageList //(using the "Salary" field's value) ImageList1.GetBitmap(imgIndex,bitmap); //Fix the bitmap dimensions bmpWidth := (Rect.Bottom - Rect.Top); fixRect.Right := Rect.Left + bmpWidth; //draw the bitmap DBGrid1.Canvas.StretchDraw(fixRect,bitmap); finally bitmap.Free; end; // reset the output rectangle, // add space for the graphics fixRect := Rect; fixRect.Left := fixRect.Left + bmpWidth; end; //draw default text (fixed position) DBGrid1.DefaultDrawColumnCell( fixRect, DataCol, Column, State); end; To "paint" the above DBGrid, I've used a TImageList (ImageList1) component ("Win32" component palette tab) to store some (dummy for this test) images. Three images are added, each used to reflect the value of an employees salary.

The DBGrid is displaying data from the "Employee" table of a "DBDemos" database. The dataset object is named "EmployeeTable", the fields are persisted using the Fields editor -> the

TField object "connected" to the "LastName" field from the table is named "EmployeeTableLastName". That's all folks. Can Delphi paint it? Yes, Delphi can! ;) Mouse wheel behaves strangely with dbgrids - this code handler will correct this behavior. Just drop a TApplicationEvents ("Additional" tab on the Component Palette) component on a form and handle it's OnMessage event as: ~~~~~~~~~~~~~~~~~~~~~~~~~ procedure TForm1.ApplicationEvents1Message (var Msg: TMsg; var Handled: Boolean) ; var i: SmallInt; begin if Msg.message = WM_MOUSEWHEEL then begin Msg.message := WM_KEYDOWN; Msg.lParam := 0; i := HiWord(Msg.wParam) ; if i > 0 then Msg.wParam := VK_UP else Msg.wParam := VK_DOWN; Handled := False; end; end; ~~~~~~~~~~~~~~~~~~~~~~~~~ Note: This fixes the mouse wheel behavior not only for DBGrid-s but for all other list component (TListBox, TListView, etc). Displaying and editing MEMO fiels in Delphi's TDBGrid Here's how to show the contents of a MEMO (textual BLOB) field in a TDBGrid. Plus: how to enable editing of a TMemoField's contents using a separate window.

If you are developing database applications with tables containing MEMO fields, you'll notice that, by default, the TDBGrid component does not show the contents of a MEMO field inside a DBGrid cell. This article provides an idea of how to solve this TMemoField's issue (with a few more tricks)... TMemoField Memo fields are used to represent lengthy text or combinations of text and numbers. When building database applications using Delphi, the TMemoField object is used to represent a memo field in a dataset. TMemoField encapsulates the fundamental behavior common to fields that contain text data or arbitrary length. In most databases, the size of the Memo field is limited by the size of the database. While you can display the contents of a MEMO field in a TDBMemo component, by design the TDBGrid will only display "(Memo)" for the contents of such fields.

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources Using the TDBGrid component (tutorials, tips and tricks) Database development with Delphi

In order to actually display some text (from the MEMO field) in the appropriate DBGrid cell, you'll only need to add a simple line of code ... For the purpose of the next discussion, let's say you have a database table named "TestTable" with at least one MEMO field named "Data". OnGetText To show the contents of a MEMO field in the DBGrid, you need to attach a simple line of code in the field's OnGetText event. The easiest way to create the OnGetText event handler is to use the Fields editor at design time to create a persistent field component for the memo field: 1. Connect your TDataset descendant component (TTable, TQuery, TADOTable, TADOQuery ....) to the "TestTable" database table. 2. Double click the dataset component to open the Fields editor 3. Add the MEMO field to the list of persistent fields 4. Select the MEMO field in the Fields editor 5. Activate the Events tab in the Object Inspector 6. Double click the OnGetText event to create the event handler Add the next line of code (marked red): procedure TForm1.DBTableDataGetText( Sender: TField; var Text: String; DisplayText: Boolean); begin Text := Copy(DBTableData.AsString, 1, 50); end; Note: the dataset object is called "DBTable", the MEMO field is called "DATA", and therefore, by default, the TMemoField connected to the MEMO database field is called "DBTableData". By assigning DBTableData.AsString to the Text parameter of the OnGetText event, we tell Delphi to display ALL the text from the MEMO field in a DBGrid cell. You can also adapt the DisplayWidth of the memo field to a more appropriate value. Note: since MEMO fields can be quite BIG, it is a good idea to show only a part of it. In the above code, only the first 50 characters are displayed.

Editing on a separate form By default, the TDBGrid does not allow editing of MEMO fields. If you want to enable "in place" editing, you could add some code to react on a user action that shows a separate window that allows editing using a TMemo component. For the sake of simplicity we'll open an editing window when ENTER is pressed "on" a MEMO field in a DBGrid. Let's use the KeyDown event of a DBGrid component: procedure TForm1.DBGrid1KeyDown( Sender: TObject; var Key: Word; Shift: TShiftState);

begin if Key = VK_RETURN then begin if DBGrid1.SelectedField = DBTableData then with TMemoEditorForm.Create(nil) do try DBMemoEditor.Text := DBTableData.AsString; ShowModal; DBTable.Edit; DBTableData.AsString := DBMemoEditor.Text; finally Free; end; end; end; Note 1: the "TMemoEditorForm" is a secondary form containing only one component: "DBMemoEditor" (TMemo). Note 2: the "TMemoEditorForm" was removed from the "Auto-create forms" list in the Project Options dialog window.

Let's see what happens in the DBGrid1's KeyDown event handler: 1. When a user presses the ENTER key (we are comparing the Key parameter to the VK_RETURN virtual key code) [Key = VK_RETURN], 2. If the currently selected field in the DBGrid is our MEMO field (DBGrid1.SelectedField = DBTableData), 3. We create the TMemoEditorForm [TMemoEditorForm.Create(nil)], 4. Send the value of the MEMO field to the TMemo component [DBMemoEditor.Text := DBTableData.AsString], 5. Display the form modally [ShowModal], 6. When a user finishes with editing and closes the form, we need to put the dataste into the Edit mode [DBTable.Edit], 7. In order to be able to assign the edited value back to our MEMO field [DBTableData.AsString := DBMemoEditor.Text]. Note: if you are looking for more TDBGrid related articles and usage tips, be sure to visit: "TDBGrid to the MAX" tips collection. Match column title alignments with field alignments in DBGrid By Zarko Gajic, About.com Guide See More About: tdbgrid user interface design using db-aware controls tfield objects tdataset Sponsored Links Delphi Programmers WantedThe Only Web IDE Custom Designed For Delphi Programmers. IDE is Freewww.morfik.com PDF component for .NETCreate, merge, fill PDF documents from Winforms/ASP.NET appswww.o2sol.com

Microwave ComponentsWide Variety of Couplers, Dividers Isolators & Circulators. In Stock!www.MCLI.com Delphi Ads Delphi Delphi String Grid Delphi Web Developer DBGrid in Delphi Delphi PDF Component (Continued from Page 1) Alignement of each field's data in a DBGrid can be set using the Alignement property. Here's how to match Column title alignement with field alignement. Usage: AlignTitles(DBGrid1) ; ~~~~~~~~~~~~~~~~~~~~~~~~~ procedure AlignTitles(Grid : DBGrid) ; var j, k: Integer; begin with Grid.DataSource.DataSet do for j := 0 to -1 + DBGrid1.FieldCount do for k := 0 to -1 + FieldCount do if Fields[k].FieldName = Grid.Fields[j].FieldName then Grid.Columns[j].Title.Alignment := Fields[k].Alignment; end; (* AlignTitles *) ~~~~~~~~~~~~~~~~~~~~~~~~~ DBGrid with MultiSelect Multiple row selection in Delphi DBGrid - providing the ability to select multiple records within the grid.

Delphi's DBGrid is one of the mostly used DB aware component in database related applications. It's main purpose is to display and Join the Discussion enable users of your application to manipulate records from a "Post your views, dataset in a tabular grid. comments, questions and One of the lesser known features of the DBGrid component is that doubts to this article." you can set it to allow multiple row selection. What this means is Discuss! that the users of your application will have the ability to select multiple records (rows) from the dataset connected to grid. Allowing multiple selections Related Resources To enable multiple selection, you only need to set the free DB Course.TOC dgMultiSelect element to True in the Options property. When Coloring DBGrid dgMultiSelect is True, users can select multiple rows in a grid Using Delphi DB using the following techniques: components - Ctrl + Mouse clicks more Database articles - Shift + Arrow Keys The (rows) records selected are represented as bookmarks and are stored in the grid's SelectedRows property. Note that SelectedRows is only meaningful when the Options property includes dgMultiSelect and dgRowSelect (both are set to True). On the other hand, when using dgRowSelect (individual cells cannot be selected) the user won't be able to edit records directly through the grid - dgEditing is automatically set to False. The SelectedRows property is an object of type TBookmarkList. We can use the SelectedRows property to, for example: get the number of rows selected, clear the selection (unselect), delete all the selected records, check whether a particular record is selected. To set dgMultiSelect to True you can either use the Object Inspector (design time) or at the run time a command like: DBGrid1.Options:= DBGrid1.Options + [dgMultiSelect]; When to use dgMultiSelect? In situations when you need an option to select random records and, for example, sum a value of a specific field in all selected records. Other situations involve deletion of multiple records or

similar... An example Following the concepts set in the Beginners Guide to Delphi Database Programming, example below uses ADO components (AdoQuery connected to ADOConnection, DBGrid connected to AdoQuery over DataSource) to display the records from a database table in a DBGrid component. If you do not know how to display records from a database table (or query) in a DBGrid component, please explore the "Connecting to a database" chapter. procedure TForm1.btnDoSumClick(Sender: TObject); var i: Integer; sum : Single; begin if DBGrid1.SelectedRows.Count > 0 then begin sum := 0; with DBGrid1.DataSource.DataSet do begin for i := 0 to DBGrid1.SelectedRows.Count-1 do begin GotoBookmark(Pointer(DBGrid1.SelectedRows.Items[i])); sum:= sum + AdoQuery1.FieldByName('Size').AsFloat; end; end; edSizeSum.Text := FloatToStr(sum); end end; The code above uses multiple selection to get the sum of the values in the "Size" field. The picture shows the code in action:

That's it. Simple and powerful. If you want to select the entire DBGrid from code, you can use this code snippet. To see how to add colors to DBGrid, see the Coloring DBGrid article; of course don't miss the "Using the DBGrid" article collection! A Beginner's Guide to Delphi Database Programming >> >> the TOC DBGrid with MultiSelect Multiple row selection in Delphi DBGrid - providing the ability to select multiple records within the grid.

Join the Discussion

Delphi's DBGrid is one of the mostly used DB aware component in "Post your views, database related applications. It's main purpose is to display and comments, questions and enable users of your application to manipulate records from a doubts to this article." dataset in a tabular grid. Discuss! One of the lesser known features of the DBGrid component is that you can set it to allow multiple row selection. What this means is that the users of your application will have the ability to select Related Resources multiple records (rows) from the dataset connected to grid. free DB Course.TOC Allowing multiple selections Coloring DBGrid To enable multiple selection, you only need to set the Using Delphi DB dgMultiSelect element to True in the Options property. When components dgMultiSelect is True, users can select multiple rows in a grid more Database articles using the following techniques: - Ctrl + Mouse clicks - Shift + Arrow Keys The (rows) records selected are represented as bookmarks and are stored in the grid's SelectedRows property. Note that SelectedRows is only meaningful when the Options property includes dgMultiSelect and dgRowSelect (both are set to True). On the other hand, when using dgRowSelect (individual cells cannot be selected) the user won't be able to edit records directly through the grid - dgEditing is automatically set to False. The SelectedRows property is an object of type TBookmarkList. We can use the SelectedRows property to, for example: get the number of rows selected, clear the selection (unselect), delete all the selected records, check whether a particular record is selected. To set dgMultiSelect to True you can either use the Object Inspector (design time) or at the run time a command like: DBGrid1.Options:= DBGrid1.Options + [dgMultiSelect]; When to use dgMultiSelect? In situations when you need an option to select random records and, for example, sum a value of a specific field in all selected records. Other situations involve deletion of multiple records or similar... An example Following the concepts set in the Beginners Guide to Delphi Database Programming, example below uses ADO components (AdoQuery connected to ADOConnection, DBGrid connected to AdoQuery over DataSource) to display the records from a database table in a DBGrid component. If you do not know how to display records from a database table (or query) in a DBGrid component, please explore the "Connecting to a database" chapter. procedure TForm1.btnDoSumClick(Sender: TObject); var i: Integer; sum : Single; begin if DBGrid1.SelectedRows.Count > 0 then begin sum := 0; with DBGrid1.DataSource.DataSet do begin for i := 0 to DBGrid1.SelectedRows.Count-1 do begin GotoBookmark(Pointer(DBGrid1.SelectedRows.Items[i])); sum:= sum + AdoQuery1.FieldByName('Size').AsFloat; end; end; edSizeSum.Text := FloatToStr(sum); end end; The code above uses multiple selection to get the sum of the values in the "Size" field. The

picture shows the code in action:

That's it. Simple and powerful. If you want to select the entire DBGrid from code, you can use this code snippet. To see how to add colors to DBGrid, see the Coloring DBGrid article; of course don't miss the "Using the DBGrid" article collection! A Beginner's Guide to Delphi Database Programming >> >> the TOC How to select all rows (records) in a DBGrid from code By Zarko Gajic, About.com Guide See More About: tdbgrid delphi database programming delphi selection controls using db-aware controls Sponsored Links Static Code AnalysisLeader in advanced code analysis Multiple languages Free Evalwww.parasoft.com/Java_Static Delphi Apps For The WebBest Pascal Visual IDE & Compiler For Delphi Programmers. Free IDE.www.morfik.com Microwave ComponentsWide Variety of Couplers, Dividers Isolators & Circulators. In Stock!www.MCLI.com Delphi Ads Delphi Delphi Software Support DBGrid in Delphi Delphi String Grid Delphi Web Developer (Continued from Page 4) When the DBGrid's Options property includes dgRowSelect and dgMultiSelect, users can select multiple rows in a grid. Here's how to select the entire DBGrid from code: ~~~~~~~~~~~~~~~~~~~~~~~~~ procedure DBGridSelectAll(AGrid: TDBGrid) ; begin AGrid.SelectedRows.Clear; with AGrid.DataSource.DataSet do begin DisableControls; First; try while not EOF do begin AGrid.SelectedRows.CurrentRowSelected := True; Next; end; finally EnableControls; end; end;

end; //Usage: //DBGridSelectAll(DBGrid1) ; ~~~~~~~~~~~~~~~~~~~~~~~~~ Sorting records in Delphi DBGrid by Clicking on Column Title How to sort records in Delphi DbGrid by clicking on the column title. Plus: how to change the appearance of the selected column title to reflect the sort order. Even more: how to change the cursor when moving over the DBGrid column titles.

Delphi DBGrid ... what a powerful component! If you are developing data aware applications you are probably using the Join the Discussion DBGrid component every day. "Post your views, In this article you'll see how to add some more nitty gritty features comments, questions and to your database applications - your users will love it! doubts to this article." Following the concepts set in the Beginners Guide to Delphi Discuss! Database Programming, examples below use ADO components (AdoQuery/AdoTable connected to ADOConnection, DBGrid connected to AdoQuery over DataSource) to display the records from a database table in a DBGrid component. All the component Related Resources names were left as Delphi named them when dropped on the form free DB Course.TOC Coloring DBGrid (DBGrid1, ADOQuery1, AdoTable1, ...) Multiple row selection in If you do not know how to display records from a database table (or query) in a DBGrid component, please explore the "Connecting DBGrid Using Delphi DB to a database" chapter. components Mouse moves over DBGrid title area? more Database articles First, let's see how to change the mouse pointer while it moves over the DBGrid title area. All you have to do is to add the code to the OnMouseMove event for the DBGrid component. The code below simply uses the MouseCoord property of the DBGrid component to "calculate" where the mouse pointer is - if over DGBrid title area, the pt.y equals 0, which is the first row in the DBGrid - the title area (displaying column/field titles). procedure TForm1.DBGrid1MouseMove (Sender: TObject; Shift: TShiftState; X, Y: Integer); var pt: TGridcoord; begin pt:= DBGrid1.MouseCoord(x, y); if pt.y=0 then DBGrid1.Cursor:=crHandPoint else DBGrid1.Cursor:=crDefault; end; Sort on column click? Change font of the sorted column title? The next piece of code is my favorite. If you are using ADO approach to Delphi database development, and want to sort the records in the dataset, you need to set the Sort property of your AdoDataset (ADOQuery, AdoTable). The Sort property is the widestring value indicating the "ORDER BY" part of the standard SQL query - of course you do not need to write the SQL query to be able to use the Sort property. You simply set the Sort property to the name of a single field or to a comma-separated list of fields, each following the sort order. Something like: ADOTable1.Sort := 'Year DESC, ArticleDate ASC' The OnTitleClick event of the DBGrid component has a Column parameter indicating the Column the user has clicked on. Now, each Column (object of type TColumn) has a Field property indicating the Field (TField) represented by the Column - and the Field in its FieldName property holds the name of the field in the underlying dataset. Therefore, to sort an ADO dataset

by field/column a simple line can be used: with TCustomADODataSet(DBGrid1.DataSource.DataSet) do Sort := Column.Field.FieldName; // + ' ASC' or ' DESC' Below you can find the code for the OnTitleClick even handler that sorts the records by column click. The code, as always, extends the idea. First we want to, in some way, mark the column that is currently used for sort order. Next, if we click on a column title and the dataset is already sorted by that column we want to change the sort order from ASC (ascending) to DESC (descending) and vice versa. And finally, when we sort the dataset by another column we want to remove the mark from the previously selected column. For the sake of simplicity, to mark the column that "sorts" the records we'll simply change the font style of the column title to Bold, and remove it when dataset is sorted using another column. procedure TForm1.DBGrid1TitleClick(Column: TColumn); {$J+} const PreviousColumnIndex : integer = -1; {$J-} begin if DBGrid1.DataSource.DataSet is TCustomADODataSet then with TCustomADODataSet(DBGrid1.DataSource.DataSet) do begin try DBGrid1.Columns[PreviousColumnIndex].title.Font.Style := DBGrid1.Columns[PreviousColumnIndex].title.Font.Style - [fsBold]; except end; Column.title.Font.Style := Column.title.Font.Style + [fsBold]; PreviousColumnIndex := Column.Index; if (Pos(Column.Field.FieldName, Sort) = 1) and (Pos(' DESC', Sort)= 0) then Sort := Column.Field.FieldName + ' DESC' else Sort := Column.Field.FieldName + ' ASC'; end; end; Note: the code above uses typed constants to preserve the value of the previously "selected" column for sort order.

That's it. Simple and powerful - this is why you have picked Delphi. If you want more articles describing how to use various Delphi data aware component, go for Using Delphi DB components. Accessing and managing MS Excel sheets with Delphi Page 1: Methods for transferring data between Excel and Delphi. How to connect to Excel with ADO and Delphi.

How to retrieve, display and edit Microsoft Excel spreadsheets Win prizes by sharing with ADO (dbGO) and Delphi. This step-by-step article describes code! how to connect to Excel, retrieve sheet data, and enable editing of Do you have some Delphi data (using the DBGrid). You'll also find a list of most common code you want to share? errors (and how to deal with them) that might pop up in the Are you interested in process. winning a prize for your Here's what you will learn about in this (lengthy) article: work? Methods for transferring data between Excel and Delphi. Delphi Programming How to connect to Excel with ADO and Delphi. Quickies Contest Creating an Excel spreadsheet editor using Delphi and ADO Retrieving the data from Excel. How to reference a table More of this Feature (or range) in an Excel workbook. Page 2: Creating an Excel A discussion on Excel field (column) types spreadsheet editor How to modify Excel sheets: edit, add, and delete rows. Page 3: Retrieving the Transferring data from a Delphi application to Excel. How to create a worksheet, and fill it with "custom" (from an Access data from Excel Page 4: Excel field database) data. (column) types Connect to: MS Excel Page 5: Modify Excel Microsoft Excel is a powerful spreadsheet calculator and data sheets analysis tool. Since rows and columns of an Excel worksheet closely relate to the rows and columns of a database table, many Page 6: Transfering data to Excel developers find appropriate to transport their data into an Excel workbook for analysis purposes; and retrieve data back to the Page 7: Full source code application afterwards. The most commonly used approach to data exchange between your application and Excel is Automation. Automation provides a Join the Discussion way to read Excel data using the Excel Object Model to dive into the worksheet, extract its data, and display it inside a grid-like "Post your views, component, namely DBGrid or StringGrid. Automation gives you comments, questions and the greatest flexibility for locating the data in the workbook as well doubts to this article." as the ability to format the worksheet and make various settings at Discuss! run time. To transfer your data to and from Excel without Automation, you can use other methods such as: Related Resources Write data into a comma-delimited text file, and let Excel parse A Beginners Guide to the file into cells, ADO programming Transfer data using DDE (Dynamic Data Exchange), Automation with Excel and Transfer your data to (and from) a worksheet using ADO. Delphi Data transfer using ADO Since Excel is JET OLE DB compliant, you can connect to it with Delphi using ADO (dbGO or AdoExpress) then retrieve the From Other Guides worksheet's data into an ADO dataset by issuing a SQL query just like you would open a dataset against any database table. In Wotking with MS Excel this way, all the methods and features of the ADODataset object are available to process the Excel data. In other words, using the ADO components, you can build an application that can use an Excel workbook as the database. Another important fact is that Excel is an out-of-process ActiveX server. ADO runs in-process, and saves the overhead of costly out-of-process calls. When you connect to Excel using ADO, you can only exchange raw data to and from a workbook. ADO connection cannot be used for sheet formatting or implementing formulas to cells. However, if you transfer your data to a worksheet that is pre-formatted, the format is maintained. After the data is inserted from your application to Excel, you can carry out any conditional formatting using a (pre-recorded) macro in the worksheet. You can connect to Excel using ADO with the two OLE DB Providers that are a part of MDAC: Microsoft Jet OLE DB Provider or Microsoft OLE DB Provider for ODBC Drivers. This article will focus on Jet OLE DB Provider which can be used to access data in Excel workbooks, through installable Indexed Sequential Access Method (ISAM) drivers. If you are new to ADO, I suggest you to first read the "Beginners Course to Delphi ADO Database Programming"

The ConnectionString Magic The ConnectionString property tells ADO how to connect to the datasource. The value used for ConnectionString consists of one or more arguments ADO uses to establish the connection. In Delphi, the TADOConnection component encapsulates the ADO connection object; it can be shared by multiple ADO dataset (TADOTable, TADOQuery, ...) components through their Connection properties. In order to connect to Excel, a valid connection string involves only two (additional) pieces of information: the full path to the workbook, and the Excel file version. In other words, a legitimate connection string could look like: ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\MyWorkBooks\myDataBook.xls;Extended Properties=Excel 8.0;'; When you want to connect to an external database format supported by the Jet, you need to set the extended properties for the connection. In our case, when connecting to Excel "database", extended properties are used to set the Excel file version. For an Excel95 workbook this value is "Excel 5.0" (without the quotes), for versions Excel 97, Excel 2000, Excel 2002 or ExcelXP the value is "Excel 8.0". Caution: you must use the Jet 4.0 Provider, since Jet 3.5 does not support the ISAM drivers. If you set the Jet Provider to version 3.5 you'll receive the "Couldn't find installable ISAM." error message. Another Jet extended property is "HDR=". "HDR=Yes" means that there is a header row in the range, so the Jet will not include the first row of the selection into the dataset. If "HDR=No" is specified, then the provider will include the first row of the range (or named range) into the dataset. The first row in a range is considered to be the header row by default ("HDR=Yes"), therefore if you have column heading you do not need to specify this value. If you do not have column headings, you need to specify "HDR=No"; All set. This is the part where things become interesting; we are now ready for some code. Let's see how to create a simple Excel Spreadsheet editor using Delphi and ADO... Note: even if you lack knowledge on ADO and Jet programming, you should proceed. As you will see editing an Excel workbook is simple as editing data from any "standard" database! Accessing and managing MS Excel sheets with Delphi Page 2: Creating an Excel spreadsheet editor using Delphi and ADO

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 1: Connect to: MS Excel Page 3: Retrieving the data from Excel Page 4: Excel field (column) types Page 5: Modify Excel sheets Page 6: Transfering data to Excel Page 7: Full source code

As discussed on the first page of this article, one way to read and edit Excel sheets using Delphi is through ADO. Setting up a project Join the Discussion Start Delphi, this by default creates a new Windows application "Post your views, hosting one Delphi form (Form1:TForm). For the sake of simplicity, comments, questions and we'll start from here by adding new components to the form. This doubts to this article." is what you will need for the visual part of the "Excel spreadsheet Discuss! editor"... A button (BitBtn1:TBitBtn), a combo box (ComboBox1:TComboBox), three labels (Label1:TLable, Related Resources Label2:TLabel, Label3:TLabel), two edits (Edit1:TEdit, A Beginners Guide to Edit2:Tedit), a list box (ListBox1:TListBox) and a status bar ADO programming (StatusBar1:TStatusBar). Note the "(Name:ClassName)" annotation, all the components are Automation with Excel and Delphi left with their default names when dropped on the form. We'll need several more components, to be precise several DB and ADO related components. Go on and add a dbgrid (DBGrid1:TDBGrid), a database navigator (DBNavigator1:TDBnavigator), and the three "standard" components we need when working with ADO: the connection object (ADOConnection1:TADOConnection1), a datasource (DataSource1:TDataSource) and a dataset descendant object (ADOQuery1:TAdoQuery). When you have finished, this is how your form should look like at design time (I've placed some default text into edits using the Object Inspector):

Note the control names in red. This is the idea: Edit1 (where the location to the Excel file is specified) is used to set the data source (Excel workbook). Edit2 is used to specify a query to run against the workbook. ComboBox1 will display the workbook sheet names. ListBox1 will give details about Field objects. StatusBar1 will show any error that pops up in the process. DBGrid and DBNavigator are in control of editing and navigating through the Excel data. If you need more-user friendly DBNavigator, check the "Customizing the DBNavigator" article; for DBGrid related articles, go see "Using the DBGrid - tips and tricks" Connectivity First, we need to connect all the DB related components together. You can "connect" the components using the Object Inspector, but I've decided to do all the work from code. This is done in the OnCreate event handler for the Form1: procedure TForm1.FormCreate(Sender: TObject); begin AdoConnection1.LoginPrompt := False; AdoQuery1.Connection := AdoConnection1; DataSource1.DataSet := AdoQuery1; DBGrid1.DataSource := DataSource1;

DBNavigator1.DataSource Application.OnException end;

:= DataSource1; := DisplayException;

Since there might be some errors popping up the screen while examining various Delphi+Excel+ADO techniques I decided to handle any possible error in one common place. The Application.OnException event is the right candidate for the job, on any exception the DisplayException gets called. This is how the DisplayException procedure looks like (nothing special, just displays the error message in the StatusBar1 control): procedure TForm1.DisplayException(Sender: TObject; E: Exception); begin StatusBar1.SimpleText := E.Message; end; The connection Now, we go for the connection part. Since we have Edit1 holding the file name of the Excel workbook we are using as the data source, we'll place all the code related to ADOConnection1 in a separate procedure. procedure TForm1.ConnectToExcel; var strConn : widestring; begin strConn:='Provider=Microsoft.Jet.OLEDB.4.0;' + 'Data Source=' + Edit1.Text + ';' + 'Extended Properties=Excel 8.0;'; AdoConnection1.Connected:=False; AdoConnection1.ConnectionString:=strConn; try AdoConnection1.Open; AdoConnection1.GetTableNames(ComboBox1.Items,True); except ShowMessage('Unable to connect to Excel, make sure the workbook ' + Edit1.Text + ' exist!'); raise; end; end;(*ConnectToExcel*) Note: The easiest way to create this procedure is to write its header in the private part of the form declaration and hit CTRL+SHIFT+C - Delphi adds skeleton code (as a result of the Code Completion feature) in the implementation section, for you to edit. Here's what the ConnectToExcel procedure does: first we build the valid connection string using Edit1 edit box since it holds the full file name to the Excel workbook. We close the connection, set the new connection string, and try to connect. In case of an error a message is displayed and the error is re-raised - enabling Application.OnException to be called and the error message displayed in the status bar. Caution: if you are trying to connect to a password-protected workbook, the connection will fail. Even if you know the password, and specify it in the ConnectionString property, and unless the workbook is already open in Excel, you'll receive the "Could not decrypt file." error - and won't be able to retrieve data. Ok, suppose that we have connected to Excel. What now? How do you know the "table" names? Or better yet what are tables (database table objects) in an Excel sheet?! As you will see on the next page, there are several ways you can specify a table (or range) in your select query statement... Accessing and managing MS Excel sheets with Delphi Page 3: Retrieving the data from Excel. How to reference a table (or range) in an Excel workbook.

Since now you know how to connect to an Excel workbook using Delphi and ADO, it's time to discuss how to retrieve sheet data. Once connected, we are just one step away from retrieving the worksheet's data. First, we need to specify exactly what data we are trying to retrieve. In the most basic case this will be the data in the worksheet. "Tables" in an Excel workbook A "database table" in Excel is simply a range (with a defined name). There are numerus ways you can reference a range in an Excel workbook. First, recall that the GetTableNames method of the ADOConnection object populates a string list with the names of tables in the database. In our case, the database is an Excel workbook - surpassingly the GetTableNames should return the sheet names of an Excel workbook - thus the sheets are merely "datase" tables. The ConnectToExcel procedure in our simple Excel spreadsheet editor has a line of code that populates the ComboBox1 component with sheet (table) names: AdoConnection1.GetTableNames(ComboBox1.Items,True); So, how do you specify the range, in the SQL statement's FROM part, you want to grab from Excel? Here's how: Use the sheet name followed by a dollar sign ($) and surrounded by square brackets, like "[Sheet1$]", Specify a named range, like "MyRange", Use an unnamed range by appending standard Excel row/column notation to the end of the sheet name, like "[Sheet1$A5:D20]". Before we try this using Delphi, let's first create a sample workbook (or simply download it here): 1. Start Excel, this by default creates a new workbook with 3 sheets (depending on your Excel version). 2. Save the workbook as "AdoDelphiExcel.xls" 3. Add some data to the work sheet "Sheet1", like in the picture below:

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 1: Connect to: MS Excel Page 2: Creating an Excel spreadsheet editor Page 4: Excel field (column) types Page 5: Modify Excel sheets Page 6: Transfering data to Excel Page 7: Full source code

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources A Beginners Guide to ADO programming Automation with Excel and Delphi

4.

Note that we have 5 columns (of which 4 have a heading). The fist column (Birth date), holds a date value. The second column (Name) holds some string data. The third column (Points) holds some integer data. The fourth column holds some real data - the column title was left blank intentionally. Finally the last column named "Formula" holds some calculated data. The value of the cell, let's say, E2 is "=D2*100/C2"; the rest of the cells in the "Formula" column use the same calculation. Ok. It's time to retrieve this Excel workbook's data into a Delphi DBGrid. "Select * From Excel" In our sample Excel spreadsheet editor Delphi project, the procedure that fetches the data from

an Excel workbook is defined as: procedure TForm1.FetchData; begin StatusBar1.SimpleText:=''; ConnectToExcel; AdoQuery1.Close; AdoQuery1.SQL.Text:=Edit2.Text; try AdoQuery1.Open; except ShowMessage('Unable to read data from Excel, make sure the query ' + Edit1.Text + ' is meaningful!'); raise; end; end; Again, the easiest way to create this procedure is to write its header in the private part of the form declaration and hit CTRL+SHIFT+C. Note the the SQL.Text property of the AdoQuery1 component - we use the query specified in the Edit2 edit box component. First we call the ConnectToExcel procedure, to take care of the situation when the file name of the Excel workbook, we are interested in, has changed. The dataset (AdoQuery1) is closed, and the SQL SELECT statement is built to retrieve the data. Finally, the Open method is called, so the DB aware component linked to ADOQuery1 (through a DataSource1) can be populated with data. To actually fetch the data, we add a call to the FetchData procedure in BitBtn1's OnClick event handler: procedure TForm1.BitBtn1Click(Sender: TObject); begin FetchData; //explained later GetFieldInfo; end; And, finally, if everything is ok, after you run your project, you should see a Delphi application displaying data from an Excel worksheet:

Caution: A query string like this one: "Select * From [Sheet1]", is not valid. In other words, if you are using a workbook sheet's name for the table name in the Select statement, and you omit the dollar sign and the brackets, or just the dollar sign, you could receive the "... engine could not

find the specified object", the "Syntax error in FROM clause." or the "Syntax error in query. Incomplete query clause." error message. A "story" about columns As discussed before, when connecting to Excel workbooks using ADO, the first row in a specified range is, by default, considered to hold the field (column) names - all subsequent rows are supposed to contain the records. When the first row does not contain headers, ADO automatically names the fields for you (where "F1" is the name of the first field, F2 represents the second field, and so on). This is why, in our example, the unnamed column's header cell, was assigned the name "F4". Now, that you know how to retrieve data from an Excel workbook, and how ADO names fields, it's time for a discussion on retrieved field (column) types... Page 4: A discussion on Excel field (column) types

After we have finished the discussion on connecting to an Excel workbook, retrieving sheet data, it's time for a quick look into Excel data types (as provided by ADO). This page also discusses the editing of an Excel work sheet using Ado and Delphi. A "story" about column data types In traditional database application, Delphi provides us with a TField object - it encapsulates the fundamental behavior common to all field components. Field components are non-visual objects that represent fields of the dataset at run (and design) time. Contrary to the traditional (relational database) approach, when using Excel as the datasource for you application, there is no direct way to specify the data types for columns in retrieved Excel ranges (sheets). Excel as a data "provider", does not provide ADO with the information about the data it contains. The Jet must scan through the referenced range to guess the type of data it caries (the formatting of the cells does not count). The number of rows Jet scans is 8, by default. If you need more rows to be examined (before field types are determined), you can add a "MaxScanRows=X" to the connection string, where X specifies the value from 0 to sixteen 16. A value of zero tells the Jet to scan all existing rows. Caution: Some problems may occur if you have mixed string values with numeric values "under" the same column. If this is the case, the Jet could return mixed null-string or null-number values. Now, that we know possible problems, let's see what field types are available when working with Excel data. Excel data types The easies way to see what data types the retrieved Excel range consist of is to use the properties and methods of Delphi's TField object. In our project, this is done in the GetFieldInfo procedure (I suppose you remember we are creating an Excel spreadsheet editor using Delphi and ADO). Recall that the GetFieldInfo is called inside the button's "Re-fetch" OnClick event handler. This is how the GetFieldInfo procedure looks:

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 1: Connect to: MS Excel Page 2: Creating an Excel spreadsheet editor Page 3: Retrieving the data from Excel Page 5: Modify Excel sheets Page 6: Transfering data to Excel Page 7: Full source code

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources A Beginners Guide to ADO programming Automation with Excel and Delphi

uses typinfo; //don't forget ...

procedure TForm1.GetFieldInfo; var i : integer; ft : TFieldType; sft : string; fname : string; begin ListBox1.Clear; for i := 0 to AdoQuery1.Fields.Count - 1 do begin ft := AdoQuery1.Fields[i].DataType; sft := GetEnumName(TypeInfo(TFieldType), Integer(ft)); fname:= AdoQuery1.Fields[i].FieldName; ListBox1.Items.Add(Format('%d) NAME: %s TYPE: %s, [1+i, fname, sft])); end; end; The result of this procedure call is visible here:

As you can see from the picture above, field types returned by the Jet are those expected. However, every field in an Excel data source can be only one of the following: Numeric, ADO type adDouble, Delphi type ftFloat - precision 15, Text, ADO type adVarChar, Delphi type ftWideString - max length 255, Memo, ADO type adVarWChar, Delphi type ftMemo, Currency, ADO type adCurrency, Delphi type ftCurrency, Boolean, ADO type adBoolean, Delphi type ftBoolean, Date, ADO type adDate, Delphi type ftDateTime, This concludes the discussion on Excel columns, and data types it provides when queried by the Jet. I think we are now more that ready to see how to actually edit the Excel data from a Delphi application... Note: given you previous Delphi knowledge, you could expect the field "Function" to act as a calculated field, however this is not the case; as you will see on the next page. Accessing and managing MS Excel sheets with Delphi Page 6: Transfering data from a Delphi application to Excel. How to create a worksheet, and fill it with "custom" data.

Ok, we've managed to set up, and get the Delphi version of the Excel spreadsheet editor up and running. In the process we've talked about connecting to Excel (eg. the ConectionString), about Excel columns and data types. What we are up to now is transferring data from other sources (in your Delphi application) to an Excel workbook. Back in the beginning of this article, we've concluded that many developers find appropriate to transport their data into an Excel workbook for analysis purposes. From Acees to Excel over Delphi using ADO It should not come as a surprise that you can use SQL statements to insert (copy) data from any Jet compliant data source to any (compatible) data destination. If this destination is an Excel workbook, Delphi (using ADO) enables you to transfer you "custom" data to Excel and even create a new workbook, if needed. To test the truth of the above statement, we'll adopt our Excel spreadsheet editor project to be able to connect to an Access database - we'll use as the data source. Go "back" to Delphi, and add two more components on the Form1: AdoConnection2 (TAdoConnection) and ADOQuery2 (TAdoQuery). We'll be accessing the sample QuickiesContest.mdb Access database introduced and described in the "Adding components to a DBGrid" article. This is how the connection to the Access database might look like, along with the link between AdoConnection2 and AdoQuery2 (add this code to the end of the Form1 OnCreate even handling procedure):

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 1: Connect to: MS Excel Page 2: Creating an Excel spreadsheet editor Page 3: Retrieving the data from Excel Page 4: Excel field (column) types Page 5: Modify Excel sheets Page 7: Full source code

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources A Beginners Guide to ADO programming Automation with Excel and Delphi

//connect to an Access database to send the data to Excel AdoConnection2.LoginPrompt:=False; AdoConnection2.ConnectionString:= 'Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\!Gajba\About\QuickiesContest.mdb; Persist Security Info=False'; AdoQuery2.Connection:=AdoConnection2; Now, we'll need two more buttons: Button1 (TButton) will be used to insert data from Access to Excel, and Button2 (TButton) will be used for data copying purposes. Insert Into ... Excel If you need to add (append) data to an existing workbook (and an existing worksheet), you can use the "Insert Into ... In" SQL statement. When transferring data to an existing sheet, the column headings must already be present. Here's an example of appending the values from three fields in the Articles table (Access database) to the Sheet2 in our test workbook:

procedure TForm1.Button2Click(Sender: TObject); var sAppend : string; begin sAppend := 'INSERT INTO [Sheet2$] IN "' + Edit1.Text + '" "Excel 8.0;" SELECT AuthorEmail, Title, Description FROM Articles'; AdoQuery2.SQL.Text:=sAppend; AdoQuery2.ExecSQL; end; Note: the above code appends the values of the fields AuthorEmail, Title, Description from the Articles table to an existing Excel workbook, the data is appended in a sheet named Sheet2. Caution: You must be very careful with the quotes (") usage or you might end up with the "Parameter object is improperly defined. Inconsistent or incomplete information was provided " error. Furthermore, if you try to append the data, and one of the column headings does not exist (like "Title"), you'll receive the "The INSERT INTO statement contains the following unknown field name: 'Title'. Make sure you have typed the name correctly, and try the operation again. " error message. Now, go to that Excel workbook, select Sheet2, and see for yourself: the data is transferred! Select Into ... Excel If you need to copy data from Access to Excel, and create a new sheet (and even a new workbook) along the way, you could try the next code: procedure TForm1.Button1Click(Sender: TObject); var sCopy : string; begin sCopy := 'SELECT * INTO ["Excel 8.0;Database=' + Edit1.Text + '"].[SheetAuthors] FROM Authors'; AdoQuery2.SQL.Text:=sCopy; AdoQuery2.ExecSQL; end; Note: the code above creates a workbook with the name specified in Edit1.Text property, if a workbook with the specified name does not already exist. The Select Into statement copies all the data from the Authors table (Access) to a newly created sheet called SheetAuthors. Furthermore note that you do not need to add the $ sign at the end of the sheet name. Caution: You must be very careful with the quotes (") usage or you might end up with the "Parameter object is improperly defined. Inconsistent or incomplete information was provided " error. What's more, if you try to copy the data, and the workbook is already open, an error message "The Microsoft Jet database engine could not find the object 'SheetAuthors'. Make sure the object exists and that you spell its name and the path name correctly " will be thrown. That does it! Bravo, if you have come so far, you deserve a "Bravo!". This was one really long article, I hope you've managed to find a way to struggle with all the "cautions" that were constantly poppingup. { Article: Accessing and managing MS Excel sheets with Delphi http://delphi.about.com/library/weekly/aa090903a.htm How to retrieve, display and edit Microsoft Excel spreadsheets with ADO (dbGO) and Delphi. This step-by-step article describes how to connect to Excel, retrieve sheet data, and enable editing of data (using the DBGrid). You'll also find a list of most common errors (and how to deal with them) that might pop up in the process. }

Download zipped project

Unit1.pas unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, DB, ADODB, Buttons, ComCtrls; type TForm1 = class(TForm) ADOConnection1: TADOConnection; DataSource1: TDataSource; DBGrid1: TDBGrid; DBNavigator1: TDBNavigator; Edit1: TEdit; Panel1: TPanel; Label1: TLabel; ADOQuery1: TADOQuery; Edit2: TEdit; Label2: TLabel; BitBtn1: TBitBtn; StatusBar1: TStatusBar; ComboBox1: TComboBox; Label3: TLabel; ListBox1: TListBox; ADOConnection2: TADOConnection; ADOQuery2: TADOQuery; Panel2: TPanel; Button1: TButton; Button2: TButton; procedure BitBtn1Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); private procedure ConnectToExcel; procedure FetchData; procedure GetFieldInfo; procedure DisplayException(Sender:TObject; E: Exception); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} uses typinfo;

{ TForm1 } procedure TForm1.ConnectToExcel; var strConn : widestring;

begin strConn:='Provider=Microsoft.Jet.OLEDB.4.0;' + 'Data Source=' + Edit1.Text + ';' + 'Extended Properties=Excel 8.0;'; AdoConnection1.Connected:=False; AdoConnection1.ConnectionString:=strConn; try AdoConnection1.Open; AdoConnection1.GetTableNames(ComboBox1.Items,True); except ShowMessage('Unable to connect to Excel, make sure the workbook ' + Edit1.Text + ' exist!'); raise; end; end;(*ConnectToExcel*) procedure TForm1.FetchData; begin StatusBar1.SimpleText:=''; if not AdoConnection1.Connected then ConnectToExcel; AdoQuery1.Close; AdoQuery1.SQL.Text:=Edit2.Text; try AdoQuery1.Open; except ShowMessage('Unable to read data from Excel, make sure the query ' + Edit1.Text + ' is meaningful!'); raise; end; end; procedure TForm1.BitBtn1Click(Sender: TObject); begin FetchData; GetFieldInfo; end; procedure TForm1.FormCreate(Sender: TObject); begin AdoConnection1.LoginPrompt:=False; AdoQuery1.Connection:=AdoConnection1; DataSource1.DataSet:=AdoQuery1; DBGrid1.DataSource:=DataSource1; DBNavigator1.DataSource:=DataSource1; Application.OnException:= DisplayException;

//connect to an Access database to send the data to Excel AdoConnection2.LoginPrompt:=False; AdoConnection2.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\!Gajba\About\QuickiesContest.mdb;Persist Security Info=False'; AdoQuery2.Connection:=AdoConnection2; AdoConnection2.Open; end;

procedure TForm1.DisplayException(Sender: TObject; E: Exception); begin StatusBar1.SimpleText:=E.Message; end; procedure TForm1.GetFieldInfo; var i : integer; ft : TFieldType; sft : string; fname : string; begin ListBox1.Clear; for i := 0 to AdoQuery1.Fields.Count - 1 do begin ft := AdoQuery1.Fields[i].DataType; sft := GetEnumName(TypeInfo(TFieldType), Integer(ft)); fname:= AdoQuery1.Fields[i].FieldName; ListBox1.Items.Add(Format('%d) NAME: %s TYPE: %s',[1+i, fname, sft])); end; end; procedure TForm1.Button2Click(Sender: TObject); var sAppend : string; begin sAppend:='INSERT INTO [Sheet2$] IN "' + Edit1.Text + '" "Excel 8.0;" SELECT AuthorEmail, Title, Description FROM Articles'; AdoQuery2.SQL.Text:=sAppend; AdoQuery2.ExecSQL; end; procedure TForm1.Button1Click(Sender: TObject); var sCopy : string; begin sCopy := 'SELECT * INTO ["Excel 8.0;Database=' + Edit1.Text + '"].[SheetAuthors] FROM Authors'; AdoQuery2.SQL.Text:=sCopy; AdoQuery2.ExecSQL; end; end.

Form1.dfm

Select Form1 (empty project), Right Click to get context popup menu, Select View As Text, Paste the text into Editor, Select View As Form. object Form1: TForm1 Left = 330 Top = 239 Width = 377 Height = 349

Caption = 'Delphi + ADO: Excel ' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False OnCreate = FormCreate PixelsPerInch = 96 TextHeight = 13 object DBGrid1: TDBGrid Left = 0 Top = 125 Width = 369 Height = 66 Align = alClient DataSource = DataSource1 TabOrder = 0 TitleFont.Charset = DEFAULT_CHARSET TitleFont.Color = clWindowText TitleFont.Height = -11 TitleFont.Name = 'MS Sans Serif' TitleFont.Style = [] end object DBNavigator1: TDBNavigator Left = 0 Top = 93 Width = 369 Height = 32 DataSource = DataSource1 Align = alTop TabOrder = 1 end object Panel1: TPanel Left = 0 Top = 0 Width = 369 Height = 93 Align = alTop TabOrder = 2 object Label1: TLabel Left = 4 Top = 8 Width = 53 Height = 13 Caption = 'Workbook:' end object Label2: TLabel Left = 4 Top = 48 Width = 31 Height = 13 Caption = 'Query:' end object Label3: TLabel Left = 180 Top = 48 Width = 36 Height = 13

Caption = 'Sheets:' end object Edit1: TEdit Left = 4 Top = 24 Width = 357 Height = 21 TabOrder = 0 Text = 'C:\Program Files\Borland\Delphi7\Projects\AdoExcel\AdoDelphiExce' + 'l.xls' end object Edit2: TEdit Left = 4 Top = 64 Width = 169 Height = 21 TabOrder = 1 Text = 'SELECT * FROM [Sheet1$]' end object BitBtn1: TBitBtn Left = 284 Top = 60 Width = 77 Height = 25 Caption = '&Re-fetch' TabOrder = 2 OnClick = BitBtn1Click Kind = bkRetry end object ComboBox1: TComboBox Left = 180 Top = 64 Width = 97 Height = 21 Style = csDropDownList ItemHeight = 13 TabOrder = 3 end end object StatusBar1: TStatusBar Left = 0 Top = 303 Width = 369 Height = 19 Panels = <> end object ListBox1: TListBox Left = 0 Top = 191 Width = 369 Height = 71 Align = alBottom ItemHeight = 13 TabOrder = 4 Visible = False end object Panel2: TPanel Left = 0 Top = 262

Width = 369 Height = 41 Align = alBottom BevelOuter = bvLowered TabOrder = 5 object Button1: TButton Left = 20 Top = 8 Width = 149 Height = 25 Caption = 'Copy data from Access' TabOrder = 0 OnClick = Button1Click end object Button2: TButton Left = 192 Top = 8 Width = 145 Height = 25 Caption = 'Insert data from Access' TabOrder = 1 OnClick = Button2Click end end object ADOConnection1: TADOConnection CursorLocation = clUseServer LoginPrompt = False Mode = cmShareDenyNone Provider = 'Microsoft.Jet.OLEDB.4.0' Left = 120 Top = 148 end object DataSource1: TDataSource DataSet = ADOQuery1 Left = 44 Top = 148 end object ADOQuery1: TADOQuery Parameters = <> Left = 196 Top = 148 end object ADOConnection2: TADOConnection ConnectionString = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\!Gajba\About\Qui' + 'ckiesContest.mdb;Persist Security Info=False' LoginPrompt = False Provider = 'Microsoft.Jet.OLEDB.4.0' Left = 140 Top = 212 end object ADOQuery2: TADOQuery Connection = ADOConnection2 Parameters = <> Left = 220 Top = 212 end end

{ ******************************************** Zarko Gajic About.com Guide to Delphi Programming http://delphi.about.com email: delphi.guide@about.com free newsletter: http://delphi.about.com/library/blnewsletter.htm forum: http://forums.about.com/ab-delphi/start/ ******************************************** } Adding components to a DBGrid How to place just about any Delphi control (visual component) into a cell of a DGBrid. Find out how to put a CheckBox, a ComboBox (drop down list box), a DateTimePicker (calendar) and even an Image inside the DBGrid.

The power of the DBGrid Win prizes by sharing I've said this many times, but I'll say it again: Delphi's DBGrid ... code! what a powerful component! Contrary to most other Delphi data- Do you have some Delphi aware controls, the DBGrid component has many nice features code you want to share? and is more powerful than you would have thought. Are you interested in The "standard" DBGrid does its job of displaying and manipulating winning a prize for your records from a dataset in a tabular grid. However, there are many work? ways (and reasons) why you should consider customizing the Delphi Programming output of a DBGrid. Quickies Contest Here's an example: Suppose you have a boolean field in your dataset. By default, the DBGrid displays boolean fields as "True" or "False" depending on More of this Feature the value of the data field. If you think the same way I do, it is Download sample MS much more visually attractive to be able to you use a "true" check Access database box control to enable editing of such fields. This article serves as an entry point to a series of articles describing how to place just about any component into a cell of a DBGrid. Join the Discussion Placing controls in DBGrid "Post your views, As some of you might not know, in-place editing of a DBGrid cell's comments, questions and contents is accomplished via a "small" edit control that is doubts to this article." displayed over the cell. Inside DBGrid, there is a TInplaceEdit that Discuss! moves around the grid - the Edit component that you enter your data into. The rest of the unfocused cells are really just "pictures". But a CheckBox, a ComboBox, ...? So what? If there is a floating Edit over the DBGrid, we'll float any Related Resources free DB Course.TOC control we like. There are no new ideas here, in fact, the basic Coloring DBGrid technique simply mimics what the DBGrid does internally. What you will learn here, is how to float any type of visual control around Multiple row selection in DBGrid the DBGrid. Sorting a DBGrid by A sample database Column click Following the concepts set out in the Beginners Guide to Delphi Using Delphi DB Database Programming, examples below use ADO components components (AdoQuery/AdoTable connected to ADOConnection, DBGrid connected to AdoQuery over DataSource) to display the records more Database articles from a database table in a DBGrid component. All the component names were left as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...) If you do not know how to display records from a database table (or query) in a DBGrid component, please explore the "Connecting to a database" chapter. Here's a sample MS Access database we'll use to explore the topic of adding components to a DBGrid. The QuickiesContest.MDB databse has three tables: Subjects, Authors and Articles.

The database was originally created to hold the entries to our Delphi Programming Quickies Contest. The picture below displays the relationships between tables. For the moment note that the Winner field in the Articles table is a YesNo field (boolean).

Download database Components in a DBGrid - the Theory When adding controls to a DBGrid, there are a couple of steps and questions to be revealed. Here's what has to be done to place a DBCheckBox inside a DBGrid cell, and enable editing of a boolean field using the CheckBox component: 1. A DBCheckBox needs to be placed (invisible) on a Form and linked to a boolean field in the DataSource component that supplies the DBGrid with a dataset. 2. If the cell holding a boolean field is focused the CheckBox component should be made visible and placed in the appropriate cell. When no longer needed the CheckBox needs to be made invisible again. 3. If the cell holding a boolean field is NOT focused a sample graphics should be drawn on the cell indicating whether a field's value is True or False. 4. When in editing mode, all keystrokes are going to the DBGrid's cell; we have to make sure they are sent to the CheckBox. Note that we are primarily interested in the [Tab] and the [Space] key. "Tab" should move the input focus to the next cell, and [Space] should toggle the state of the CheckBox. Note: the above "steps" are more or less the same no matter what component is placed in (i.e. floating over) the cell. Components in a DBGrid - Theory into Practice Finally, here's how all the above theory looks in practice (with more examples coming in the near future): Placing a CheckBox in a DBGrid Here's how to place a check box into a DBGrid. Create visually more attractive user interfaces for editing boolean fields inside a DBGrid.

Drop down pick list inside a DBGrid - part 1 Here's how to place a drop down pick list into a DBGrid. Create visually more attractive user interfaces for editing lookup fields inside a DBGrid - using the PickList property of a DBGrid column.

Drop down list (DBLookupComboBox) inside a DBGrid - part 2 Here's how to place a DBLookupComboBox into a DBGrid. Create visually more attractive user interfaces for editing lookup fields inside a DBGrid - place a DBLookupComboBox into a cell of a DBGrid.

DateTimePicker inside a DBGrid Here's how to place a TDateTimePicker into a DBGrid. Create visually more attractive user interfaces for editing date/time fields inside a DBGrid - place a drop down calendar into a cell of a DBGrid.

Need more DBGrid related articles? Be sure to check the rest of the articles dealing with the DBGrid (and other db-aware) components; of course don't miss the "DBGrid to the Max" article collection! CheckBox inside a DBGrid Here's how to place a check box into a DBGrid. Create visually more attractive user

interfaces for editing boolean fields inside a DBGrid.

This is the first article, in the series of articles named "Adding components to a DBGrid". The idea is to show how to place just about any Delphi control (visual component) into a cell of a DGBrid. If you are unfamiliar with the idea, please first read the "Adding components to a DBGrid" article. CheckBox in a DBGrid? As discussed in the above article, there are many ways (and reasons) why you should consider customizing the output of a DBGrid: suppose you have a boolean field in your dataset. By default, the DBGrid displays boolean fields as "True" or "False" depending on the value of the data field. If you think the same way I do, it is much more visually attractive to be able to you use a "true" check box control to enable editing of such fields. Creating a sample application To begin, start Delphi and, on that default empty new form, place a TDBGrid, a TADOTable, and a TADOConnection, TDataSource. Leave all the component names as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...). Use the Object Inspector to set a ConnectionString property of the ADOConnection1 (TADOConnection) component to point to the sample QuickiesContest.mdb MS Access database. Connect DBGrid1 to DataSource1, DataSource1 to ADOTable1, and finally ADOTable1 to ADOConnection1. ADOTable1's TableName property should point to the Articles table (thus making the DBGrid display the records of the Articles table). If you have set all the properties correctly, when you run the application (given that the Active property of the ADOTable1 component is True) you should see the following output:

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Adding components into a DBGrid - the theory Download sample QuickiesContest.mdb database

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources free DB Course.TOC Coloring DBGrid Multiple row selection in DBGrid Sorting a DBGrid by Column click Using Delphi DB components more Database articles What you need to "see" in the above picture is that, by default, the DBGrid displays the boolean field's value as "True" or "False" depending on the value of the data field. The field that holds the boolean value is "Winner". What we are up against in this article is to make the above picture look like the one below:

CheckBox in a DBGrid! Or, better to say, a DBCheckBox in a DBGrid. Ok, here we go. To show a check box inside a cell of a DBGrid we'll need to make one available for us at run time. Select the "Data controls" page on the Component Palette and pick a TDBCheckbox. Drop one anywhere on the form - it doesn't matter where, since most of the time it will be invisible or floating over the grid. TDBCheckBox is a data-aware control that allows the user to select or deselect a single value - most appropriate for boolean fields. Next, set its Visible property to False. Change the Color property of DBCheckBox1 to the same color as the DBGrid (so it blends in with the DBGrid) and remove the Caption. And most importantly, make sure the DBCheckBox1 is connected to the DataSource1 and to the correct field (DataSource = DataSource1, DataField = Winner). Note that all the above DBCheckBox1's property values can be set in the form's OnCreate event like: procedure TForm1.FormCreate(Sender: TObject); begin DBCheckBox1.DataSource := DataSource1; DBCheckBox1.DataField := 'Winner'; DBCheckBox1.Visible := False; DBCheckBox1.Color := DBGrid1.Color; DBCheckBox1.Caption := ''; //explained later in the article DBCheckBox1.ValueChecked := 'Yes a Winner!'; DBCheckBox1.ValueUnChecked := 'Not this time.'; end; What comes next is the most interesting part. While editing the boolean field in the DBGrid, we need to make sure the DBCheckBox1 is placed above ("floating") the cell in the DBGrid displaying the boolean field. For the rest of the (non-focused) cells carrying the boolean fields (in the "Winner" column), we need to provide some graphical representation of the boolean value (True/False). This means you need at least two images for drawing: one for the checked (True value) state, and one for the unchecked (False value) state. The easiest way to accomplish this is to use the Windows API DrawFrameControl function to draw directly on the DBGrid's canvas. Here's the code in the DBGrid's OnDrawColumnCell event handler that occurs when the grid needs to paint a cell. procedure TForm1.DBGrid1DrawColumnCell( Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); const IsChecked : array[Boolean] of Integer = (DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED); var DrawState: Integer; DrawRect: TRect; begin if (gdFocused in State) then

begin if (Column.Field.FieldName = DBCheckBox1.DataField) then begin DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 2; DBCheckBox1.Top := Rect.Top + DBGrid1.top + 2; DBCheckBox1.Width := Rect.Right - Rect.Left; DBCheckBox1.Height := Rect.Bottom - Rect.Top; DBCheckBox1.Visible := True; end end else begin if (Column.Field.FieldName = DBCheckBox1.DataField) then begin DrawRect:=Rect; InflateRect(DrawRect,-1,-1); DrawState := ISChecked[Column.Field.AsBoolean]; DBGrid1.Canvas.FillRect(Rect); DrawFrameControl(DBGrid1.Canvas.Handle, DrawRect, DFC_BUTTON, DrawState); end; end; end; To finish this step, we need to make sure DBCheckBox1 is invisible when we leave the cell: procedure TForm1.DBGrid1ColExit(Sender: TObject); begin if DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField then DBCheckBox1.Visible := False end; We need just two more events to handle. Note that when in editing mode, all keystrokes are going to the DBGrid's cell, we have to make sure they are sent to the CheckBox. In the case of a CheckBox we are primarily interested in the [Tab] and the [Space] key. [Tab] should move the input focus to the next cell, and [Space] should toggle the state of the CheckBox. procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); begin if (key = Chr(9)) then Exit; if (DBGrid1.SelectedField.FieldName = DBCheckBox1.DataField) then begin DBCheckBox1.SetFocus; SendMessage(DBCheckBox1.Handle, WM_Char, word(Key), 0); end; end; And finally, the last touch. It could be appropriate for the Caption of the checkbox to change as the user checks or unchecks the box. Note that the DBCheckBox has two properties (ValueChecked and ValueUnChecked) used to specify the field value represented by the check box when it is checked / unchecked. My ValueChecked property holds 'Yes a Winner!' and ValueUnChecked equals 'Not this time.' procedure TForm1.DBCheckBox1Click(Sender: TObject); begin if DBCheckBox1.Checked then DBCheckBox1.Caption := DBCheckBox1.ValueChecked else DBCheckBox1.Caption := DBCheckBox1.ValueUnChecked;

end; That's it. Run the project and voila ... check boxes all over the Winner field's column. If you need any help with the code I encourage you to post any questions on the Delphi Programming Forum. DateTimePicker inside a DBGrid Here's how to place a TDateTimePicker into a DBGrid. Create visually more attractive user interfaces for editing date/time fields inside a DBGrid - place a drop down calendar into a cell of a DBGrid.

Yes! More controls are being added to a DBGrid! What a great idea! Let's see how to create the best data editing grid ever! This is the fourth article, in the series of articles named "Adding components to a DBGrid". The idea is to show how to place just about any Delphi control (visual component) into a cell of a DGBrid. If you are unfamiliar with the idea, please first read the "Adding components to a DBGrid" article. DateTimePicker in a DBGrid? Why not! If you have a database table containing a date (time) field you could/should be tempted to provide a calendar-like drop down selection for a user to select the date time value. Of course, Delphi has all you need, just take the pieces (components and code) and of you go. The TDateTimePicker (on "Win32" component palette tab) is a visual component designed specifically for entering dates or times. Since Delphi does not provide a DB-aware version of the TDateTimePicker component, we'll need to use some tricks to make our date picker appear inside a DBGrid - in this article you'll see the fastest way of implementing an "in-dbgrid" drop down date (time) calendar picker. DateTimePicker in a DBGrid! Let's start by creating a sample application ... Note: be sure to check this link in order to see what we are "dealing" with. Our sample MS Access database, quickiescontest.mdb, has a table named "Articles" and this table has one date/time field called "DateAdded". Once you set up a DBGrid displaying records from the Authors table, Delphi will add a TDateTimeField field object that represents the DateAdded field in the database table. Note that if you use the Fields editor at design time to create a persistent field component for the date-time field, you can access it by name at runtime - in our case the field is called "DateAdded", and the TDateTimeField variable is "AdoTable1DateAdded". Now, since we've already discussed the theory of adding controls to a DBGrid, I'll just list the necessary steps here, along with the code... Naturally, we first need to place a TDateTimePicker on a form, so drop one. Using the Object Inspector, change the name of the TDateTime component to "DateTimePicker". Next, set its Visible property to False. As stated above, the TDateTime picker is not db-aware so there are no DB related properties to set. Magic... What's left for us to do, is to actually make a drop down calendar hover over a cell (when in edit mode) displaying the DateAdded field's value. We've already talked theory - I'll show you only the code here (you'll have the option to download the entire project later): First, we need to make sure the DateTimePicker is moved (and

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Adding components into a DBGrid - the theory Download sample QuickiesContest.mdb database Download full source CODE

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources Adding components into a DBGrid - theory and practice free DB Course.TOC DBGrid related articles and tips How to make a TDateTimePicker display blank

sized) over the cell in which the DateAdded field is displayed. procedure TForm1.DBGrid1DrawColumnCell (Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin if (gdFocused in State) then begin if (Column.Field.FieldName = 'DateAdded') then with DateTimePicker do begin Left := Rect.Left + DBGrid1.Left + 1; Top := Rect.Top + DBGrid1.Top + 1; Width := Rect.Right - Rect.Left + 2; Width := Rect.Right - Rect.Left + 2; Height := Rect.Bottom - Rect.Top + 2; Visible := True; end; end end; Next, when we leave the cell, we have to hide the date time picker: procedure TForm1.DBGrid1ColExit(Sender: TObject); begin if DBGrid1.SelectedField.FieldName = 'DateAdded' then DateTimePicker.Visible := False end; Next, note that when in editing mode, all keystrokes are going to the DBGrid's cell, we have to make sure they are sent to the DateTimePicker. We are primarily interested in the [Tab] key [Tab] should move the input focus to the next cell. procedure TForm1.DBGrid1KeyPress (Sender: TObject; var Key: Char); begin if (key = Chr(9)) then Exit; if (DBGrid1.SelectedField.FieldName = 'DateAdded') then begin DateTimePicker.SetFocus; SendMessage(DateTimePicker.Handle, WM_Char, word(Key), 0); end end; We are not finished yet. Just two more events to handle! What we need to do is to place the dataset into edit mode when the user opens the drop-down calendar, and we have to assign the value of the date that is marked on the calendar to the DateAdded field. Just handle the OnDropDown and the OnChange events: procedure TForm1.DateTimePickerChange(Sender: TObject); begin if DBGrid1.DataSource.State in [dsEdit, dsInsert] then ADOTable1DateAdded.Value := DateTimePicker.DateTime; end; procedure TForm1.DateTimePickerDropDown(Sender: TObject); begin DBGrid1.DataSource.Edit; end; That's it. Run the project and voila ... one nicely looking drop down calendar enabling you to

change the value of DateAdded field's column.

Note: TDateTimePicker formats date and time values according to the date and time settings in the Regional Settings of the Control panel on the user's system - this is why you see Croatian date-time formatting. If you need any help, after you download and explore the full source code for this project, I encourage you to post any questions on the Delphi Programming Forum. Ok, ok.. you'll be even more happy with a "real" db-aware TDateTimePicker ... let's say we'll build one in the near future. Drop down list inside a DBGrid - part 1 Page 1: three ways to set up a drop down list in a DBGrid.

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 2: Defining a PickList for a field Download sample QuickiesContest.mdb database

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Here's how to place a drop down pick list into a DBGrid. Create Related Resources visually more attractive user interfaces for editing lookup fields Adding components into a inside a DBGrid - using the PickList property of a DBGrid column. DBGrid - the theory This is the second article, in the series of articles named "Adding components to a DBGrid". The idea is to show how to place just free DB Course.TOC about any Delphi control (visual component) into a cell of a DBGrid related articles DGBrid. If you are unfamiliar with the idea, please first read the and tips "Adding components to a DBGrid" article. Drop down pick list in a DBGrid? As discussed in the above article, there are many ways (and reasons) why you should consider customizing the output of a DBGrid: suppose you have a lookup field in your dataset. One way to explain a database lookup field is to say that lookup fields are fields that can *only* accept values from a fixed list of values ("static" or from a dataset). Here's an example: if you have a Products table with one field holding the name of the supplier, when editing that field it would be great to enable a user to pick a supplier name from a list of suppliers (contained in the Suppliers table) Creating a sample application To begin, start Delphi and, on that default empty new form, place a TDBGrid, a TADOTable, and a TADOConnection, TDataSource. Leave all the component names as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...). Use the Object Inspector to set a ConnectionString property of the ADOConnection1 (TADOConnection) component to point to the sample QuickiesContest.mdb MS Access database. Connect DBGrid1 to DataSource1, DataSource1 to ADOTable1, and finally ADOTable1 to ADOConnection1. ADOTable1's TableName property should point to the Articles table (thus making the DBGrid display the records of the Articles table). Note: Nothing stops you from using any other Delphi's dataset components like TTable, TQuery, ... If you have set all the properties correctly, when you run the application (given that the Active property of the ADOTable1 component is True) you should see the following output:

Note that the Subject field can only accept values from the Subjects table (defined by referential integrity). What you need to "see" in the above picture is that, when editing this field wit no pick list, if you try to edit a value to some value not found in the Subjects table ("master" table), you'll get "You cannot add or change a record because a related record is required in table Subjects" error message What we are up against in this article is to make the above picture look like the one below:

There are three ways you can set a lookup field in Delphi. 1. If you have a data form where all editings are done in a DBGrid, the best is to create a new (lookup) field for the dataset. 2. If a form hosts a set of data controls (DBEdit, DBComboBox, etc.), it makes sense to just use DBLookupComboBox without creating a new field. 3. The last one is to use columns of a DBGrid with its PickList property, again without creating a new field. This approach is not a true lookup approach but you'll see that it can act as one. The PickList list values that the user can select for the field value - just like a standard DBComboBox - but inside a DBGrid. We'll discuss each of them, starting with the last. On the next page you'll find out how to make a DBGrid column's cell display a drop-down list of values. Drop down list (DBLookupComboBox) inside a DBGrid - part 2 Page 1: DBLookupComboBox in a DBGrid?

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Part 1: Three ways to set up a drop down list in a DBGrid. Page 2: DBLookupComboBox in a DBGrid! Download sample QuickiesContest.mdb database Download full source

This is the third article, in the series of articles named "Adding CODE components to a DBGrid". The idea is to show how to place just about any Delphi control (visual component) into a cell of a DGBrid. If you are unfamiliar with the idea, please first read the Join the Discussion "Adding components to a DBGrid" article. "Post your views, In the first part of this article (second in the series), we've comments, questions and discussed about lookup fields, and what are the options of displaying a lookup field in a DBGrid, it's time to see how to place doubts to this article." a DBLookupComboBox into a cell of a DGBrid to enable a user to Discuss! pick a value for a lookup field from a drop down list box. Here's how to place a DBLookupComboBox into a DBGrid. Create Related Resources visually more attractive user interfaces for editing lookup fields inside a DBGrid - place a DBLookupComboBox into a cell of a Adding components into a DBGrid. DBGrid - the theory DBLookupComboBox in a DBGrid? Yes, just as we can place a check box in a cell of a DBGrid (for free DB Course.TOC boolean field editing), we can use the same technique to put a combo box into a cell holding a lookup field. DBGrid related articles Recall that a DBLookupComboBox is used, in database and tips applications, to present the user with a restricted list of choices from which to set a valid field value. When a user selects a list item, the corresponding field value is changed in the underlying dataset. In general, you use a DBLookupComboBox on a data entry form, separate from a DBGrid. This time we'll make a DBLookupComboBox float over a cell containing a lookup field. Creating a sample application Note that we've already created, in the previous article, a sample application containing a DBGrid and several DB related components. Here's how: To begin, start Delphi and, on that default empty new form, place a TDBGrid, a TADOTable, and a TADOConnection, TDataSource. Leave all the component names as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...). Use the Object Inspector to set a ConnectionString property of the ADOConnection1 (TADOConnection) component to point to the sample QuickiesContest.mdb MS Access database. Connect DBGrid1 to DataSource1, DataSource1 to ADOTable1, and finally ADOTable1 to ADOConnection1. ADOTable1's TableName property should point to the Articles table (thus making the DBGrid display the records of the Articles table). Note (again): nothing stops you from using any other Delphi's dataset components like TTable, TQuery, ... Now, take a look at the AuthorEmail field. Note that this field can only accept values from the Email field of the Authors table (defined by referential integrity). What you need to "see" in the picture below is that, when editing this field wit no lookup defined, if you try to edit a value to some value not found in the Authors table ("master" table), you'll get "You cannot add or change a record because a related record is required in table Authors" error message.

What we are up against in this article is to make the above picture look like the one below:

Ok. First we need to add several more components on a form along with a DBLookupComboBox. Next we have to set them up and do some Delphi magic ;) Interested? Go for the next page... Drop down list (DBLookupComboBox) inside a DBGrid - part 2 Page 2: DBLookupComboBox in a DBGrid!

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Part 1: Three ways to set up a drop down list in a DBGrid. Page 1: DBLookupComboBox in a DBGrid? Download sample QuickiesContest.mdb database Download full source CODE

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Here's how to place a DBLookupComboBox into a DBGrid. Create visually more attractive user interfaces for editing Related Resources lookup fields inside a DBGrid - place a DBLookupComboBox into Adding components into a a cell of a DBGrid. DBGrid - the theory What a great idea! Let's see how to create the best data editing grid ever! free DB Course.TOC Creating a lookup with a DBLookupComboBox To show a DBLookupComboBox inside a cell of a DBGrid we'll DBGrid related articles need to make one available for us at run time. Select the "Data and tips controls" page on the Component Palette and pick a DBLookupComboBox. Drop one anywhere on the form (leave the default name, "DBLookupComboBox1") - it doesn't matter where, since most of the time it will be invisible or floating over the grid. Next, you have to add one more DataSource and a DataSet component to "fill" the combo box with values. Drop a TDataSource (name: DataSource2) and a TAdoQuery (name: AdoQuery1) anywhere on the form. For a DBLookupComboBox to work properly several more properties must be set, these properties are key to the lookup connection: DataSource and DataField determine the main connection. The DatField is a field into which we insert the looked-up values. ListSource is the source of the lookup dataset. KeyField identifies the field in the ListSource that must match the value of the DataField field. ListFields is the field (one or more) of the lookup dataset that are actually displayed in the combo. ListField can show more than one field. Multiple field names should be separated by semicolons. You have to set large enough value for the DropDownWidth (of a ComboBox) to really see multiple columns of data. Here's how to set all the important properties from code (in the form's OnCreate event handler): procedure TForm1.FormCreate(Sender: TObject); begin with DBLookupComboBox1 do begin DataSource := DataSource1; // -> AdoTable1 -> DBGrid1 ListSource := DataSource2; DataField := 'AuthorEmail'; // from AdoTable1 - displayed in the DBGrid KeyField := 'Email'; ListFields := 'Name; Email'; Visible end; := False;

DataSource2.DataSet := AdoQuery1; AdoQuery1.Connection := AdoConnection1; AdoQuery1.SQL.Text := 'SELECT Name, Email FROM Authors'; AdoQuery1.Open; end; Note: when you want to display more than one field in a DBLookupComboBox, as in the example above, you have to make sure that all columns are visible. This is done by setting the DropDownWidth property. However, you'll see that initially you have to set this to a very large value which results in dropped list being too wide in most cases. One workaround is to set the DisplayWidth of a particular Field shown in a drop down list. The next code snippet, placed inside the OnCreate event for the form, ensures that both author name and it's email are displayed inside the drop down list: AdoQuery1.FieldByName('Email').DisplayWidth:=10; AdoQuery1.FieldByName('Name').DisplayWidth:=10; AdoQuery1.DropDownWidth:=150; Magic... What's left for us to do, is to actually make a combo box hover over a cell (when in edit mode) displaying the AuthorEmail field. We've already discussed the theory - I'll show you only the

code here (you'll have the option to download the entire project later): First, we need to make sure the DBLookupComboBox1 is moved (and sized) over the cell in which the AuthorEmail field is displayed. procedure TForm1.DBGrid1DrawColumnCell (Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin if (gdFocused in State) then begin if (Column.Field.FieldName = DBLookupComboBox1.DataField) then with DBLookupComboBox1 do begin Left := Rect.Left + DBGrid1.Left + 2; Top := Rect.Top + DBGrid1.Top + 2; Width := Rect.Right - Rect.Left; Width := Rect.Right - Rect.Left; Height := Rect.Bottom - Rect.Top; Visible := True; end; end end; Next, when we leave the cell, we have to hide the combo box: procedure TForm1.DBGrid1ColExit(Sender: TObject); begin if DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField then DBLookupComboBox1.Visible := False end; Next, note that when in editing mode, all keystrokes are going to the DBGrid's cell, we have to make sure they are sent to the DBLookupComboBox. In the case of a DBLookupComboBox we are primarily interested in the [Tab] key - [Tab] should move the input focus to the next cell. procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); begin if (key = Chr(9)) then Exit; if (DBGrid1.SelectedField.FieldName = DBLookupComboBox1.DataField) then begin DBLookupComboBox1.SetFocus; SendMessage(DBLookupComboBox1.Handle, WM_Char, word(Key), 0); end end; That's it. Run the project and voila ... one nicely looking drop down list box (with two columns!) all over the AuthorEmail field's column. What does happen when you select a value (Name; Email) from the drop down list? When you pick an item ("row") from a DBLookupComboBox, the value or the corresponding KeyField field is stored as the value of the DataField field.

If you need any help, after you download and explore the full source code for this project, I encourage you to post any questions on the Delphi Programming Forum. Need more DBGrid related articles? Be sure to check the "Adding components to a DBGrid" article and the rest of the articles dealing with the DBGrid (and other db-aware) components; of course don't miss the "DBGrid to the Max" article collection! Drop down list inside a DBGrid - part 1 Page 2: defining a PickList for a (lookup) field. Making the drop-down list appear faster.

Win prizes by sharing code! Do you have some Delphi code you want to share? Are you interested in winning a prize for your work? Delphi Programming Quickies Contest

More of this Feature Page 1: Three ways to set up a drop down list in a DBGrid. Download sample QuickiesContest.mdb database

Join the Discussion "Post your views, comments, questions and doubts to this article." Discuss!

Related Resources Adding components into a DBGrid - the theory

Here's how to place a drop down pick list into a DBGrid. Create free DB Course.TOC visually more attractive user interfaces for editing lookup fields DBGrid related articles inside a DBGrid - using the PickList property of a DBGrid column. and tips Now, that you know what are lookup fields, and what are the options of displaying a lookup field in Delphi's DBGrid, it's time to see how to use the PickList property of a DGBrid column to enable a user to pick a value for a lookup field from a drop down list box. A quick info on DBGrid Columns property A DBGrid control has a Columns property - a collection of TColumn objects representing all of the columns in a grid control. Columns can be set at design time through the Columns editor, or programmatically at runtime. You'll usually add Columns to a DBGird when you want to define how a column appears, how the data in the column is displayed and to access the properties, events, and methods of TDBGridColumns at runtime. A customized grid enables you to configure multiple columns to present different views of the same dataset (different column orders, different field choices, and different column colors and fonts, for example). Now, each Column in a grid is "linked" to a field from a dataset displayed in the grid. What's more, each column has a PickList property. The PickList property lists values that the user can select for the column's linked field value. Filling the PickList What you will learn here is how to fill that String List with values from another dataset at run time. Recall, that we are editing the Articles table - and that a Subject field can only accept values from the Subjects table: ideal situation for the PickList! Here's how to set up the PickList property. First, we add a call to the SetupGridPickList procedure in the Form's OnCreate event handler. procedure TForm1.FormCreate(Sender: TObject); begin SetupGridPickList('Subject', 'SELECT Name FROM Subjects'); end; The easiest way to create the SetupGridPickList procedure is to go to the private part of the form declaration, add the declaration there and hit the CTRL + SHIF + C key combination Delphi's code completion will do the rest: ... type TForm1 = class(TForm) ... private procedure SetupGridPickList( const FieldName : string; const sql : string); public ... Note: the SetupGridPickList procedure takes two parameters. The first parameter, FieldName, is the name of the field we want to act like a lookup field; the second parameter, sql, is the SQL expression we use to populate the PickList with possible values - in general the SQL expression should return a datataset with only one field. Here's how the SetupGridPickList looks like: procedure TForm1.SetupGridPickList(const FieldName, sql: string); var slPickList:TStringList; Query : TADOQuery; i : integer; begin slPickList:=TStringList.Create; Query := TADOQuery.Create(self); try Query.Connection := ADOConnection1; Query.SQL.Text := sql;

Query.Open; //Fill the string list while not Query.EOF do begin slPickList.Add(Query.Fields[0].AsString); Query.Next; end; //while //place the list it the correct column for i:=0 to DBGrid1.Columns.Count-1 do if DBGrid1.Columns[i].FieldName = FieldName then begin DBGrid1.Columns[i].PickList:=slPickList; Break; end; finally slPickList.Free; Query.Free; end; end; (*SetupGridPickList*) That's it. Now, when you click the Subject column (to enter into edit mode), you'll get the next picture:

Note 1: by default, the drop-down list displays 7 values. You can change the length of this list by setting the DropDownRows property. Note 2: nothing stops you from filling up the PickList from a list of values not coming from a database table. If, for example, you have a field that only accepts week day names ('Monday', ..., 'Sunday') you can build a "hard-coded" PickList. "Uh, I need to click the PickList 4 times..." Note that when you want to edit the field displaying a drop down list, you'll need to click the cell 4 times in order to actually pick a value from a list. The next code snippet, added to the DBGrid's OnCellClick event handler, mimics a hit to the F2 key followed by Alt + DownArrow. procedure TForm1.DBGrid1CellClick(Column: TColumn); begin //Making the drop-down pick list appear faster if Column.PickList.Count > 0 then begin keybd_event(VK_F2,0,0,0); keybd_event(VK_F2,0,KEYEVENTF_KEYUP,0); keybd_event(VK_MENU,0,0,0); keybd_event(VK_DOWN,0,0,0); keybd_event(VK_DOWN,0,KEYEVENTF_KEYUP,0); keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0); end; end; That's all folks

This concludes Part 1 of this article; in the next two parts you'll learn how to place a DBLookupComboBox inside a cell of a DBGrid. If you need any help, please post all your problems (and/or suggestions) to the About Delphi Programming Forum. How to Refresh a DBGrid without Losing the Current Row Position Refresh DBGrid Data - Preserve Row Position By Zarko Gajic, About.com Guide Filed In: 1. Coding Delphi Applications 2. > Delphi Tips and Tricks 3. > Delphi 2008 Tips Sponsored Links Delphi Programmers WantedThe Only Web IDE Custom Designed For Delphi Programmers. IDE is Freewww.morfik.com Losing Someone and GodGod can be with you even When facing deathwww.LosingSomeoneAndGod.com Web/XML Appl. GeneratorNARM/Network Application Rendering Modelwww.bitech-factory.ch There's DBGrid, there's a dataset and we have a data aware Delphi application :) When DBGrid is used to display data from a dataset (query or table), by design, after you call Refresh method on a dataset (re-open) (for example, using a DBNavigator), the current row position will be set to zero (first record). This means that if a user has selected a row somewhere near the bottom of a DBGrid, after a Refresh, the active row will be changed to first row :( If you have been asking "Is there any way to reopen or refresh a query, leaving the TDBGrid data exactly where it is (without changing the positions)?" Here's one answer to the problem: Refresh DBGrid Data - Preserve Row Position Here's a handly little procedure to refresh the content of a DBGrid without losing the row position. //THackDBGrid = class(TDBGrid) //refresh datagrid data - preserve row position procedure Refresh_PreservePosition; var rowDelta: Integer; row: integer; recNo: integer; ds : TDataSet; begin ds := THackDBGrid(DBGrid1).DataSource.DataSet; rowDelta := -1 + THackDBGrid(DBGrid1).Row; row := ds.RecNo; ds.Refresh; with ds do begin DisableControls; RecNo := row; MoveBy(-rowDelta) ; MoveBy(rowDelta) ; EnableControls; end; end; Drag and drop operations with Delphi's DBGrid Learn how to enable dragging and dropping from and to a TDBGrid component.

Dragging and dropping is a convenient way for users to manipulate data. You can let users drag an entire control, or let Join the Discussion them drag items from one control to another, or drag and drop "Post your views, nodes of a tree view (for example). comments, questions and Each Delphi control exposes several events (OnDragOver, doubts to this article." OnDragDrop) and properties (DragMode) you use to program drag Discuss! and drop operations in your applications. In order to start a drag operation for a control, you either have to set the DragMode property to dmAutomatic, or leave it to Related Resources dmManual (default) and manually call the BeginDrag method. BeginDrag starts the dragging of a control. When we have started Using TDBGrid the drag operation we need to have a component that will act as a Dragging and dropping drop target. To enable a control to accept an object being draged you have to code two events: OnDragOver (to signal that the control can accept a dragged object) and OnDragDrop (to specify what happens when the user drops an object). In Delphi database applications, where TDBGrid is used to enable a user to view and edit data in a tabular grid, dragging and dropping can come quite handy. Let's see how to add drag and drop capabilities to DBGrid. Dragging FROM a DBGrid Let's say you have a DBGrid attached to some Dataset object displaying some data from a database. Here's what needs to be coded if you want to enable a user to drag the selected record to a TMemo control, for example. 1. Handle the OnCellClick event of a DBGrid (Name := 'DBGrid1') to start the drag operation: procedure TForm1.DBGrid1CellClick(Column: TColumn); begin DBGrid1.BeginDrag(true); end; 2. Specify that a Memo control (Name := 'Memo1') can accept data from a DBGrid, using the OnDragDrop event of a Memo: procedure TForm1.Memo1DragOver( Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := Source IS TDBGrid; end; 3. Finally, handle the OnDragDrop of a Memo control to process data being dropped. We'll add the code to add the values of all the fields of the current record to a Memo control: procedure TForm1.Memo1DragDrop( Sender, Source: TObject; X, Y: Integer); var i : integer; begin Memo1.Clear; for i := 0 to -1 + DBGrid1.FieldCount do begin Memo1.Lines.Add(DBGrid1.Fields[i].AsString); end; end;

Once you have a running application, click on a row in a DBGrid (to make the row/record active) then click inside the Memo control: Memo gets filled with the values of the fields from the selected (dragged record). Dragging TO a DBGrid While dragging FROM a DBGrid turns to be easy to program, dragging TO a DBGrid is quite a tricky task. Suppose you have a ListBox filled with items, and want to enable a user to drag an item to a particular cell (e.g. to edit one particular field) of a DBGrid - to change the value of a specific field. How do you determine the exact row and the exact cell of a DBGrid where the user has finished the drag operation? As discussed in the "accessing protected members of a component" article, many Delphi components have useful properties and methods that are marked invisible ("protected") to a Delphi developer. When a user finishes the drag operation and drops a value over a cell in a DBGrid, we have to set the active record, place the underlying dataset into edit mode, and assign a value to a particular field. Here's the code needed to handle dragging to a DBGrid: 1. Since we are dropping items from a ListBox control, by handling the OnDragOver of a DBGrid we specify that only items from a ListBox are acceptable to be dropped on a DBGrid: procedure TForm1.DBGrid1DragOver( Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := Source IS TListBox; end; 2. The tricky part goes in the OnDragDrop event handler for DBGrid: procedure TForm1.DBGrid1DragDrop( Sender, Source: TObject; X, Y: Integer); var gc : TGridCoord; s : string; begin gc := THackDBGrid(DBGrid1).MouseCoord(X,Y); if (gc.X > 0) AND (gc.Y > 0) then begin if ListBox1.ItemIndex = -1 then Exit; s := ListBox1.Items[ListBox1.ItemIndex]; with THackDBGrid(DBGrid1) do begin DataSource.DataSet.MoveBy (gc.Y - Row); DataSource.DataSet.Edit; Columns.Items[-1 + gc.X].Field.AsString := s; end; end;

end;

Note: We first use the protected hack to get our hands on the protected MouseCoord method to access the row and column of a DBGrid. This technique is explained in the Selecting and highlighting a row in a DBGrid - "OnMouseOverRow" article. If the target is not the first row (where column titles are by default) and not the first column (where row indicator is by default) we set the current row using the MoveBy method and the Row property. Next, dataset is put into edit mode. Finally, the value of the target field is assigned using the selected item from the ListBox. That's it. If you are looking for more DBGrid related article, check the "TDBGrid to the MAX" collection. More drag and drop ideas can be found here: dragging and dropping with Delphi.

Вам также может понравиться