Mega Code Archive

 
Categories / Delphi / Ide Indy
 

Deciphering the secrets of how to implement the DataGrid in Delphi 2005

Title: Deciphering the 'secrets' of how to implement the DataGrid in Delphi 2005 Question: Delphi is not the first choice for Web developers yet but Delphi 2005 offers for the first time working environment one should seriously consider when starting to code for Web. Especially for those who have previous experience with Delphi the move to the world of ASP.NET can be both smooth and exciting, thanks to the help of Delphi 2005. Answer: ASP.NET is the first web development platform which gives the Delphi programmer the possibility to use the valuable tools he/she has gathered through the years - the OOP Paradigma, the Components luxury and the clear structure of business logic - in the world of Internet. For an experienced Delphi programmer the first encounter with this world is very reassuring: everything sounds familiar, everything seems just one step further in the implementation of concepts, experiments and ideas he/she has already met at some earlier stage of Delphi development. ClientDataSet,PageProducer, InternetExpress - a number of examples can be named. One could say, 'Thinking Delphi' has conquered the Web world. Even though Delphi alone is rarely mentioned, it could be a source of pride and inspiration for the Borland people and we, the Delphi developers, can step into this world with self-confidence. DataGrid is maybe the most central visual component in ASP.NET and therefore an excellent evidence for the advantages of this platform. Unfortunately, the documentation how to implement it in Delphi is quite scarce and some of the approaches can seem enigmatic to beginners. Let's try to decipher them. First glance 3 lines of code are enough to bring the DataGrid to life. It is almost the same we have been accustomed to do in our 'normal', desktop world Delphi:put the component on the form (WebForm), add the Data components like SQLConnection (TDatabase), SQLAdapter (TQuery), DataSet (TDataSource), build the connection string, write the Select command and connect the Data components in the Object Inspector, add the code on the PageLoad event (OnShow) and press the F9. We have the table data exhibited in grid form. Not only the names, but also the objects and the world behind them are not the ones from the good 'old'Delphi, but for now the parallels will help to investigate them. procedure TWebForm1.Page_Load(sender: System.Object; e:System.EventArgs); begin SQLConnection1.Open; SQLDataAdapter1. Fill(DataSet1); DataGrid1.DataBind; end; Ado.Net is a disconnected environment, pretty much like the old ClientDataSet. We use the DataAdapter to provide the data and to fill the DataSet and connect the Grid to it. That's all. The result is quite unpolished, but our next step will be to improve it. One of the important differences in ASP.Net is that here one should take into account the code both into the .pas (Unit) and the .aspx (bridging the Win32 world - Form, .dfm) file. One can see this as a feature or just as a sign of the ASP.NET is not mature enough (normally we don't intervene in the content of the .dfm file as text), but the visual interpretation is not fully automated yet. Nevertheless dividing the two files - the visual one and the code behind - is a very significant advance in Internet programming. Developing an Internet Project in Delphi 2005 is like constant dancing between these two sides; dancing, which sometimes even requires some special dance steps. For now the .aspx file is short and simple. Clicking on the Columns properties button in the Object inspector opens the Columns collection editor. There are several possible types, for now we choose bound columns, giving them the Datafield and the Headertext properties. Turning off the AutoGenerateColumns property of the DataGrid makes the columns customizable. But the access to the Object Inspector of every column is some kind of 'hidden'. Only after clicking the column tag on .aspx file F11 there will be result. The Column object offers plenty of visual properties to change easily the skin of the DataGrid. Delphi 2005 transforms every value entered in the Object inspector here into an attribute in the Object tag in the .aspx file . Unfortunately the BoundColumn has no ID properties. This means there is no way to access it in Delphi code and to change it at runtime. Unless it is transferred to Template. Making the DataGrid move DataGrid has several events for manipulating the data in the Database. In the new ASP language they are named commands: EditCommand, CancelCommand,UpdateCommand, DeleteCommand. But what would bring these events into life? The secret is in adding an EditCommandColumn using the Columns Collection Editor. This Column creates links to the events. Updates are not automatic, one must code them, but the DataGridCommandEventArgs offers the information what and where in the data was changed: procedure TWebForm1.DataGrid1_UpdateCommand(source:System.Object;e:System.Web.UI.WebControls.DataGridCommandEventArgs); var dr:DataRow; begin dr:=DataSet1.Tables[0].Rows.Find(DataGrid1.DataKeys[e.Item.ItemIndex]); //which row to edit dr['picture_name']:= Textbox(e.Item.Cells[0].Controls[0] ).text; //the first visible column in DataGrid SqlDataAdapter1.Update(DataSet1); //write to Database DataGrid1.DataSource :=DataSet1; DataGrid1.DataBind; end; (Delphi 2005 does not supply the UpdateCommand in DataAdapter automatically. If you want to use the Adapter for updating, you should first add the command and the parameters in the Object Inspector). In Edit mode DataGrid will switch the labels with textboxes by default. And we still have only one control into a cell. That is why casting to a TextBox will return the changed value. We can intervene behind the scene - as already mentioned - converting the BoundColumns to TemplateColumns. This happens again in the Columns editor. Now for every transformed column two detached objects are created and can be seen in the .aspx file: ItemTemplate and EditItemTemplate. DataGrid will use the first one for rendering and the second for entering the data. The two new objects are in the grid container - Label and TextBox, but this time they are both design-time accessible through Object Inspector and run-time accessible by the Delphi code. Clicking on the object tag opens the relevant page in the Object Inspector. Besides of the usual bunch of visual properties, the most important one is ID. Setting it to a unique value exposes the integrated into DataGrid web control to accessing from Delphi, so the code above can now be rewritten as follows: dr['picture_name']:= Textbox(e.Item.FindControl('PictureName') ).text; where 'PictureName' is the ID of the Textbox. There is no more need to count the order of the cells and the controls in the DataGrid. Converting the BoundColumn to TemplateColumn can change, with some tricks applied, the whole appearance of the DataGrid. By default Delphi creates text columns also for Boolean fields; this can be easily changed. Just change 'TextBox' and 'Label' in the relevant ItemTemplate with 'CheckBox', and the 'Text' property with the 'Checked' property, so the .aspx code will look like: Access to the new created CheckBoxes can be gained again by using ID and the FindControl function. This approach gives plenty of possibilities to 'recompose' and enrich the DataGrid. We can, for instance, add a validator to the EditItemTemplate. CustomValidator is a web control designed to examine the user values against custom rules. Let's take one example in order to show how to assign events to the 'hidden' into the container objects. Adding a CustomValidator to the EditTemplate is quite straightforward - just add it into .aspx file: Create simple Validate event for the Validator: procedure TWebForm1.CustomValidator1_ServerValidate(source:System.Object; args: System.Web.UI.WebControls.ServerValidateEventArgs); begin args.IsValid := Length(args.Value )2; end; We already know how to find the Validator programmatically in the DataGrid Update event: var v:CustomValidator; begin v:= CustomValidator(e.Item.FindControl('CustomValidator1')); But how to assign the ServerValidate event to the containered control? Well, Following the Delphi logic it shouldn't be difficult. We are used to doing this as follows: v.ServerValidate := CustomValidator1_ServerValidate ; But in .Net world this causes an error: 'read/write not allowed for CLR events. Use Include/Exclude procedure (E2298)Multicast events cannot be assigned to or read from traditional Delphi read/write events. Use Include/Exclude to add or remove methods' . So, let's reformulate the statement: Include(v.ServerValidate, CustomValidator1_ServerValidate); v.Validate ; if not v.IsValid then... Now everything is OK. If the entered value does not confirm the validation rule, the error message text will be shown in the grid - exactly under the wrong input. Delphi without any limits We have discovered how to add controls to the grid cells, how to access them and how to assign events to them in Delphi code. Now we are very close to our final goal: creating the DataGrid only by coding in Delphi, i.e.,without any design time intervention in the .aspx file. Yes, this is possible. There are just a few more tricks in order to make everything work properly. Creating a grid at run-time using the default auto generated columns is easy. Normally one will use the Page.Controls.Add method to put it onto the page after creating an instance. Setting the ID property will prevent dynamical - and therefore unknown to us when coding ID - assigning of default. Have the habit always to assign ID when creating objects in ASP.NET.That is almost always the only secure way to the object in code. Another tip is using a Placeholder and adding the grid instance to it controls collection, not to the Page. This will ensure that the controls added to grid container by programming will occur between the form tags and the error 'control... must be placed inside a form tag with runat=server' will not occur. In order to create the template columns one should implement the ITemplate interface. It contains only one method: InstantiateIn which gives access to the container. Here is a short example of creating a CheckBox - column for a Boolean table values: type MyTemplateColumn = class(System.Object,ITemplate) private FColumnName: string; public procedure set_ColumnName(const Value: string); public property ColumnName:string read FColumnName write set_ColumnName; procedure InstantiateIn(Container:Control); procedure OnDataBinding(sender: System.Object; e: System.EventArgs); end; procedure MyTemplateColumn.InstantiateIn(container: Control); var Cb:CheckBox; begin cb:= CheckBox.Create ; cb.ID :='MycbID'; Container.Controls.Add(cb); Include(cb.DataBinding ,OnDataBinding); end; procedure MyTemplateColumn.OnDataBinding(sender: TObject; e:System.EventArgs); var Mycontainer: DataGridItem; MyCheckBox:CheckBox; begin MyCheckBox:= CheckBox(sender); MyContainer:= DataGridItem(MyCheckBox.NamingContainer); MyCheckBox.Checked := System.Convert.ToBoolean(DataRowView(MyContainer.DataItem).Item [FColumnName ]) ; end; Now we can simply create an instance of the object which implements the Interface and assign it to a template in a template column: tc:= TemplateColumn.Create ; myTemp:= MyTemplateColumn.Create ; myTemp.ColumnName :='picture_IsPrinted'; tc.HeaderText :='Printed'; tc.ItemTemplate := myTemp; That's all! Now we have all the tools to create an 'enriched' with different web controls DataGrid, to edit or delete data, and everything only from Delphi code. The only design step was just putting a Placeholder on the form. The form and the code behind are detached, but they are inter-accessible. It's up to the developer how much visual designing or 'pure' Delphi.NET coding he/she will invest into the project. Examining Delphi 2005 one can find, even though sometimes hidden or not documented enough, real opportunities for Web programming in Delphi mode the older versions didn't possess. With the Partial classes and other improvements ASP.NET 2 will soon change the Delphi internet programming environment again and will further widen these opportunities. Lubomir Rosenstein is a Community member and Delphi developer specializing in database application programming. He has written many articles on computer programming on the Internet. He lives and works in Germany.