https://wiki.freepascal.org/api.php?action=feedcontributions&user=Dejvid&feedformat=atomFree Pascal wiki - User contributions [en]2024-03-19T13:18:04ZUser contributionsMediaWiki 1.35.6https://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30341XML Tutorial2008-07-23T17:50:17Z<p>Dejvid: /* Generating a XML file */ parentNode</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below is the complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
RootNode, parentNode, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
RootNode := xdoc.CreateElement('register');<br />
Xdoc.Appendchild(RootNode); // save root node<br />
<br />
//create a parent node<br />
RootNode:= xdoc.DocumentElement;<br />
parentNode := xdoc.CreateElement('usuario');<br />
TDOMElement(parentNode).SetAttribute('id', '001'); // create atributes to parent node<br />
RootNode.Appendchild(parentNode); // save parent node<br />
<br />
//create a child node<br />
parentNode := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(parentNode).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
parentNode.Appendchild(nofilho); // save node<br />
RootNode.ChildNodes.Item[0].AppendChild(parentNode); // insert child node in respective parent node<br />
<br />
//create a child node<br />
parentNode := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(parentNode).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
parentNode.Appendchild(nofilho); // save node<br />
.ChildNodes.Item[0].AppendChild(parentNode); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
The result will be the XML file below:<br />
<xml><br />
<?xml version="1.0"?><br />
<register><br />
<usuario id="001"><br />
<nome>Fernando</nome><br />
<idade>32</idade><br />
</usuario><br />
</register><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30340XML Tutorial2008-07-23T17:37:28Z<p>Dejvid: /* Generating a XML file */ <xml</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below is the complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
RootNode, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
RootNode := xdoc.CreateElement('register');<br />
Xdoc.Appendchild(RootNode); // save root node<br />
<br />
//create a parent node<br />
RootNode:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
RootNode.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
RootNode.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
The result will be the XML file below:<br />
<xml><br />
<?xml version="1.0"?><br />
<register><br />
<usuario id="001"><br />
<nome>Fernando</nome><br />
<idade>32</idade><br />
</usuario><br />
</register><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30339XML Tutorial2008-07-23T17:31:39Z<p>Dejvid: /* Generating a XML file */ The result will be the XML file below</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below is the complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
RootNode, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
RootNode := xdoc.CreateElement('register');<br />
Xdoc.Appendchild(RootNode); // save root node<br />
<br />
//create a parent node<br />
RootNode:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
RootNode.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
RootNode.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
The result will be the XML file below:<br />
<br />
<?xml version="1.0"?><br />
<register><br />
<usuario id="001"><br />
<nome>Fernando</nome><br />
<idade>32</idade><br />
</usuario><br />
</register><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30338XML Tutorial2008-07-23T17:21:00Z<p>Dejvid: RootNode</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below is the complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
RootNode, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
RootNode := xdoc.CreateElement('register');<br />
Xdoc.Appendchild(RootNode); // save root node<br />
<br />
//create a parent node<br />
RootNode:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
RootNode.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
RootNode.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30337XML Tutorial2008-07-23T17:11:04Z<p>Dejvid: /* Generating a XML file */</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below is the complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('register');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30239XML Tutorial2008-07-21T09:39:40Z<p>Dejvid: /* Modifying a XML document */ position</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
position: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
position := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[position].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30238XML Tutorial2008-07-21T09:37:33Z<p>Dejvid: /* Modifying a XML document */ d</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will locate the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
Posicao: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
Posicao := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[Posicao].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=User:Dejvid&diff=30237User:Dejvid2008-07-21T09:22:42Z<p>Dejvid: </p>
<hr />
<div>Have done [[Lazarus For Delphi Users|Delphi]] programming for some time as a hobby.<br />
<br />
<br />
[[Image:Lazarus IDE Windows.jpg|thumb|250px|right]]<br />
<br />
<br />
[[shell]]<br />
[[Real]]<br />
[[LCL Unicode Support]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=FCL&diff=30236FCL2008-07-21T09:16:11Z<p>Dejvid: Category:Free Component Library</p>
<hr />
<div>{{FCL}}<br />
<br />
The ''Free Component Library'' (FCL) consists of a collection of units, providing components (mostly classes) for common tasks.<br />
It intends to be compatible with Delphi's ''Visual Component Library'' (VCL), but the FCL is restricted to non-visual components.<br />
On the other hand, the FCL also goes beyond the VCL.<br />
<br />
See [http://www.freepascal.org/fcl/fcl.html Free Component Library] for the current development status and an overview of the available components<br />
(though this seems inconsistent with [http://lazarus-ccr.sourceforge.net/docs/fcl/index.html Reference for 'fcl'] in Lazarus).<br />
You can also check the [http://www.freepascal.org/cgi-bin/viewcvs.cgi/trunk/packages/ source repository].<br />
Note that there are also some platform-specific files in the FCL.<br />
<br />
After the example, you can find a list of some FCL components.<br />
<br />
=== Usage ===<br />
<br />
To use an FCL component you need to include its name in a '''uses''' clause of your program or unit (see example below).<br />
The default compiler configuration is set up to search the fcl directory for such units.<br />
You can also set the appropriate search path with a command-line compiler option of the form -Fu<path-to-fcl-units>.<br />
<br />
=== Subpackages ===<br />
<br />
Currently (future 2.2.2+) the FCL is divided into several subpackages:<br />
<br />
* [[fcl-base]] The base units <br />
* [[fcl-async]] Asynchronous I/O (serial?) <br />
* [[fcl-db]] Generic database support + prepackaged drivers<br />
* [[fcl-fpcunit]] Unit testing framework<br />
* [[fcl-image]] Image reading and writing (aka fpimage)<br />
* [[fcl-json]] Routines for javascript object streaming<br />
* [[fcl-net]] Network related units<br />
* [[fcl-passrc]] Pascal language parsing and transformation<br />
* [[fcl-process]] Process controll <br />
* [[fcl-registry]] Registry <br />
* [[fcl-web]] Helper for web development<br />
* [[fcl-xml]] XML (DOM) unit and related units.<br />
<br />
<br />
=== Documentation ===<br />
<br />
Currently, the FCL is not documented (feel free to contribute;<br />
also take a look at [http://lazarus-ccr.sourceforge.net/docs/fcl/index.html Reference for 'fcl']).<br />
For Delphi compatible units, you could consult the Delphi documentation.<br />
You can always take a look at the source files in the [http://www.freepascal.org/cgi-bin/viewcvs.cgi/trunk/packages/ source repository].<br />
<br />
=== Example ===<br />
<br />
The following program illustrates the use of the class TObjectList in the FCL unit Contnrs (providing various containers, including lists, stacks, and queues):<br />
<br />
<delphi> program TObjectListExample;<br />
<br />
uses<br />
Classes, { from RTL for TObject }<br />
Contnrs; { from FCL for TObjectList }<br />
<br />
type<br />
TMyObject = class(TObject) { just some application-specific class }<br />
private<br />
FName: String; { with a string field }<br />
public<br />
constructor Create(AName: String); { and a constructor to create it with a given name }<br />
property Name: String read FName; { and a property to read the name }<br />
end;<br />
<br />
constructor TMyObject.Create(AName: String);<br />
begin<br />
inherited Create;<br />
FName := AName;<br />
end;<br />
<br />
var<br />
VObjectList: TObjectList; { for a list of objects; it is a reference to such a list! }<br />
<br />
begin<br />
VObjectList := TObjectList.Create { create an empty list }<br />
with VObjectList do<br />
begin<br />
Add(TMyObject.Create('Thing One'));<br />
Writeln((Last as TMyObject).Name);<br />
Add(TMyObject.Create('Thing Two'));<br />
Writeln((Last as TMyObject).Name);<br />
end;<br />
end.</delphi><br />
<br />
This program must be compiled in an object-oriented mode, such as -Mobjfpc or -Mdelphi.<br />
<br />
=== FCL Components ===<br />
<br />
This is not an exhaustive list (to avoid duplication of effort).<br />
It only mentions some important components, or components that are otherwise not easy to find.<br />
<br />
; Classes : Base classes for Object Pascal extensions in Delphi mode<br />
; Contnrs : Some common container classes<br />
; FPCUnit : Unit testing framework (based on Kent Beck's unit testing framework, e.g. cf. [http://www.junit.org/ JUnit]), [http://www.freepascal.org/docs-html/fpcunit.pdf documenting article about FPCUnit]<br />
; XMLRead, XMLWrite and DOM : Detailed at the [[XML Tutorial]]<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Category:Stubs&diff=30235Category:Stubs2008-07-21T09:13:52Z<p>Dejvid: Category:supercategory</p>
<hr />
<div>[[Category:supercategory]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30234XML Tutorial2008-07-21T09:11:26Z<p>Dejvid: Free Component Library cat</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will located the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
Posicao: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
Posicao := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[Posicao].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML<br />
<br />
[[Category:Free Component Library]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=30233XML Tutorial2008-07-21T09:10:11Z<p>Dejvid: /* Introduction */ for using XML</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for using XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will located the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
Posicao: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
Posicao := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[Posicao].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=29750XML Tutorial2008-07-04T11:52:12Z<p>Dejvid: /* Generating a XML file */ remove ref to non existant link</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for the use of XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will located the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
Posicao: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
Posicao := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[Posicao].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
(This was taken from a tutorial in DeveLazarus blog )<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML</div>Dejvidhttps://wiki.freepascal.org/index.php?title=XML_Tutorial&diff=29749XML Tutorial2008-07-04T11:25:16Z<p>Dejvid: /* External Links */ dead link removed</p>
<hr />
<div>{{XML Tutorial}}<br />
<br />
== Introduction ==<br />
<br />
The Extensible Markup Language is a [http://www.w3.org/ W3C] recommended language created to interchange information between different systems. It is a text based way to store information. Modern data interchange languages such as XHTML, as well as most WebServices technologies, are based on XML.<br />
<br />
Currently there is a set of units that provides support for XML on Free Pascal. These units are called "XMLRead", "XMLWrite" and "DOM" and they are part of the Free Component Library (FCL) from the Free Pascal Compiler. The FCL is already on the default search path for the compiler on Lazarus, so you only need to add the units to your uses clause in order to get XML support. The FCL is not documented currently (October / 2005), so this short tutorial aims at introducing XML access using those units.<br />
<br />
The XML DOM (Document Object Model) is a set of standarized objects that provide a similar interface for the use of XML on different languages and systems. The standard only specifies the methods, properties and other interface parts of the object, leaving the implementation free for different languages. The FCL currently supports fully the [http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ XML DOM 1.0].<br />
<br />
== Examples ==<br />
<br />
Bellow there is a list of XML data manipulation examples with growing complexity.<br />
<br />
=== Reading a text node ===<br />
<br />
For Delphi Programmers:<br />
Note that when working with TXMLDocument, the text within a Node is considered a separate TEXT Node. As a result, you must access a node's text value as a separate node. Alternatively, the '''TextContent''' property may be used to retrieve content of all text nodes beneath the given one, concatenated together.<br />
<br />
The '''ReadXMLFile''' procedure always creates a new '''TXMLDocument''', so you don't have to create it beforehand. However, be sure to destroy the document by calling '''Free''' when you are done.<br />
<br />
For instance, consider the following XML:<br />
<br />
<xml><br />
<?xml version="1.0" encoding="utf-8"?><br />
<request><br />
<request_type>PUT_FILE</request_type><br />
<username>123</username><br />
<password>abc</password><br />
</request><br />
</xml><br />
<br />
The following code example shows both the correct and the incorrect ways of getting the value of the text node:<br />
<br />
<delphi><br />
var<br />
PassNode: TDOMNode;<br />
Doc: TXMLDocument;<br />
begin<br />
// Read in xml file from disk<br />
ReadXMLFile(Doc, 'c:\xmlfiles\test.xml');<br />
// Retrieve the "password" node<br />
PassNode := Doc.DocumentElement.FindNode('password');<br />
// Write out value of the selected node<br />
WriteLn(PassNode.NodeValue); // will be blank<br />
// The text of the node is actually a separate child node<br />
WriteLn(PassNode.FirstChild.NodeValue); // correctly prints "abc"<br />
// alternatively<br />
WriteLn(PassNode.TextContent);<br />
// finally, free the document<br />
Doc.Free;<br />
end;<br />
</delphi><br />
<br />
=== Printing the names of nodes ===<br />
<br />
A quick note on navigating the DOM tree: When you need to access nodes in sequence, it is best to use '''FirstChild''' and '''NextSibling''' properties (to iterate forward), or '''LastChild''' and '''PreviousSibling''' (to iterate backward). For random access it is possible to use '''ChildNodes''' or '''GetElementsByTagName''' methods, but these will create a TDOMNodeList object which eventually must be freed. This differs from other DOM implementations like MSXML, because FCL implementation is object-based, not interface-based.<br />
<br />
The following example shows how to print the names of nodes to a TMemo placed on a form.<br />
<br />
Bellow is the XML file called 'C:\Programas\teste.xml':<br />
<br />
<xml><br />
<?xml version="1.0" encoding="ISO-8859-1"?><br />
<images directory="mydir"><br />
<imageNode URL="graphic.jpg" title=""><br />
<Peca DestinoX="0" DestinoY="0">Pecacastelo.jpg1.swf</Peca><br />
<Peca DestinoX="0" DestinoY="86">Pecacastelo.jpg2.swf</Peca><br />
</imageNode><br />
</images><br />
</xml><br />
<br />
And here the Pascal code to execute the task:<br />
<br />
<delphi><br />
var<br />
Documento: TXMLDocument;<br />
Child: TDOMNode;<br />
j: Integer;<br />
begin<br />
ReadXMLFile(Documento, 'C:\Programas\teste.xml');<br />
Memo.Lines.Clear;<br />
// using FirstChild and NextSibling properties<br />
Child := Documento.DocumentElement.FirstChild;<br />
while Assigned(Child) do<br />
begin<br />
Memo.Lines.Add(Child.NodeName + ' ' + Child.Attributes.Item[0].NodeValue);<br />
// using ChildNodes method<br />
with Child.ChildNodes do<br />
try<br />
for j := 0 to (Count - 1) do<br />
Memo.Lines.Add(Item[j].NodeName + ' ' + Item[j].FirstChild.NodeValue);<br />
finally<br />
Free;<br />
end;<br />
Child := Child.NextSibling;<br />
end;<br />
Documento.Free;<br />
end;<br />
</delphi><br />
<br />
This will print:<br />
<br />
<pre><br />
imageNode graphic.jpg<br />
Peca Pecacastelo.jpg1.swf<br />
Peca Pecacastelo.jpg1.swf<br />
</pre><br />
<br />
=== Populating a TreeView with XML ===<br />
<br />
One common use of XML files is to parse them and show their contents in a tree like format. You can find the TTreeView component on the "Common Controls" tab on Lazarus.<br />
<br />
The function below will take a XML document previously loaded from a file or generated on code, and will populate a TreeView with it´s contents. The caption of each node will be the content of the first attribute of each node.<br />
<br />
<delphi><br />
procedure TForm1.XML2Tree(tree: TTreeView; XMLDoc: TXMLDocument);<br />
var<br />
iNode: TDOMNode;<br />
<br />
procedure ProcessNode(Node: TDOMNode; TreeNode: TTreeNode);<br />
var<br />
cNode: TDOMNode;<br />
begin<br />
if Node = nil then Exit; // Stops if reached a leaf<br />
<br />
// Adds a node to the tree<br />
TreeNode := tree.Items.AddChild(TreeNode, Node.Attributes[0].NodeValue);<br />
<br />
// Goes to the child node<br />
cNode := Node.FirstChild;<br />
<br />
// Processes all child nodes<br />
while cNode <> nil do<br />
begin<br />
ProcessNode(cNode, TreeNode);<br />
cNode := cNode.NextSibling;<br />
end;<br />
end;<br />
<br />
begin<br />
iNode := XMLDoc.DocumentElement.FirstChild;<br />
while iNode <> nil do<br />
begin<br />
ProcessNode(iNode, nil); // Recursive<br />
iNode := iNode.NextSibling;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Modifying a XML document ===<br />
<br />
The first thing to remember is that TDOMDocument is the "handle" to the DOM. You can get an instance of this class by creating one or by loading a XML document.<br />
<br />
Nodes on the other hand cannot be created like a normal object. You *must* use the methods provided by TDOMDocument to create them, and latter use other methods to put them on the correct place on the tree. This is because nodes must be "owned" by a specific document on DOM.<br />
<br />
Below are some common methods from TDOMDocument:<br />
<br />
<delphi><br />
function CreateElement(const tagName: DOMString): TDOMElement; virtual;<br />
function CreateTextNode(const data: DOMString): TDOMText;<br />
function CreateCDATASection(const data: DOMString): TDOMCDATASection;<br />
virtual;<br />
function CreateAttribute(const name: DOMString): TDOMAttr; virtual;<br />
</delphi><br />
<br />
And here an example method that will located the selected item on a TTreeView and then insert a child node to the XML document it represents. The TreeView must be previously filled with the contents of a XML file using the [[Networking#Populating a TreeView with XML|XML2Tree function]].<br />
<br />
<delphi><br />
procedure TForm1.actAddChildNode(Sender: TObject);<br />
var<br />
Posicao: Integer;<br />
NovoNo: TDomNode;<br />
begin<br />
{*******************************************************************<br />
* Detects the selected element<br />
*******************************************************************}<br />
if TreeView1.Selected = nil then Exit;<br />
<br />
if TreeView1.Selected.Level = 0 then<br />
begin<br />
Posicao := TreeView1.Selected.Index;<br />
<br />
NovoNo := XMLDoc.CreateElement('item');<br />
TDOMElement(NovoNo).SetAttribute('nome', 'Item');<br />
TDOMElement(NovoNo).SetAttribute('arquivo', 'Arquivo');<br />
with XMLDoc.DocumentElement.ChildNodes do<br />
begin<br />
Item[Posicao].AppendChild(NovoNo);<br />
Free;<br />
end;<br />
<br />
{*******************************************************************<br />
* Updates the TreeView<br />
*******************************************************************}<br />
TreeView1.Items.Clear;<br />
XML2Tree(TreeView1, XMLDoc);<br />
end<br />
else if TreeView1.Selected.Level >= 1 then<br />
begin<br />
{*******************************************************************<br />
* This function only works on the first level of the tree,<br />
* but can easely modifyed to work for any number of levels<br />
*******************************************************************}<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Create a TXMLDocument from a string ===<br />
<br />
Given al XML file in MyXmlString, the following code will create it's DOM:<br />
<br />
<delphi><br />
Var<br />
S : TStringStream;<br />
XML : TXMLDocument;<br />
<br />
begin<br />
S:= TStringStream.Create(MyXMLString);<br />
Try<br />
S.Position:=0;<br />
XML:=Nil;<br />
ReadXMLFile(XML,S); // Complete XML document<br />
// Alternatively:<br />
ReadXMLFragment(AParentNode,S); // Read only XML fragment.<br />
Finally<br />
S.Free;<br />
end;<br />
end;<br />
</delphi><br />
<br />
=== Validating a document ===<br />
<br />
Since March 2007, DTD validation facility has been added to the FCL XML parser. Validation is checking that logical structure of the document conforms to the predefined rules, called ''Document Type Definition'' (DTD).<br />
<br />
Here is an example of XML document with a DTD:<br />
<br />
<xml><br />
<?xml version='1.0' encoding='utf-8'?><br />
<!DOCTYPE root [<br />
<!ELEMENT root (child)+ ><br />
<!ELEMENT child (#PCDATA)><br />
]><br />
<root><br />
<child>This is a first child.</child><br />
<child>And this is the second one.</child><br />
</root><br />
</xml><br />
<br />
This DTD specifies that 'root' element must have one or more 'child' elements, and that 'child' elements may have only character data inside. If parser detects any violations from these rules, it will report them.<br />
<br />
Loading such document is slightly more complicated. Let's assume we have XML data in a TStream object:<br />
<br />
<delphi><br />
procedure TMyObject.DOMFromStream(AStream: TStream);<br />
var<br />
Parser: TDOMParser;<br />
Src: TXMLInputSource;<br />
TheDoc: TXMLDocument;<br />
begin<br />
// create a parser object<br />
Parser := TDOMParser.Create;<br />
// and the input source<br />
Src := TXMLInputSource.Create(AStream);<br />
// we want validation<br />
Parser.Options.Validate := True;<br />
// assign a error handler which will receive notifications<br />
Parser.OnError := @ErrorHandler;<br />
// now do the job<br />
Parser.Parse(Src, TheDoc);<br />
// ...and cleanup<br />
Src.Free;<br />
Parser.Free;<br />
end;<br />
<br />
procedure TMyObject.ErrorHandler(E: EXMLReadError);<br />
begin<br />
if E.Severity = esError then // we are interested in validation errors only<br />
writeln(E.Message);<br />
end;<br />
</delphi><br />
<br />
=== Generating a XML file ===<br />
<br />
Below are a complete code to write in a XML file.<br />
You can read the complete tutorial in DeveLazarus blog (see [[XML_Tutorial#External Links|External Links]])<br />
Please, remember DOM and XMLWrite libs in uses clause<br />
<br />
<delphi><br />
unit Unit1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, StdCtrls,<br />
DOM, XMLWrite;<br />
<br />
type<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
Button1: TButton;<br />
Label1: TLabel;<br />
Label2: TLabel;<br />
procedure Button1Click(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end;<br />
<br />
var<br />
Form1: TForm1;<br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
xdoc: TXMLDocument; // variable to document<br />
noraiz, nopai, nofilho: TDOMNode; // variable to nodes<br />
begin<br />
//create a document<br />
xdoc := TXMLDocument.create;<br />
<br />
//create a root node<br />
noraiz := xdoc.CreateElement('cadastro');<br />
Xdoc.Appendchild(noraiz); // save root node<br />
<br />
//create a parent node<br />
noraiz:= xdoc.DocumentElement;<br />
nopai := xdoc.CreateElement('usuario');<br />
TDOMElement(nopai).SetAttribute('id', '001'); // create atributes to parent node<br />
noraiz.Appendchild(nopai); // save parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('nome'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('sexo', 'M'); // create atributes<br />
nofilho := xdoc.CreateTextNode('Fernando'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert child node in respective parent node<br />
<br />
//create a child node<br />
nopai := xdoc.CreateElement('idade'); // create a child node<br />
//TDOMElement(nopai).SetAttribute('ano', '1976'); // create atributes<br />
nofilho := xdoc.CreateTextNode('32'); // insert a value to node<br />
nopai.Appendchild(nofilho); // save node<br />
noraiz.ChildNodes.Item[0].AppendChild(nopai); // insert a childnode in respective parent node<br />
<br />
writeXMLFile(xDoc,'teste.xml'); // write to XML<br />
Xdoc.free; // free memory<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</delphi><br />
<br />
As result, the XML file below:<br />
<br />
<xml> <br />
<?xml version="1.0" ?> <br />
- <cadastro><br />
- <usuario id="001"><br />
<nome>Fernando</nome> <br />
<idade>32</idade> <br />
</usuario><br />
</cadastro><br />
</xml><br />
<br />
--[[User:Fernandosinesio|Fernandosinesio]] 22:28, 24 April 2008 (CEST)fernandosinesio@gmail.com<br />
<br />
== External Links ==<br />
<br />
* [http://www.w3schools.com/xml/default.asp W3Schools] Xml Tutorial<br />
<br />
* [http://www.thomas-zastrow.de/texte/fpcxml/index.php Thomas Zastrow article] FPC and XML</div>Dejvidhttps://wiki.freepascal.org/index.php?title=IDE_Window:_Debugger_Options&diff=27350IDE Window: Debugger Options2008-03-23T12:59:18Z<p>Dejvid: [[category:Debugging</p>
<hr />
<div>[[category:IDE Window|Debugger Options]][[category:IDE Window - Configuration|Debugger Options]][[category:IDE Window - Debug|Options]]<br />
{{IDE Window: Debugger Options}}<br />
<br />
== General ==<br />
<center>[[Image:Debugger_options_general.png]]</center><br />
<br />
=== Debugger type and path ===<br />
<br />
Choose the debugger. <br />
* '''None''' - No debugger. On Run, simply execute the program.<br />
* '''GNU debugger (gdb)''' - The gdb is not part of Lazarus. Unles you are using windows, you must install it yourself. <br />
This is the connector to gdb. You must set the path to gdb (for example /usr/bin/gdb) in the field below.<br />
* '''GNU debugger through SSH''' - for remote debugging. You can use a SSH connection to a another computer and execute gdb there. You need a SSH connection without prompt for password for this. See the SSH documentation on how to do that.<br />
<br />
=== Additional search path ===<br />
<br />
You can add extra directories, where to search for sources, named in the debugging information of the executable. This is used for all projects.<br />
<br />
=== Debugger general options ===<br />
<br />
==== Show message on stop ====<br />
<br />
Enable this to show a notification, when programs stops.<br />
<br />
=== Debugger specific options ===<br />
<br />
Each debugger type has special options.<br />
<br />
==== GNU debugger (gdb) ====<br />
<br />
* '''OverrideRTLCallingConvention''':<br />
This is an internal flag and can stay default.<br />
To handle software exceptions, Lazarus uses some internal breakpoints on locations where these exceptions are raised. When an executable is fully compiled with debug info (thus also the RTL) one can retrieve the agruments passed to those exception routines. However that is usually not the case, so Lazarus has its internal way of examinig the callstack and registers. In order to interprete those arguments correctly it needs to know what the internal calling convention was of those routines. Pre FPC 1.9.x arguments were passed on stack. Nowadays arguments are passed in registers.<br />
In case the the FPC version detection routine draws the wrong conclusion, one can override the calling convention here.<br />
<br />
== Event Log ==<br />
<center>[[Image:Debugger_options_eventlog.png]]</center><br />
<br />
=== General ===<br />
<br />
Eventually logging info should go the the [[IDE Window:Event Log|event log]], for now it is show in the [[IDE Window: Debug Output|debug output]] window.<br />
<br />
* '''Clear log on run''': clear the event log, on each start of the program.<br />
* '''Limit line count to''': keep only the last lines of output.<br />
<br />
=== Messages ===<br />
(not implemented)<br />
<br />
This controls which messages are logged in the event log<br />
<br />
== Language Exceptions ==<br />
<center>[[Image:Debugger_options_language_exceptions.png]]</center><br />
<br />
Programs can raise exceptions. For example, when a file can not be read. Here you setup, if the debugger should stop on an exception.<br />
<br />
=== Ignore these exceptions ===<br />
<br />
Add your exceptions to ignore here.<br />
For example: EDivByZero<br />
<br />
=== Break on Lazarus Exceptions ===<br />
<br />
Uncheck this option if you don't want to stop on any exception<br />
<br />
== OS Exceptions ==<br />
<center>[[Image:Debugger_options_os_exceptions.png]]</center><br />
<br />
=== Signals ===<br />
(not implemented)<br />
<br />
Defines if signals should be handled by the debugger or by the user program. For instance, an div by zero is first signalled by the OS. Then the FPC RTL translates this to an EDivByZero. When the signal is handled by the debugger, the program is stopped before the RTL translates this message. Currently the debugger always stops on a signal.<br />
<br />
[[category:Debugging]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Debugging_-_GDB_tricks&diff=27349Debugging - GDB tricks2008-03-23T12:57:49Z<p>Dejvid: [[category:Debugging</p>
<hr />
<div>= General tricks =<br />
<br />
* Some pascal specific patches were excepted; so if possible set language to pascal "set language pascal"<br />
* To get help, use "help <command>" or "help" to see a category list.<br />
* To view datastructures raw, use the "x" command (see "help x")<br />
<br />
== Examining Type Info ==<br />
<br />
* Q: What function should I use for dumping the type info?<br />
* A: Run x/100c in gdb until it frames. then use 'frame <framenr>' to jump to the correct frame<br />
<br />
== Inspecting an ansistring ==<br />
<br />
* Q: How do I examine an ansistring?<br />
* A: "x/s <stringname>"<br />
[[category:Debugging]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Creating_a_Backtrace_with_GDB&diff=27348Creating a Backtrace with GDB2008-03-23T12:56:24Z<p>Dejvid: [[category:Debugging</p>
<hr />
<div>{{Creating a Backtrace with GDB}}<br />
<br />
== Reasons to create a backtrace ==<br />
<br />
=== Bug Reports ===<br />
Backtraces can be very helpful when submitting bug reports. They will no doubt increase the chances of the bug getting fixed sooner.<br />
<br />
=== Help yourself find a problem ===<br />
A backtrace can really take the pain out of finding a problem with your own program. Often exposing the problem immediately.<br />
<br />
=== Just plain fun!! ===<br />
Okay this might be a stretch, but making backtraces is just plain fun!<br />
<br />
Seriously though, if you can think of another reason to create a backtrace add it here.<br />
<br />
== Creating a Backtrace ==<br />
To create a backtrace with GDB (Gnu DeBugger):<br />
<br />
# You must have the program [http://www.gnu.org/software/gdb/gdb.html GDB]. It is available on most *nix installations. If you downloaded the Windows version of fpc or Lazarus, GDB is already included in the installation. In Lazarus it is set up as the default debugger. Look in Environment->Debugger Options->General Tab. Default path for GDB on Windows is c:\lazarus\mingw\bin\gdb.exe.<br />
# GDB must be in the PATH environment variable. If it's not under windows, from the command prompt type (example): set PATH=%PATH%;c:\lazarus\mingw\bin\<br />
# The program you wish to debug MUST have debugging info included in the executable. If you have used "strip", "upx", compiled with "Xs" or used some other exe shrinker/compressor this won't work!<br />
<br />
<br />
===Windows Users===<br />
<br />
1. Open a MS-DOS prompt<br />
<br />
2. Change to the drive you have your exe on: <br />
C: {press enter}<br />
3. Change to the directory containing the exe:<br />
cd \myprograms\Project1 {press enter}<br />
4. Load your program with GDB:<br />
gdb Project1.exe {press enter}<br />
<br />
<br />
===Linux/BSD Users/MacOSX===<br />
<br />
1. Open your favorite Terminal program<br />
<br />
2. Change to the directory containing the exe:<br />
cd $HOME/myprograms/project1 {press enter}<br />
<br />
3. Load your program with GDB:<br />
gdb project1 {press enter}<br />
<br />
<br />
===You should now see a prompt that looks something like this===<br />
<br />
GNU gdb 6.3.50.20050217-cvs<br />
Copyright 2004 Free Software Foundation, Inc.<br />
GDB is free software, covered by the GNU General Public License, and you are<br />
welcome to change it and/or distribute copies of it under certain conditions.<br />
Type "show copying" to see the conditions.<br />
There is absolutely no warranty for GDB. Type "show warranty" for details.<br />
This GDB was configured as "i686-pc-linux-gnu".<br />
(gdb) _<br />
<br />
'''Let GDB stop when an exception is raised'''<br />
<br />
(gdb) break fpc_raiseexception {press enter}<br />
<br />
'''Now, run your program:'''<br />
<br />
(gdb) run {press enter}<br />
<br />
Your program will run normally, until it crashes or causes an error.<br />
<br />
When your program crashes instead of the usual error "Access Violation" or "Division by Zero" your program will just seem to stop, not respond and not repaint itself. This is because gdb has caught the error and is waiting for you to tell it what to do.<br />
<br />
Switch over to your MS-DOS window or Terminal and you should have a screen something like this:<br />
<br />
Program received signal SIGFPE, Arithmetic exception.<br />
0x080733aa in TFORM1__BUTTON1CLICK (SENDER=0x403d8bd8, this=0x403d77d8) at unit1.pas:36<br />
Current language: auto; currently pascal<br />
(gdb) _<br />
To create a backtrace now type:<br />
backtrace {press enter}<br />
or<br />
bt {press enter}<br />
You should now have a screen that looks something like this:<br />
#0 0x080733aa in TFORM1__BUTTON1CLICK (SENDER=0x403d8bd8, this=0x403d77d8) at unit1.pas:36<br />
#1 0x080ecd78 in TCONTROL__CLICK (this=0x403d8bd8) at control.inc:1768<br />
#2 0x081274bd in TBUTTONCONTROL__CLICK (this=0x403d8bd8) at buttoncontrol.inc:57<br />
#3 0x08129171 in TCUSTOMBUTTON__CLICK (this=0x403d8bd8) at buttons.inc:186<br />
#4 0x08129198 in TCUSTOMBUTTON__WMDEFAULTCLICKED (MESSAGE={MSG = 1031, WPARAM = <br />
136821192, LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, <br />
WPARAMHI = 2087, LPARAMLO = 59548, LPARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416}, <br />
this=0x403d8bd8) at buttons.inc:198<br />
#5 0x0805bbf7 in SYSTEM_TOBJECT_$__DISPATCH$formal ()<br />
#6 0x080ec6e0 in TCONTROL__WNDPROC (THEMESSAGE={MSG = 1031, WPARAM = 136821192, <br />
LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, WPARAMHI = 2087, <br />
LPARAMLO = 59548, PARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416}, <br />
this=0x403d8bd8) at control.inc:1447 <br />
#7 0x080e4ed9 in TWINCONTROL__WNDPROC (MESSAGE={MSG = 1031, WPARAM = 136821192, <br />
LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, WPARAMHI = 2087, <br />
LPARAMLO = 59548, PARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416}, <br />
this=0x403d8bd8) at wincontrol.inc:2154<br />
#8 0x08161955 in DELIVERMESSAGE (TARGET=0x403d8bd8, AMESSAGE=void) at gtkproc.inc:3242<br />
#9 0x0818897c in GTKWSBUTTON_CLICKED (AWIDGET=0x8285380, AINFO=0x40453794) at gtkwsbuttons.pp:112<br />
... { Truncated for simplicity} Complete listing [[Creating_a_Backtrace_with_GDB\Backtrace|here]]<br />
(gdb) _<br />
''Note: If the backtrace it too long you will be prompted to press enter for the next page of the backtrace until it is complete''<br />
<br />
The information displayed here has the sequence of events up to where the error occured. [[Creating_a_Backtrace_with_GDB#Understanding a Backtrace| Understanding a Backtrace]]<br />
<br />
To exit GDB (and your program) you may now type:<br />
quit {press enter}<br />
Or to continue running until the next exception type:<br />
cont {press enter}<br />
<br />
''Hint: At any time while your program is running you may press Ctrl+C (from the Terminal or MS-DOS prompt) to get a "(gdb) _" prompt''<br />
<br />
Note: The use of an english keyboard layout can prevent problems with gdb (e.g. if you special characters like ' \ ').<br />
<br />
== Getting the Backtrace out of the terminal and into an email ==<br />
Currently we have made a backtrace and seen it in the Terminal(or MS-DOS prompt) but how do I get it into an email? There are two ways that I know of:<br />
# Use the copy/paste functionality of your Terminal(or MS-DOS prompt)<br />
# Redirect the output of gdb to a file like so:<br />
gdb project1 > output.txt<br />
Now you can open the file and copy/paste it into your email.<br />
<br />
Caveat: The second method requires that you type blindly. So you would type "run" to start the program and "backtrace" to make a backtrace like normal but nothing would show up on the screen.<br />
<br />
Exception: On Linux you can get around not being able to see with the following command:<br />
touch output.txt & tail output.txt -f & gdb project1 > output.txt<br />
<br />
== Understanding a Backtrace ==<br />
TODO: Write Me :)<br />
<br />
(gdb) bt<br />
#0 0x080733aa in TFORM1__BUTTON1CLICK (SENDER=0x403d8bd8, this=0x403d77d8) at unit1.pas:36<br />
#1 0x080ecd78 in TCONTROL__CLICK (this=0x403d8bd8) at control.inc:1768<br />
#2 0x081274bd in TBUTTONCONTROL__CLICK (this=0x403d8bd8) at buttoncontrol.inc:57<br />
#3 0x08129171 in TCUSTOMBUTTON__CLICK (this=0x403d8bd8) at buttons.inc:186<br />
#4 0x08129198 in TCUSTOMBUTTON__WMDEFAULTCLICKED (MESSAGE={MSG = 1031, WPARAM = 136821192, <br />
LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, WPARAMHI = 2087, <br />
LPARAMLO = 59548, LPARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416}, <br />
this=0x403d8bd8) at buttons.inc:198<br />
#5 0x0805bbf7 in SYSTEM_TOBJECT_$__DISPATCH$formal ()<br />
#6 0x080ec6e0 in TCONTROL__WNDPROC (THEMESSAGE={MSG = 1031, WPARAM = 136821192, <br />
LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, WPARAMHI = 2087, <br />
LPARAMLO = 59548, LPARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416}, <br />
this=0x403d8bd8) at control.inc:1447<br />
#7 0x080e4ed9 in TWINCONTROL__WNDPROC (MESSAGE={MSG = 1031, WPARAM = 136821192, <br />
LPARAM = -1073747812, RESULT = 1075866658, WPARAMLO = 47560, WPARAMHI = 2087, <br />
LPARAMLO = 59548, LPARAMHI = 49151, RESULTLO = 27682, RESULTHI = 16416},<br />
this=0x403d8bd8) at wincontrol.inc:2154<br />
#8 0x08161955 in DELIVERMESSAGE (TARGET=0x403d8bd8, AMESSAGE=void) at gtkproc.inc:3242<br />
#9 0x0818897c in GTKWSBUTTON_CLICKED (AWIDGET=0x8285380, AINFO=0x40453794) at gtkwsbuttons.pp:112<br />
#10 0x401d7877 in gtk_marshal_NONE__NONE () from /usr/lib/libgtk-1.2.so.0<br />
#11 0x40206e5a in gtk_signal_remove_emission_hook () from /usr/lib/libgtk-1.2.so.0<br />
#12 0x4020616a in gtk_signal_set_funcs () from /usr/lib/libgtk-1.2.so.0<br />
#13 0x40204234 in gtk_signal_emit () from /usr/lib/libgtk-1.2.so.0<br />
#14 0x40176e1c in gtk_button_clicked () from /usr/lib/libgtk-1.2.so.0<br />
#15 0x4017838a in gtk_button_get_relief () from /usr/lib/libgtk-1.2.so.0<br />
#16 0x401d7877 in gtk_marshal_NONE__NONE () from /usr/lib/libgtk-1.2.so.0<br />
#17 0x4020607b in gtk_signal_set_funcs () from /usr/lib/libgtk-1.2.so.0<br />
#18 0x40204234 in gtk_signal_emit () from /usr/lib/libgtk-1.2.so.0<br />
#19 0x40176d60 in gtk_button_released () from /usr/lib/libgtk-1.2.so.0<br />
#20 0x40177d26 in gtk_button_get_relief () from /usr/lib/libgtk-1.2.so.0<br />
#21 0x401d764d in gtk_marshal_BOOL__POINTER () from /usr/lib/libgtk-1.2.so.0<br />
#22 0x4020619f in gtk_signal_set_funcs () from /usr/lib/libgtk-1.2.so.0<br />
#23 0x40204234 in gtk_signal_emit () from /usr/lib/libgtk-1.2.so.0<br />
#24 0x40239fd9 in gtk_widget_event () from /usr/lib/libgtk-1.2.so.0<br />
#25 0x401d74d0 in gtk_propagate_event () from /usr/lib/libgtk-1.2.so.0<br />
#26 0x401d65c1 in gtk_main_do_event () from /usr/lib/libgtk-1.2.so.0<br />
#27 0x40065b44 in gdk_wm_protocols_filter () from /usr/lib/libgdk-1.2.so.0<br />
#28 0x4003de75 in g_get_current_time () from /usr/lib/libglib-1.2.so.0<br />
#29 0x4003e32c in g_get_current_time () from /usr/lib/libglib-1.2.so.0<br />
#30 0x4003e4f5 in g_main_iteration () from /usr/lib/libglib-1.2.so.0<br />
#31 0x401d6388 in gtk_main_iteration_do () from /usr/lib/libgtk-1.2.so.0<br />
#32 0x080b4d26 in TGTKWIDGETSET__WAITMESSAGE (this=0x40473014) at gtkobject.inc:1427<br />
#33 0x08070073 in TAPPLICATION__IDLE (this=0x4045b014) at application.inc:319<br />
#34 0x08070ffb in TAPPLICATION__HANDLEMESSAGE (this=0x4045b014) at application.inc:827<br />
#35 0x08071380 in RUNMESSAGE (parentfp=0xbffff5a4) at application.inc:933<br />
#36 0x080712d7 in TAPPLICATION__RUN (this=0x4045b014) at application.inc:944<br />
#37 0x080531c3 in main () at project1.lpr:13<br />
(gdb)<br />
<br />
This Backtrace was produced using this code:<br />
<br />
30. procedure TForm1.Button1Click(Sender: TObject);<br />
31. var<br />
32. X, Y, Z: Integer;<br />
33. begin<br />
34. X := 0;<br />
35. Y := 0;<br />
36. Z := X div Y;<br />
37. end;<br />
[[category:Debugging]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Databases&diff=25769Databases2007-12-23T12:05:24Z<p>Dejvid: /* Introduction */ '''Warning''':</p>
<hr />
<div>{{Databases}}<br />
<br />
Work in progress..<br />
<br />
== Introduction ==<br />
<br />
This page will be an entrance to the topic 'Lazarus and databases'. The following table provides an overview about the supported databases. It needs to be updated soon, because it is based on the time when FPC 1.9.7 was released. '''Warning''': You should only install the database components for which you have the client libraries installed (if the database need client libaries). Otherwise Lazarus could fail to start because of missing files. You would then have no choice but to reinstall Lazarus, because uninstalling the component isn't possible.<br />
<br />
=== Table 1 ===<br />
{| BORDER="1" CELLSPACING="0"<br />
!STYLE="background:#ffdead;"|'''Database'''<br />
!STYLE="background:#ffdead;"|'''Package name'''<br />
!STYLE="background:#ffdead;"|'''Need client lib?'''<br />
!STYLE="background:#ffdead;"|'''Need server?'''<br />
!STYLE="background:#ffdead;"|'''Supported versions'''<br />
!STYLE="background:#ffdead;"|'''Supported platforms'''<br />
|----<br />
|'''Textfiles'''||sdf||No||No|| - || All<br />
|----<br />
|'''In memory'''||memds||No||No|| - || All<br />
|----<br />
|'''DBase'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| III+, IV, VII || All<br />
|----<br />
|'''FoxPro'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| - || All<br />
|----<br />
|'''Paradox'''||[[TParadoxDataSet | TParadoxDataSet]]||No||No|| up to Table Level 7 (and up ??) || All<br />
|----<br />
|[http://www.sqlite.org/ '''SQLite''']||SQLite||Yes||No|| - || -<br />
|----<br />
|[http://www.mysql.com/ '''MySQL''']||SQLdb||Yes||Yes|| 3 - 4.0 || i386: Linux, Win32<br />
|----<br />
|[http://www.firebirdsql.org/ '''Firebird''']||IBase||Yes||Yes||1 - 1.5|| i386: Linux, Win32<br />
|----<br />
|[http://www.postgresql.org/ '''PostgreSQL''']||SQLdb||Yes||Yes|| 6.6 - 8 || i386: Linux, Win32<br />
|----<br />
|'''ODBC'''||[[ODBCConn|SQLdb]]||Yes||Depends|| 3.x <sup>1)</sup> || i386: Linux, Win32<br />
|----<br />
|[http://www.borland.com/us/products/interbase/index.html '''Interbase''']||IBase||Yes||Yes||4 - 6|| i386: Linux, Win32<br />
|----<br />
|[http://www.oracle.com/ '''Oracle''']||SQLdb||Yes||Yes|| - || - <br />
|}<br />
<br />
<sup>1)</sup> This version number refers to the ODBC standard, not to the version number of a driver or driver manager. There are ODBC 3.x drivers for most DBMSs.<br />
<br />
== The bindings to the database-clients ==<br />
<br />
If you want to use one of the databases-client libraries, those libraries has to be installed. Not only on the computer you're programming on, but also on the computers where the application must run. Note that some databases (in particular MySQL) only work if the bindings which are compiled in the application are from the same version as those of the installed libraries. You can find out how to install those libraries (.so files on *nix systems, and .dll's on windows) on the website of the database-developers. The binding units can be found in the packages/base directory in the fpc-sources. They basically consist of the client-api calls like mysql_connect_database, which are completely different for each database. It is possible to write database applications using these units, but it is usually far more work and bug-sensitive than using the DB-unit Lazarus components.<br />
<br />
Most of these bindings-packages are hard-linked to the client-libraries. This means that if the application is compiled with one of these units in it, the whole application can not be linked if the client libraries are not available on the workstation. This means that if you do not have installed - for example - MySQL on your computer, and you are using the mysql4.pp unit in your program. The program will not link. If you succeed to compile the program on a computer which has MySQL installed, it still won't even start on any other workstation without the appropriate MySQL-libraries.<br />
<br />
To avoid such problems some of the packages are also able to link dynamically to the libraries. Before any calls to those libraries can be made, the unit has to be 'initialized'. This initialization fails if the database-client isn't installed on the computer. If the program is ready using the client-library, the unit has to be 'released'.<br />
<br />
== Additional information about Lazarus and databases ==<br />
<br />
* FAQ: [[Lazarus DB Faq|Lazarus database FAQ]]<br />
* Tutorial: [[Lazarus Database Tutorial]]<br />
* MySQL: [[MySQLDatabases]]<br />
* Firebird: [[Firebird in action]]<br />
* ODBC: [[ODBCConn]]<br />
<br />
== Database packages contained in Lazarus ==<br />
<br />
=== sqldblaz.lpk ===<br />
This package provides access to different databases (e.g. MySQL or Interbase/Firebird). The components (TSQLQuery, TSQLTransaction, TIBConnection, TODBCConnection, TOracleConnection, TMySQL40Connection, TMySQL41Connection, TMySQL50Connection, TPQConnection) are on the 'SQLdb' tab in the component palette.<br />
*[[SQLdb Package]]<br />
<br />
=== dbflaz.lpk ===<br />
This package provides access to dBase and FoxPro databases. You can get more information in the [[Lazarus Tdbf Tutorial]]. The TDbf component is on the 'Data Access' tab in the component palette.<br />
<br />
<strike>=== interbaselaz.lpk ===<br />
This package provides access to Interbase and Firebird databases. The components are similar to Delphi but far from Delphi complexity (only e components included: TIBDatabase, TIBTransaction, TIBQuery). Furthermore these components allow only a read only access. The package must be installed manually. It depends on an installed gds32.dll file (under Windows). If you install the package without this file, Lazarus fails to start. The components are on the 'Interbase' tab in the component palette.</strike> The package was removed in the meantime.<br />
<br />
=== sqlitelaz.lpk ===<br />
This package provides access to SQLite databases. You can get more information in the [[Lazarus Database Tutorial]].<br />
<br />
=== sdflaz.lpk ===<br />
The component TSdfDataSet can be found on the 'Data Access' tab in the component palette.<br />
<br />
=== lazreport.lpk ===<br />
The homepage of the report generator is [http://lazreport.sourceforge.net/ http://lazreport.sourceforge.net/].<br />
More informationen (et al. an additional link) can be found [[Projects_using_Lazarus#LazReport| here]].<br />
LazReport depends on the Printer4Lazarus package.<br />
With revision 11950 LazReport was included in the Lazarus SVN repository.<br />
<br />
== External packages / libraries ==<br />
<br />
=== Zeos library ===<br />
This library provides access to different databases. Information about the interaction between Zeos and Lazarus can be found in the [[Zeos tutorial]].<br />
<br />
=== Pascal Data Objects ===<br />
There is now an alternative. The functions introduced with MySQL 4.1 and 5.0 like prepared statements, binding, and stored procedures are supported by database API called Pascal Data Objects, which is inspired by PHP Data Objects. All the code and documentation necessary to use this new API is available on Sourceforge:<br />
<br />
http://pdo.sourceforge.net<br />
<br />
Jan 30, 2007: PDO has added drivers for Firebird 1.5 and Firebird 2.0<br />
<br />
=== TPSQL ===<br />
These components provide access via TCP/IP to PostgreSQL databases. You can find more information on [[TPSQL|this page]].<br />
<br />
=== FIBL ===<br />
These components provide access to Interbase and Firebird databases. The homepage is [http://sourceforge.net/projects/fibl http://sourceforge.net/projects/fibl].<br />
<br />
=== FBLib Firebird Library ===<br />
<br />
[http://fblib.altervista.org/ FBLib] is an open Source Library No Data Aware for direct access to Firebird Relational Database from Borland Delphi/Kylix, Free Pascal and Lazarus.<br />
<br />
Current Features include:<br />
<br />
* Direct Access to Firebird 1.0.x, 1.5.x and 2.x Classic or SuperServer<br />
* Multiplatform [Win32,Gnu/Linux,FreeBSD)<br />
* Automatic select client library 'fbclient' or 'gds32'<br />
* Query with params<br />
* Support SQL Dialect 1/3<br />
* LGPL License agreement<br />
* Extract Metadata<br />
* Simple Script Parser<br />
* Only 100-150 KB added into final EXE<br />
* Support BLOB Fields<br />
* Export Data to HTML SQL Script<br />
* Service manager (backup,restore,gfix...)<br />
* Events Alerter<br />
<br />
You can download documentation on [http://fblib.altervista.org/ FBLib's website].<br />
<br />
=== Unified Interbase ===<br />
<br />
UIB provides access to Interbase, Firebird and YAFFIL databases. The homepage is [http://www.progdigy.com www.progdigy.com]. A svn repository is available under https://uib.svn.sourceforge.net/svnroot/uib .<br />
<br />
=== TechInsite Object Persistence Framework (tiOPF) ===<br />
More informationen about tiOPF can be found on this [[tiOPF|page]].</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Databases&diff=25768Databases2007-12-23T12:04:38Z<p>Dejvid: /* Introduction */ You would then have no choice but to reinstall Lazarus</p>
<hr />
<div>{{Databases}}<br />
<br />
Work in progress..<br />
<br />
== Introduction ==<br />
<br />
This page will be an entrance to the topic 'Lazarus and databases'. The following table provides an overview about the supported databases. It needs to be updated soon, because it is based on the time when FPC 1.9.7 was released. One hint: You should only install the database components for which you have the client libraries installed (if the database need client libaries). Otherwise Lazarus could fail to start because of missing files. You would then have no choice but to reinstall Lazarus, because uninstalling the component isn't possible.<br />
<br />
=== Table 1 ===<br />
{| BORDER="1" CELLSPACING="0"<br />
!STYLE="background:#ffdead;"|'''Database'''<br />
!STYLE="background:#ffdead;"|'''Package name'''<br />
!STYLE="background:#ffdead;"|'''Need client lib?'''<br />
!STYLE="background:#ffdead;"|'''Need server?'''<br />
!STYLE="background:#ffdead;"|'''Supported versions'''<br />
!STYLE="background:#ffdead;"|'''Supported platforms'''<br />
|----<br />
|'''Textfiles'''||sdf||No||No|| - || All<br />
|----<br />
|'''In memory'''||memds||No||No|| - || All<br />
|----<br />
|'''DBase'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| III+, IV, VII || All<br />
|----<br />
|'''FoxPro'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| - || All<br />
|----<br />
|'''Paradox'''||[[TParadoxDataSet | TParadoxDataSet]]||No||No|| up to Table Level 7 (and up ??) || All<br />
|----<br />
|[http://www.sqlite.org/ '''SQLite''']||SQLite||Yes||No|| - || -<br />
|----<br />
|[http://www.mysql.com/ '''MySQL''']||SQLdb||Yes||Yes|| 3 - 4.0 || i386: Linux, Win32<br />
|----<br />
|[http://www.firebirdsql.org/ '''Firebird''']||IBase||Yes||Yes||1 - 1.5|| i386: Linux, Win32<br />
|----<br />
|[http://www.postgresql.org/ '''PostgreSQL''']||SQLdb||Yes||Yes|| 6.6 - 8 || i386: Linux, Win32<br />
|----<br />
|'''ODBC'''||[[ODBCConn|SQLdb]]||Yes||Depends|| 3.x <sup>1)</sup> || i386: Linux, Win32<br />
|----<br />
|[http://www.borland.com/us/products/interbase/index.html '''Interbase''']||IBase||Yes||Yes||4 - 6|| i386: Linux, Win32<br />
|----<br />
|[http://www.oracle.com/ '''Oracle''']||SQLdb||Yes||Yes|| - || - <br />
|}<br />
<br />
<sup>1)</sup> This version number refers to the ODBC standard, not to the version number of a driver or driver manager. There are ODBC 3.x drivers for most DBMSs.<br />
<br />
== The bindings to the database-clients ==<br />
<br />
If you want to use one of the databases-client libraries, those libraries has to be installed. Not only on the computer you're programming on, but also on the computers where the application must run. Note that some databases (in particular MySQL) only work if the bindings which are compiled in the application are from the same version as those of the installed libraries. You can find out how to install those libraries (.so files on *nix systems, and .dll's on windows) on the website of the database-developers. The binding units can be found in the packages/base directory in the fpc-sources. They basically consist of the client-api calls like mysql_connect_database, which are completely different for each database. It is possible to write database applications using these units, but it is usually far more work and bug-sensitive than using the DB-unit Lazarus components.<br />
<br />
Most of these bindings-packages are hard-linked to the client-libraries. This means that if the application is compiled with one of these units in it, the whole application can not be linked if the client libraries are not available on the workstation. This means that if you do not have installed - for example - MySQL on your computer, and you are using the mysql4.pp unit in your program. The program will not link. If you succeed to compile the program on a computer which has MySQL installed, it still won't even start on any other workstation without the appropriate MySQL-libraries.<br />
<br />
To avoid such problems some of the packages are also able to link dynamically to the libraries. Before any calls to those libraries can be made, the unit has to be 'initialized'. This initialization fails if the database-client isn't installed on the computer. If the program is ready using the client-library, the unit has to be 'released'.<br />
<br />
== Additional information about Lazarus and databases ==<br />
<br />
* FAQ: [[Lazarus DB Faq|Lazarus database FAQ]]<br />
* Tutorial: [[Lazarus Database Tutorial]]<br />
* MySQL: [[MySQLDatabases]]<br />
* Firebird: [[Firebird in action]]<br />
* ODBC: [[ODBCConn]]<br />
<br />
== Database packages contained in Lazarus ==<br />
<br />
=== sqldblaz.lpk ===<br />
This package provides access to different databases (e.g. MySQL or Interbase/Firebird). The components (TSQLQuery, TSQLTransaction, TIBConnection, TODBCConnection, TOracleConnection, TMySQL40Connection, TMySQL41Connection, TMySQL50Connection, TPQConnection) are on the 'SQLdb' tab in the component palette.<br />
*[[SQLdb Package]]<br />
<br />
=== dbflaz.lpk ===<br />
This package provides access to dBase and FoxPro databases. You can get more information in the [[Lazarus Tdbf Tutorial]]. The TDbf component is on the 'Data Access' tab in the component palette.<br />
<br />
<strike>=== interbaselaz.lpk ===<br />
This package provides access to Interbase and Firebird databases. The components are similar to Delphi but far from Delphi complexity (only e components included: TIBDatabase, TIBTransaction, TIBQuery). Furthermore these components allow only a read only access. The package must be installed manually. It depends on an installed gds32.dll file (under Windows). If you install the package without this file, Lazarus fails to start. The components are on the 'Interbase' tab in the component palette.</strike> The package was removed in the meantime.<br />
<br />
=== sqlitelaz.lpk ===<br />
This package provides access to SQLite databases. You can get more information in the [[Lazarus Database Tutorial]].<br />
<br />
=== sdflaz.lpk ===<br />
The component TSdfDataSet can be found on the 'Data Access' tab in the component palette.<br />
<br />
=== lazreport.lpk ===<br />
The homepage of the report generator is [http://lazreport.sourceforge.net/ http://lazreport.sourceforge.net/].<br />
More informationen (et al. an additional link) can be found [[Projects_using_Lazarus#LazReport| here]].<br />
LazReport depends on the Printer4Lazarus package.<br />
With revision 11950 LazReport was included in the Lazarus SVN repository.<br />
<br />
== External packages / libraries ==<br />
<br />
=== Zeos library ===<br />
This library provides access to different databases. Information about the interaction between Zeos and Lazarus can be found in the [[Zeos tutorial]].<br />
<br />
=== Pascal Data Objects ===<br />
There is now an alternative. The functions introduced with MySQL 4.1 and 5.0 like prepared statements, binding, and stored procedures are supported by database API called Pascal Data Objects, which is inspired by PHP Data Objects. All the code and documentation necessary to use this new API is available on Sourceforge:<br />
<br />
http://pdo.sourceforge.net<br />
<br />
Jan 30, 2007: PDO has added drivers for Firebird 1.5 and Firebird 2.0<br />
<br />
=== TPSQL ===<br />
These components provide access via TCP/IP to PostgreSQL databases. You can find more information on [[TPSQL|this page]].<br />
<br />
=== FIBL ===<br />
These components provide access to Interbase and Firebird databases. The homepage is [http://sourceforge.net/projects/fibl http://sourceforge.net/projects/fibl].<br />
<br />
=== FBLib Firebird Library ===<br />
<br />
[http://fblib.altervista.org/ FBLib] is an open Source Library No Data Aware for direct access to Firebird Relational Database from Borland Delphi/Kylix, Free Pascal and Lazarus.<br />
<br />
Current Features include:<br />
<br />
* Direct Access to Firebird 1.0.x, 1.5.x and 2.x Classic or SuperServer<br />
* Multiplatform [Win32,Gnu/Linux,FreeBSD)<br />
* Automatic select client library 'fbclient' or 'gds32'<br />
* Query with params<br />
* Support SQL Dialect 1/3<br />
* LGPL License agreement<br />
* Extract Metadata<br />
* Simple Script Parser<br />
* Only 100-150 KB added into final EXE<br />
* Support BLOB Fields<br />
* Export Data to HTML SQL Script<br />
* Service manager (backup,restore,gfix...)<br />
* Events Alerter<br />
<br />
You can download documentation on [http://fblib.altervista.org/ FBLib's website].<br />
<br />
=== Unified Interbase ===<br />
<br />
UIB provides access to Interbase, Firebird and YAFFIL databases. The homepage is [http://www.progdigy.com www.progdigy.com]. A svn repository is available under https://uib.svn.sourceforge.net/svnroot/uib .<br />
<br />
=== TechInsite Object Persistence Framework (tiOPF) ===<br />
More informationen about tiOPF can be found on this [[tiOPF|page]].</div>Dejvidhttps://wiki.freepascal.org/index.php?title=MySQLDatabases&diff=23777MySQLDatabases2007-08-20T11:07:02Z<p>Dejvid: /* Sources */ Category:Databases</p>
<hr />
<div>{{MySQLDatabases}}<br />
<br />
'''Work in progress'''<br />
<br />
==Introduction==<br />
<br />
In the [[Lazarus Database Tutorial#Lazarus and MySQL|Database tutorial]] we saw a first attempt to connect to a MySQL server. We did not use any components, either visual or non-visual at that time. This page will explain how to connect to a MySQL server in a (perhaps) easier way.<br />
==Available Components ==<br />
<br />
===MySQL Components===<br />
If you look in the $Lazarus/components/mysql directory you will find two lazarus packages. There is a package for MySQL version 3.2x (mysql3laz.lpk) and one for MySQL version 4.x (mysql4laz.lpk). If you did not do so yet, install the appropriate package, depending on the server version. Remember that it is not possible to use the package for version 3 to connect to a server running version 4 or vice versa. <br />
When you've installed this package you will see a new tab in the component palet, called MySQL. On this tab there are two components, TMySQLDatabase and TMySQLDataset.<br />
<br />
NB. Before trying to install this components in Lazarus be sure that you have appropiated MySQL client libraries or else Lazarus may not start after install.<br />
===SQLdb Components===<br />
Another possibility is the installation of the package in the $Lazarus/components/sqldb directory. In this directory you see a package file called sqldblaz.lpk. You need to install this package and the mysql4connlaz.lpk from the $Lazarus/components/sqldb/mysql directory. The first package contains some general components used for all databases. These component are TSQLTransaction and TSQLQuery and can be found on the new SQLdb tab. After installation of the mysql4connlaz.lpk you will find a third component on the SQLdb tab called TMySQLConnection (depicted by a dolphin).<br />
<br />
If you can't find the $Lazarus/components/sqldb/mysql directory and mysql4connlaz.lpk, then this means that you are using a newer version in which mysql4connlaz.lpk merged with sqldblaz.lpk. It is OK, if you instal sqldblaz.lpk, you will get all components mentioned in the same Lazarus IDE toolbar TAB.<br />
<br />
If you do not know how to install components / packages, have a look at this [[Install Packages|page]] for an "Install Howto".<br />
As the SQLdb components are the more general and can be used for other databases just by replacing the TMySQLConnection with for instance a TIBConnection, we will develop a program with the SQLdb components.<br />
<br />
==Explanation of the used components==<br />
===TMySQLConnection===<br />
<br />
The TMySQLConnection is used to store parameters to connect to the database server. It enables you to set the host to connect to and the userid and password to use in the connection. Another property of the TMySQLConnection is used to indicate the database you want to use. The 'LoginPrompt' is not functional yet, so make sure that next to the HostName and DatabaseName the UserName and Password properties have values as well before you try to open the connection.<br />
Be sure to use a TSQLTransaction as well and connect it to your MySQLConnection by setting the Transaction property, or you will not be able to open a SQLQuery.<br />
<br />
Note. In latest SVN version of Lazarus you will find 3 MySQLConnection components. These are TMySQL40Connection, TMySQL41Connection and TMySQL50Connection. Make sure you use the correct one to connect to your server.<br />
So if you are running MySQL 4.1 use the TMySQL41Connection.<br />
<br />
===TSQLTransaction===<br />
A SQLTransaction is needed for some internal housekeeping. A SQLTransaction is automatically activated when you open a dataset using it. Closing a connection also deactivates the related transaction and closes all datasets using it.<br />
<br />
===TSQLQuery===<br />
TSQLQuery is used to execute SQLstatements on the server. You can retrieve data by setting the SQL to some SELECT statement and call the Open method. Or you can manipulate data by issuing some an INSERT, DELETE or UPDATE statement. In the latter case you should not use the Open method but the ExecSQL method.<br />
<br />
===TDataSource===<br />
<br />
A datasource provides the connection between the visible data aware components like DBEdit, DBGrid and a dataset. It makes the data available for the data aware components to display.<br />
A datasource can only be connected to a single dataset at a time but there can be several data aware components connected.<br />
<br />
===TDBGrid===<br />
A DBGrid can be used to present the data retrieved by a Dataset. The DBGrid needs a datasource to connect to a dataset. When the dataset is opened, the DBgrid will automatically be populated with the data.<br />
<br />
==Our program==<br />
<br />
===The basics===<br />
We will try to make a program based on the one made [[Lazarus Database Tutorial/nl#Verbinding met MySQL vanuit een Lazarus Programma|here (in Dutch)]] which is based on the [[Lazarus Database Tutorial#Connecting to MySQL from a Lazarus Application|original (in English)]] by [[user:Kirkpatc|Chris]].<br />
<br />
===The main form===<br />
We will use the same main screen and build all functionality from scratch :) As you will see there is a lot less to take care of, because the components really take away all the hard stuff! So lets start by making a screen that looks like this.<br> [[image:Trymysql.png]]<br><br />
From the SQLdb-tab place a TMySQLConnection, a TSQLTransaction and a TSQLQuery [[image:Components.png]] on this form. Don't change the default names given to this components. Except for the Connection component. To make this article the same for all versions of MySQL, name your MySQL??Connection component: MySQLConnection1<br />
We have to link these components together so they can do their job. So the following properties have to be set:<br />
{|<br />
|-<br />
| '''Component''' || '''Property''' || '''Value'''<br />
|-<br />
| MySQLConnection1 || Transaction || SQLTransaction1<br />
|-<br />
| SQLTransaction1 || Database || MySQLConnection1<br />
|-<br />
| SQLQuery1 || Database || MySQLConnection1<br />
|-<br />
| SQLQuery1 || Transaction || SQLTransaction1<br />
|}<br />
The Transaction-property of SQLQuery1 will automatically be set if you have set the Transaction property of MySQLConnection1 first. When you set this, you will notice that SQLTransaction1.Database has been set to MySQLConnection1.<br />
<br />
As said earlier: Make sure you are using the correct Connection component for your version of MySQL server.<br />
<br />
===The code===<br />
<br />
As you can see in the screen dump the only buttons available on start of the program are "Connect to server" and "Exit". For the other buttons to work we need more information so these are disabled. We could decide to disable "Connect to Server" as well until the information for the host, username and password has been given. I decided against this because our user might think: "Nothing seems possible, so let's hit exit." :)<br />
<br />
Before I start giving you any code I would like to stress that there should be more exception handling in the code. Critical sections should be placed in<br />
try ... finally<br />
or<br />
try ... except<br />
constructions.<br />
<br />
====Connect to a server====<br />
The first thing we have to do is get connected to our server. As when connecting we don't know what databases are available on the server we will ask for a list of databases on connecting. However there is one catch, to make the connection we have to enter a valid DatabaseName in the properties of the MySQLConnection. You will see in the code that I am using the "mysql" database. This database is used by mysql for some housekeeping so it will always be there.<br />
procedure TFormTryMySQL.ConnectButtonClick(Sender: TObject);<br />
begin<br />
// Check if we have an active connection. If so, let's close it.<br />
if MySQLConnection1.Connected then CloseConnection(Sender);<br />
// Set the connection parameters.<br />
MySQLConnection1.HostName := HostEdit.Text;<br />
MySQLConnection1.UserName := UserEdit.Text;<br />
MySQLConnection1.Password := PasswdEdit.Text;<br />
MySQLConnection1.DatabaseName := 'mysql'; // MySQL is allways there!<br />
ShowString('Opening a connection to server: ' + HostEdit.Text);<br />
MySQLConnection1.Open;<br />
// First lets get a list of available databases.<br />
if MySQLConnection1.Connected then begin<br />
ShowString('Connected to server: ' + HostEdit.Text);<br />
ShowString('Retrieving list of available databases.');<br />
SQLQuery1.SQL.Text := 'show databases';<br />
SQLQuery1.Open;<br />
while not SQLQuery1.EOF do begin<br />
DatabaseComboBox.Items.Add(SQLQuery1.Fields[0].AsString);<br />
SQLQuery1.Next;<br />
end;<br />
SQLQuery1.Close;<br />
ShowString('List of databases received!');<br />
end;<br />
end;<br />
<br />
The first thing we do is check to see if we are connected to a server, if we are then we call a private method "CloseConnection". In this method some more housekeeping is done. like disabling buttons and clearing comboboxes and listboxes. Then we set the necessary parameters to connect to server.<br />
:''Throughout our program you may see calls to ShowString. This method adds a line to the memo on our form which acts like a kind of log.''<br />
With the parameters set, we can connect to the server. This is done by calling<br />
MySQLConnection1.Open;<br />
In a proper application one would place this in an exception handling construct to present a friendly message to the user if the connection failed.<br />
When we are connected we want to get a list of databases from the server. To get data from the server a TSQLQuery is used. The SQL property is used to store the SQL-statement send to the server. MySQL knows the "SHOW DATABASES" command to get the list of databases. So after we have set the SQL-text, we call<br />
SQLQuery1.Open;<br />
The result set of a SQLQuery can be examined through the fields property. As you can see we iterate through the records by calling<br />
SQLQuery1.Next;<br />
When we have added all available databases to our combobox, we close the SQLQuery again.<br />
<br />
====Selecting a database====<br />
If the user selects a database in the DatabaseComboBox we enable the "Select Database" button. In the OnClick event of this button we set the DatabaseName of MySQLConnection1, and request a list of tables. The last statement of this procedure enables the "Open Query" Button, so the user can enter a query in the "Command" Editbox and have it send to the server.<br />
procedure TFormTryMySQL.SelectDBButtonClick(Sender: TObject);<br />
begin<br />
// A database has been selected so lets get the tables in it.<br />
CloseConnection(Sender);<br />
if DatabaseComboBox.ItemIndex <> -1 then begin<br />
with DatabaseComboBox do<br />
MySQLConnection1.DatabaseName := Items[ItemIndex];<br />
ShowString('Retreiving list of tables');<br />
SQLQuery1.SQL.Text := 'show tables';<br />
SQLQuery1.Open;<br />
while not SQLQuery1.EOF do begin<br />
TableComboBox.Items.Add(SQLQuery1.Fields[0].AsString);<br />
SQLQuery1.Next;<br />
end;<br />
SQLQuery1.Close;<br />
ShowString('List of tables received');<br />
end;<br />
OpenQueryButton.Enabled := True;<br />
end;<br />
MySQL has a special command to get a list of tables, comparable to getting the list of databases, "show tables". The result of this query is handled in the same way as the list of databases and all the tables are added to the TableComboBox.<br />
You might wonder why we do not open the connection again before opening the query? Well, this is done automatically (if necessary) when we activate the SQLQuery.<br />
<br />
====Fields in a table====<br />
In MySQL you can again use a form of "SHOW" to get the fields in a table. In this case "SHOW COLUMNS FROM <tablename>". If the user picks a table from the TableComboBox the OnChangeEvent of this ComboBox is triggered which fills the FieldListbox.<br />
procedure TFormTryMySQL.TableComboBoxChange(Sender: TObject);<br />
begin<br />
FieldListBox.Clear;<br />
SQLQuery1.SQL.Text := 'show columns from ' + TableComboBox.Text;<br />
SQLQuery1.Open;<br />
while not SQLQuery1.EOF do begin<br />
FieldListBox.Items.Add(SQLQuery1.Fields[0].AsString);<br />
SQLQuery1.Next;<br />
end;<br />
SQLQuery1.Close;<br />
end;<br />
As well as the names of the fields, the result set contains information on the type of field, if the field is a key, if nulls are allowed and some more.<br />
<br />
====Showing the data====<br />
Well as we said we would use components to get connected to the database, lets use some components to show the data as well. We will use a second form to show a grid with the data requested by the user. This form will be shown when the user typed a SQL command in the "Command" editbox and afterwards clicks the "Open Query" button. This is the OnClick event:<br />
procedure TFormTryMySQL.OpenQueryButtonClick(Sender: TObject);<br />
begin<br />
ShowQueryForm := TShowQueryForm.Create(self);<br />
ShowQueryForm.Datasource1.DataSet := SQLQuery1;<br />
SQLQuery1.SQL.Text := CommandEdit.Text;<br />
SQLQuery1.Open;<br />
ShowQueryForm.ShowModal;<br />
ShowQueryForm.Free;<br />
SQLQuery1.Close;<br />
end;<br />
The ShowQueryForm looks like this:<br />
<center>[[Image:Mysqlshow.png]]</center><br />
and contains a <br />
{|<br />
|-<br />
| TPanel || Align || alBottom<br />
|-<br />
| TDataSource || || <br />
|-<br />
| TDBGrid || Align || alClient<br />
|-<br />
| || DataSource || DataSource1<br />
|-<br />
| TButton || Caption || Close<br />
|}<br />
The button is placed on the panel.<br />
What happens in the "Open Query" OnClick is this. First we create an instance of TShowQueryForm. Secondly we set the DataSet property of the DataSource to our SQLQuery1. Then we set the SQLQuery SQL command to what the user entered in the "Command" editbox and open it. Then the ShowQueryForm is shown modally, this means that it will have the focus of our application until it is closed. When it is closed, we "free" it and close SQLQuery1 again.<br />
<br />
==Sources==<br />
The sources for this project can be downloaded [http://prdownloads.sourceforge.net/lazarus-ccr/mysql_demo_20050408.tar.gz?download here]<br />
For more demo projects see [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359 sourceforge]<br />
[[Category:Databases]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Databases_in_Lazarus&diff=23776Databases in Lazarus2007-08-20T11:04:48Z<p>Dejvid: Category:Databases</p>
<hr />
<div>== Aim ==<br />
This page aims to provide a general overview of using databases in Lazaraus, and to provide a home from which other database pages can be linked<br />
<br />
==Datasets==<br />
Database use in Lazarus (or FreePascal) is fundamentally based on the TDataset class. This represents a table or query to your application. However, like many other such fundamental classes, you don't use the TDataset class it self, you use a descendant of it. There are many of these. They provide access to different kinds of databases, such as local dbase or text files, or back-end databases such as PostgreSQL, Firebird, MySQl and so forth. Some dataset descendants link directly to database tables, while others use additional components or libraries to perform the link. <br />
<br />
The [[Databases]] page documents such descendants.<br />
<br />
Dataset descendants, being non-visual components are (usually) part of the Free Component Library (FCL) rather than the Lazarus Component Library (LCL).<br />
<br />
== Using Datasets ==<br />
<br />
Datasets can be used both programmatically and with visual controls. A typical Lazarus database application will often use both methods. In either case, the first step is to create the TDataset descendant, initialise it to connect to the table or query you want, and open it. This can be done either in code at run time or by putting a component on your form and setting it's properties at design time. The details of this vary considerably with different TDataset descendants, so see the various guides under [[Databases]] for what has to be done for your database.<br />
<br />
When the dataset is opened, a set of field components are created, one for each field or column of the table or query you opened. Each field component is a descendant of TField, appropriate to the particular data type of the field, eg, TStringField.<br />
<br />
===Using datasets from code===<br />
<br />
Programmatic access will be explained in more detail in [[Using Dataset and Field components]], but as a very simple overview:<br />
* Use the TDataset descendant to open the table or query, filter the rows you want to see, and to move from row to row. <br />
* Use the TField descendants to: <br />
**Access general information about fields<br />
**Access the specific data values for the current row. (use the As... properties, such as AsString, AsInteger, etc.)<br />
* Access the fields of a TDataset descendant by using either:<br />
**The fields property, eg Fields[0] is the first field, <br />
**The FieldByName method, eg FieldByName('AGE') returns the field associated with the database field called 'AGE'<br />
<br />
===Using the visual (data-aware) controls===<br />
<br />
To use databases in a simple, "RAD" style Lazarus application, you usually configure the dataset descendant at design time and the use the data-aware controls. To do this: <br />
*Add the dataset descendant for the database of your choice, together with any supporting components, to your form, and open it (Set the 'Active' property to true)<br />
*Add a TDatasource component (from the "Data Access" tab) to the form, and "link" it to the dataset (set the DataSet property)<br />
*Add data-aware controls from the "Data Controls" tab to the form, and link each one to the DataSource (not dataset) component<br />
*Most controls link to a single field, so you also need to set the Field for each tab.<br />
See [[#Data Controls]] below for more details on the controls<br />
<br />
=== Dataset State ===<br />
Datasets can be in a number of states. While there are quite a few (look up TDataSetState in the source), the main ones to be aware of initally are<br />
<table> <br />
<tr><br />
<th>State</th><br />
<th>Function</th> <br />
</tr><br />
<tr><br />
<td>dsInactive</td><br />
<td>The dataset is closed</td><br />
</tr><br />
<tr><br />
<td>dsBrowse</td><br />
<td>The user can browse through the dataset, looking at values</td> <br />
</tr><br />
<tr><br />
<td>dsEdit</td><br />
<td>The user can edit values on the current row. Values are not saved until a post is performed.</td><br />
</tr><br />
<tr><br />
<td>dsInsert</td><br />
<td>A new row has been added, and the user can set the values. The record is not saved until a post is performed</td><br />
</tr><br />
</table><br />
The other states are fairly transitory, and are usually handled "automatically". They are used internally and in more complicated code. If your database only views data, and you open the dataset at design time, you can largely ignore the state, as it will mostly be dsBrowse. However, most applications will want to change the data at some stage. If you are using data-aware controls, they will handle a lot of this automatically. If you change the text in a DBEdit control, for example, it will put the dataset into dsEdit state - unless you are already in dsEdit or dsInsert. If you "scroll" to a different record while the current state is dsEdit or dsInsert, that record will be "posted" and the dataset revert to dsBrowse. However, if you are accessing the dataset from code, you will often have to change the state in code as well. The dbNavigator control (see below) allows the user to change the state explicitly.<br />
<br />
===Post and Cancel===<br />
If you have edited or inserted a record, the new values are held in a buffer.<br />
*Calling the dataset cancel method removes the new record (insert) or reverts the values to their previous values (edit). <br />
*Calling the database post method saves the values (edit) or record (insert). In some dataset descendants, they will be written to the database immediately, while in others they will be stored in a list of updates until a further call is made to save all changes to the database. Finally, even when they are written to the database, you may still have to call a "commit" method to make the database write them permanently. All of this also varies considerably with the dataset descendant, so look up the details for the one you are using.<br />
<br />
==Data Controls==<br />
<br />
To use any of these controls, add the control to a form and set at least the datasource property. Other key properties will be noted.<br />
<br />
===Single Field Controls===<br />
<br />
These controls all attach to a single field. As well as datasource, set the field name. Controls include:<br />
<br />
*[[TDBText|DB Text control]] Displays a text field (readonly, no border)<br />
*[[TDBEDit|DB Edit control]] Displays / edits a text field as an edit box <br />
*[[TDBMemo|DB Memo control]] Displays / edits a text field in a multi-line edit box <br />
*[[TDBImage|DB Image control]] Displays a picture stored in a database as a BLOB<br />
*[[TDBListBox|DB List Box Control]] and [[TDBComboBox|DB Combo Box Control]] Allow the user to insert values into a database field from the list of values in the Items property of the controls<br />
*[[TDBCheckBox|DB Check Box]] Displays / edits a boolean field by checking/clearing a check box<br />
*[[TDBRadioGroup|DB Radio Group control]] Displays the items as in a normal radio group, reading/setting the field value from a matching values list<br />
*[[TDBCalendar|DB Calendar control]] Displays / edits a date field using a calendar panel<br />
*[[TDBGroupBox|DB Group Box control]]<br />
<br />
===[[TDBGrid|DB Grid control]]===<br />
<br />
This control can show a number of fields in a row/column layout - in fact by default it shows them all. However, you can put entries into the [[TColumns|columns collection]] to restrict it to specific fields and to set the widths and titles of each column.<br />
<br />
There seem to be some issues currently with editing in dbGrid, so while editing is certainly possible, it may be safer to use it as display only (set readonly to true) and provide single field controls for editing. <br />
<br />
===[[TDBNavigator|Navigator Control]]===<br />
<br />
This control gives the user some direct control over the dataset. It allows the user to:<br />
* Move to the next or previous record, or to the start or end of the records<br />
* Add a new record (equivalent to a dataset.insert method call)<br />
* Put the dataset into edit mode<br />
* Delete a record<br />
* Post or Cancel current changes<br />
* Refresh the data (useful in multiuser database applications)<br />
<br />
Key Properties:<br />
* VisibleButtons: Lets you control what the user can do. For example, if deletes are not allowed, hide the delete button. If you have a DBGrid attached to the same dataset, you may decide you do not need the next and prior buttons. <br />
* Width: If you do not show all buttons, you may want to set the with to height * number_of_visible_buttons<br />
[[Category:Databases]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Databases&diff=23775Databases2007-08-20T11:03:51Z<p>Dejvid: Category:Databases</p>
<hr />
<div>{{Databases}}<br />
<br />
Work in progress..<br />
<br />
== Introduction ==<br />
<br />
This page will be an entrance to the topic 'Lazarus and databases'. The following table provides an overview about the supported databases. It needs to be updated soon, because it is based on the time when FPC 1.9.7 was released. One hint: You should only install the database components for which you have the client libraries installed (if the database need client libaries). Otherwise Lazarus could fail to start because of missing files. Then you would have to reinstall Lazarus, because uninstalling the component isn't possible.<br />
<br />
=== Table 1 ===<br />
{| BORDER="1" CELLSPACING="0"<br />
!STYLE="background:#ffdead;"|'''Database'''<br />
!STYLE="background:#ffdead;"|'''Package name'''<br />
!STYLE="background:#ffdead;"|'''Need client lib?'''<br />
!STYLE="background:#ffdead;"|'''Need server?'''<br />
!STYLE="background:#ffdead;"|'''Supported versions'''<br />
!STYLE="background:#ffdead;"|'''Supported platforms'''<br />
|----<br />
|'''Textfiles'''||sdf||No||No|| - || All<br />
|----<br />
|'''In memory'''||memds||No||No|| - || All<br />
|----<br />
|'''DBase'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| III+, IV, VII || All<br />
|----<br />
|'''FoxPro'''||[[Lazarus_Tdbf_Tutorial | DBFLaz]]||No||No|| - || All<br />
|----<br />
|'''Paradox'''||[[TParadoxDataSet | TParadoxDataSet]]||No||No|| up to Table Level 7 (and up ??) || All<br />
|----<br />
|[http://www.sqlite.org/ '''SQLite''']||SQLite||Yes||No|| - || -<br />
|----<br />
|[http://www.mysql.com/ '''MySQL''']||SQLdb||Yes||Yes|| 3 - 4.0 || i386: Linux, Win32<br />
|----<br />
|[http://www.firebirdsql.org/ '''Firebird''']||IBase||Yes||Yes||1 - 1.5|| i386: Linux, Win32<br />
|----<br />
|[http://www.postgresql.org/ '''PostgreSQL''']||SQLdb||Yes||Yes|| 6.6 - 8 || i386: Linux, Win32<br />
|----<br />
|'''ODBC'''||[[ODBCConn|SQLdb]]||Yes||Depends|| 3.x <sup>1)</sup> || i386: Linux, Win32<br />
|----<br />
|[http://www.borland.com/us/products/interbase/index.html '''Interbase''']||IBase||Yes||Yes||4 - 6|| i386: Linux, Win32<br />
|----<br />
|[http://www.oracle.com/ '''Oracle''']||SQLdb||Yes||Yes|| - || - <br />
|}<br />
<br />
<sup>1)</sup> This version number refers to the ODBC standard, not to the version number of a driver or driver manager. There are ODBC 3.x drivers for most DBMSs.<br />
<br />
== The bindings to the database-clients ==<br />
<br />
If you want to use one of the databases-client libraries, those libraries has to be installed. Not only on the computer you're programming on, but also on the computers where the application must run. Note that some databases (in particular MySQL) only work if the bindings which are compiled in the application are from the same version as those of the installed libraries. You can find out how to install those libraries (.so files on *nix systems, and .dll's on windows) on the website of the database-developers. The binding units can be found in the packages/base directory in the fpc-sources. They basically consist of the client-api calls like mysql_connect_database, which are completely different for each database. It is possible to write database applications using these units, but it is usually far more work and bug-sensitive than using the DB-unit Lazarus components.<br />
<br />
Most of these bindings-packages are hard-linked to the client-libraries. This means that if the application is compiled with one of these units in it, the whole application can not be linked if the client libraries are not available on the workstation. This means that if you do not have installed - for example - MySQL on your computer, and you are using the mysql4.pp unit in your program. The program will not link. If you succeed to compile the program on a computer which has MySQL installed, it still won't even start on any other workstation without the appropriate MySQL-libraries.<br />
<br />
To avoid such problems some of the packages are also able to link dynamically to the libraries. Before any calls to those libraries can be made, the unit has to be 'initialized'. This initialization fails if the database-client isn't installed on the computer. If the program is ready using the client-library, the unit has to be 'released'.<br />
<br />
== Additional information about Lazarus and databases ==<br />
<br />
* FAQ: [[Lazarus DB Faq|Lazarus database FAQ]]<br />
* Tutorial: [[Lazarus Database Tutorial]]<br />
* MySQL: [[MySQLDatabases]]<br />
* Firebird: [[Firebird in action]]<br />
* ODBC: [[ODBCConn]]<br />
<br />
== Database packages contained in Lazarus ==<br />
<br />
=== sqldblaz.lpk ===<br />
This package provides access to different databases (e.g. MySQL or Interbase/Firebird). The components (TSQLQuery, TSQLTransaction, TIBConnection, TODBCConnection, TOracleConnection, TMySQL40Connection, TMySQL41Connection, TMySQL50Connection, TPQConnection) are on the 'SQLdb' tab in the component palette.<br />
*[[SQLdb Package]]<br />
<br />
=== dbflaz.lpk ===<br />
This package provides access to dBase and FoxPro databases. You can get more information in the [[Lazarus Tdbf Tutorial]]. The TDbf component is on the 'Data Access' tab in the component palette.<br />
<br />
=== interbaselaz.lpk ===<br />
This package provides access to Interbase and Firebird databases. The components are similar to Delphi but far from Delphi complexity (only e components included: TIBDatabase, TIBTransaction, TIBQuery). Furthermore these components allow only a read only access. The package must be installed manually. It depends on an installed gds32.dll file (under Windows). If you install the package without this file, Lazarus fails to start. The components are on the 'Interbase' tab in the component palette.<br />
<br />
=== sqlitelaz.lpk ===<br />
This package provides access to SQLite databases. You can get more information in the [[Lazarus Database Tutorial]].<br />
<br />
=== sdflaz.lpk ===<br />
The component TSdfDataSet can be found on the 'Data Access' tab in the component palette.<br />
<br />
== External packages / libraries ==<br />
<br />
=== Zeos library ===<br />
This library provides access to different databases. Information about the interaction between Zeos and Lazarus can be found in the [[Zeos tutorial]].<br />
<br />
=== Pascal Data Objects ===<br />
There is now an alternative. The functions introduced with MySQL 4.1 and 5.0 like prepared statements, binding, and stored procedures are supported by database API called Pascal Data Objects, which is inspired by PHP Data Objects. All the code and documentation necessary to use this new API is available on Sourceforge:<br />
<br />
http://pdo.sourceforge.net<br />
<br />
Jan 30, 2007: PDO has added drivers for Firebird 1.5 and Firebird 2.0<br />
<br />
=== TPSQL ===<br />
These components provide access via TCP/IP to PostgreSQL databases. You can find more information on [[TPSQL|this page]].<br />
<br />
=== FIBL ===<br />
These components provide access to Interbase and Firebird databases. The homepage is [http://sourceforge.net/projects/fibl http://sourceforge.net/projects/fibl].<br />
<br />
=== FBLib Firebird Library ===<br />
<br />
[http://fblib.altervista.org/ FBLib] is an open Source Library No Data Aware for direct access to Firebird Relational Database from Borland Delphi/Kylix, Free Pascal and Lazarus.<br />
<br />
Current Features include:<br />
<br />
* Direct Access to Firebird 1.0.x, 1.5.x and 2.x Classic or SuperServer<br />
* Multiplatform [Win32,Gnu/Linux,FreeBSD)<br />
* Automatic select client library 'fbclient' or 'gds32'<br />
* Query with params<br />
* Support SQL Dialect 1/3<br />
* LGPL License agreement<br />
* Extract Metadata<br />
* Simple Script Parser<br />
* Only 100-150 KB added into final EXE<br />
* Support BLOB Fields<br />
* Export Data to HTML SQL Script<br />
* Service manager (backup,restore,gfix...)<br />
* Events Alerter<br />
<br />
You can download documentation on [http://fblib.altervista.org/ FBLib's website].<br />
<br />
=== Unified Interbase ===<br />
<br />
UIB provides access to Interbase, Firebird and YAFFIL databases. The homepage is [http://www.progdigy.com www.progdigy.com]. A svn repository is available under https://uib.svn.sourceforge.net/svnroot/uib .<br />
<br />
=== TechInsite Object Persistence Framework (tiOPF) ===<br />
More informationen about tiOPF can be found on this [[tiOPF|page]].<br />
<br />
=== LazReport ===<br />
The homepage of the report generator is [http://lazreport.sourceforge.net/ http://lazreport.sourceforge.net/].<br />
More informationen (et al. an additional link) can be found [[Projects_using_Lazarus#LazReport| here]].<br />
LazReport depends on the Printer4Lazarus package.<br />
[[Category:Databases]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=MySQL&diff=23774MySQL2007-08-20T11:00:53Z<p>Dejvid: ]</p>
<hr />
<div>#REDIRECT [[MySQLDatabases]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=MySQL&diff=23773MySQL2007-08-20T11:00:32Z<p>Dejvid: #REDIRECT [MySQLDatabases]</p>
<hr />
<div>#REDIRECT [MySQLDatabases]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Lazarus_Packages&diff=19549Lazarus Packages2007-06-10T21:22:15Z<p>Dejvid: /* What is a lazarus package? */ n contrast</p>
<hr />
<div>{{Lazarus Packages}}<br />
<br />
= Overview of the Lazarus Package System =<br />
<br />
== What is a lazarus package? ==<br />
<br />
A lazarus package is a collection of units and components, containing information how they can be compiled and how they can be used by projects or other packages or the IDE. In contrast to Delphi, packages are not limited to libraries and they can be OS independent. ([[packages|Delphi Packages]] are specially compiled libraries used by applications, the IDE or both. Delphi packages require in-compiler support, which FPC is not capable of at the moment and of course this magic is OS dependent.)<br />
<br />
Currently the [[Free Pascal]] compiler only supports static packages. Therefore you must compile and restart the IDE, each time a package is installed or uninstalled.<br />
<br />
A lazarus package is identified/distinguished by the name and its version.<br />
<br />
== FAQ ==<br />
<br />
Q: Do I need to install a package?<br><br />
A: You only need to install a package, if it contains designtime items, like components for the IDE component palette. If you don't use those items, you don't need to install the package. If you only want to use a package in your project, don't install it.<br />
<br />
Q: I installed a package, but the IDE does not find the units<br><br />
A: Installing a package means, the package is integrated into the IDE, not your project. These are separate things. To use a package in your project, use Project -> Project Inspector -> Add -> New Requirement. And uninstall the package, if it does not contain any IDE goodies.<br />
<br />
== Quick Start ==<br />
<br />
To see the packagesystem in action and to get used to it, do the following:<br />
<br />
Creating a new package:<br />
* File->New... -> Package -> Standard Package<br />
* A package editor opens<br />
* Use the Save button at top left.<br />
* Depending on your 'naming' setting in the 'environment options', the IDE will ask you to save the file lowercase. Say yes.<br />
Congratulations: You have just created your first package!<br />
<br />
Adding a new component:<br />
* Use the Add button -> New component<br />
* Choose a component in the ancestor type combobox. For instance: TBevel.<br />
* Click Ok<br />
* The file will be added to the package and opened in the editor<br />
* Install the package by clicking the 'install' button in the top of the package editor.<br />
* Lazarus will save the package and ask you, if the IDE should be rebuilt. Say yes.<br />
* The packages are statically linked, so a restart of the IDE is needed.<br />
* Restart Lazarus and see your new component in the component palette (For example: A TBevel1 will be on the 'Additional' page).<br />
* If you do not see your new component in the component palette, it is most likely that you are not running the re-compiled version of Lazarus. You can set where Lazarus builds to in: Environment -> Environment options -> Files -> Lazarus directory. Instead of calling lazarus directly, you also can use startlazarus, which starts the newly created lazarus, for example the lazarus executable in the ~/.lazarus directory, if you don't have write access to the directory lazarus was installed into.<br />
Congratulations: You have just installed your first package with your first package component.<br />
<br />
== The IDE menu items for packages: ==<br />
<br />
* File->New... -> Package -> Standard Package<br />
Creates a new package.<br />
* Project -> Project Inspector<br />
Here you can see, what packages are required by the currently open project.<br />
You can add new dependencies and remove unneeded ones.<br />
* Run -> Compiler options -> Inherited<br />
Here you can see what compiler options are inherited from which package.<br />
<br />
Components -> <br />
- 'Open package'<br />
A dialog shows all open packages with their state.<br />
<br />
- 'Open package file'<br />
Open a .lpk file<br />
<br />
- 'Open package of current unit'<br />
Open the .lpk file, that belongs to the file in the source editor<br />
<br />
- 'Open recent package'<br />
Open a recently open package file (lpk file)<br />
<br />
- 'Add active unit to a package'<br />
Adds the unit in the source editor to a package<br />
<br />
- 'Package Graph'<br />
The package graph shows all open packages and their dependencies.<br />
<br />
- 'Configure installed packages'<br />
Edit the list of packages installed in the IDE. Install or uninstall several packages at once.<br />
<br />
Project -> Project Inspector<br />
Here you can see and edit all packages used by the project.<br />
<br />
== The theory ==<br />
<br />
Each Lazarus package has a .lpk file. A package is identified by its name and its version. The name must correspond to the lpk filename. For example:<br />
<br />
Name: Package1, Version: 1.0, Filename: /home/.../package1.lpk.<br />
<br />
* The IDE automatically creates the main source file (package1.pas). See below. The lpk file contains information about the required packages, the files it uses, how to compile them, and what is needed to use the package by other packages/projects. The directory where the lpk file is, is called the "package directory".<br />
* The IDE maintains a list of all package files (<config directory>/packagelinks.xml). Everytime a package is opened in the IDE it will be added to this list. When a package is opened, the IDE automatically opens all required packages via this list.<br />
* There are three base packages: FCL, LCL and SynEdit. These are parts of the IDE and so they are autocreated, readonly and have no lpk file.<br />
* Normally a package has a source directory with some pascal units. And normally the lpk file will be there too. A package has also an output directory. Default is the subdirectory 'lib/$(TargetCPU)-$(TargetOS)/' in the package directory.<br />
* Before a package is compiled the IDE checks all required packages and if they need update and have the auto update flag, they are compiled first. Then the IDE creates the package main source file. If the lpk file was package1.lpk, then the main source file is package1.pas. This file contains all units in the uses section plus a 'Register' procedure, which is called in the intialization section. <br />
<br />
For example:<br />
<br />
This file was automatically created by Lazarus. Do not edit!<br />
This source is only used to compile and install<br />
the package GTKOpenGL 1.0.<br><br />
unit GTKOpenGL;<br />
interface<br />
uses GTKGLArea, GTKGLArea_Int, NVGL, NVGLX, LazarusPackageIntf;<br />
implementation<br />
procedure Register;<br />
begin<br />
RegisterUnit('GTKGLArea', @GTKGLArea.Register);<br />
end;<br />
initialization<br />
RegisterPackage('GTKOpenGL', @Register)<br />
end.<br />
<br />
* Then the compiler is called and the package is compiled to the output directory.<br />
* After successful compilation the state file is created. The state file is put into the output directory. It has the name <packagename>.compiled and contains the information, how the package was compiled. This state file is used by the IDE to check if update is needed.<br />
<br />
For example: gtkopengl.compiled:<br />
<br />
<?xml version="1.0"?><br />
<CONFIG><br />
<Compiler Value="/usr/bin/ppc386" Date="781388725"/><br />
<Params Value=" -Rintel -S2cgi -CD -Ch8000000 -OG1p1<br />
-Tlinux -gl -vewnhi -l -Fu../../../lcl/units<br />
-Fu../../../lcl/units/gtk -Fu../../../packager/units<br />
-Fu. -FElib/ gtkopengl.pas"/><br />
</CONFIG><br />
<br />
* The IDE opens all needed packages automatically. This means, it opens all installed packages, all packages marked for installation (auto install), all packages with an open Editor, all packages required by the project and all packages required by one of the other packages. Unneeded packages are automatically unloaded, when the IDE becomes idle.<br />
* The IDE never opens two packages with the same name at the same time. When the user opens another package file with the same name as an already opened package the IDE will ask to replace the old one.<br />
* The IDE maintains two extra sets of packages: The 'installed' packages and the 'auto install' packages. The auto install packages will be linked into the IDE on next compile. It creates two new files in the config directory: staticpackages.inc and idemake.cfg. Then it calls 'make ide OPT=@/path/to/your/config/idemake.cfg' to compile itself.<br />
<br />
== Hints and Tips ==<br />
Please add any hints, tips or gotchas here.<br />
<br />
*To rename a package, use 'save as'.<br />
<br />
Example<br />
<br />
I will use the tiOPF framework as an example The tiOPF has the<br />
following directory layout, due to the fact that it compiles for FPC,<br />
Delphi 5-7, D2005 and D2006.<br />
<br />
Source <= full path \Programming\3rdParty\tiOPF\Source<br />
\Compilers<br />
\Delphi7 <= Delphi 7 package files live here<br />
\D2005<br />
\FPC <= the tiOPF.lpk lived here<br />
\Core <= core unit files<br />
\Options <= optional unit file for extra features<br />
\GUI<br />
<br />
Using this example, I included in the "Options - Usage - Units"<br />
editbox the following paths:<br />
"$(PkgOutDir);..\..\Core;..\..\Options;..\..\GUI" which will be added<br />
to whatever project uses this package.<br />
<br />
== Contributors and Comments ==<br />
<br />
This page has been converted from the epikwiki [http://lazarus-ccr.sourceforge.net/index.php?wiki=LazarusPackages version].</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=19037Grids Reference Page2007-05-17T14:57:29Z<p>Dejvid: /* known problems */ dbgrid paints the cell background twice (once w</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate the user in showing specific kinds of information.<br />
<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this page is to help people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to delphi grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid paints the cell background twice (once with fillrect and once in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=19036Grids Reference Page2007-05-17T14:52:20Z<p>Dejvid: /* Differences between Lazarus and Delphi grids */ to delphi grids.</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate the user in showing specific kinds of information.<br />
<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this page is to help people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to delphi grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid is painting twice the cell background (one with fillrect and one in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=19035Grids Reference Page2007-05-17T14:51:12Z<p>Dejvid: /* A Starting Example */ this page is to help people with little or no</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate the user in showing specific kinds of information.<br />
<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this page is to help people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to the delphi's grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid is painting twice the cell background (one with fillrect and one in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=19034Grids Reference Page2007-05-17T14:50:13Z<p>Dejvid: /* Overview */ to facilitate the user in showing specific kinds</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate the user in showing specific kinds of information.<br />
<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this is to be nice with people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to the delphi's grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid is painting twice the cell background (one with fillrect and one in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=19033Grids Reference Page2007-05-17T14:48:56Z<p>Dejvid: /* Overview */ this information consists of</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample and mainly depends on what the user wants to show. Generally this information consists of text, colors, images or a combination of those three. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate to the user showing a specific kind of information.<br />
<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this is to be nice with people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to the delphi's grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid is painting twice the cell background (one with fillrect and one in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Slash&diff=18874Slash2007-05-11T09:57:59Z<p>Dejvid: Category:Symbols</p>
<hr />
<div>The '''''/''''' or '''slash''' is used in a Pascal program to perform division. The results are a real value unless the values on both sides of the slash are integer, in which case the value is integer and the result is the same as the [[div]] operator.<br />
<br />
A := 3 / 4;<br />
<br />
returns 0.75 if a is real, and zero if a is integer.<br />
<br />
The value on the right side of the slash must not be zero, or a division by zero error occurs. This can be caught through use of a [[try]] .. [[finally]] or [[try]] .. [[except]] block.<br />
<br />
{{Symbols}}<br />
<br />
{{Stub}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=;&diff=18873;2007-05-11T09:57:15Z<p>Dejvid: </p>
<hr />
<div>The '''semicolon''', ''''';''''' is used to indicate the end of a statement. It is mandatory for most statements, voluntary in some statements, and forbidden in one particular statement.<br />
<br />
(In order to explain usage, where Pascal keywords are referred to in the text of this article, they are shown in UPPER case. Program listings will use lower case.)<br />
<br />
The semicolon is generally mandatory, and is required after every statement. The places where it is optional are<br />
* before an END statement<br />
* before the period at the end of a PROGRAM or UNIT<br />
<br />
The places where a semicolon is forbidden are<br />
* Before a BEGIN statement which begins a compound statement <br />
* Before an immediately following ELSE clause of a nested IF statement<br />
<br />
It is generally forbidden before the ELSE clause in an [[If|IF]] statement or a [[case|CASE]] statement. However, in the case of a nested IF statement it may be required before the ELSE, if there is an ELSE applying to another IF statement earlier in the program text and the particular IF is not ended by an ELSE statement. This will be explained below.<br />
<br />
A semicolon is forbidden before the [[Else|ELSE]] clause which applies to the immediately last IF statement in order that the compiler can know where that IF statement ends. For example<br />
<br />
(*0001*) if continuance then <br />
(*0002*) if separated then <br />
(*0003*) Condition(a) <br />
(*0004*) else <br />
(*0005*) if combined then <br />
(*0006*) Condition(b)<br />
(*0007*) else <br />
(*0008*) Condition(c);<br />
(*0009*) Calculate_fee;<br />
<br />
For this example, the semi colon is forbidden on line 3 (otherwise the ELSE on line 4 would apply to the IF on line 1).<br />
<br />
If there was going to be a BEGIN between lines 1 and 2, and an END after line 8, then a semicolon would be forbidden on line 1.<br />
<br />
Because line 6 has no semi colon, the ELSE on line 7 applies to the IF on line 5. If line 6 ended with a semi colon, then the ELSE on line 7 would apply to the IF on line 1 (the ELSE on line 4 applies to the IF on line 2). Thus the semicolon is forbidden in this context in order to get that result.<br />
<br />
However, if you ''wanted'' the ELSE to apply to line 1, the semicolon would be ''mandatory'' on line 6, in order to "close" the IF on line 5.<br />
<br />
If there was an IF statement above line 1, the semi colon on line 8 closes the IF statement on line 1, and if an ELSE were to be placed after line 8, it would not apply to the IF statement on line 1.<br />
<br />
It may be preferable to enclose all statements inside an IF statement with a BEGIN / END block if there might be a question as to which ELSE applies to which IF, in a set of nested IF statements.<br />
<br />
{{Symbols}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Not_equal&diff=18872Not equal2007-05-11T09:56:47Z<p>Dejvid: </p>
<hr />
<div>The not equal symbol, '''''<>''''' (consisting of [[Less than|'''''<''''' (the less than symbol)]] followed by [[Greater than|'''''>''''' (the greater than symbol)]] is used to compare whether the value on the left side of the symbol, is not equal to the value to the right side of the symbol, and returns the boolean value of '''true''' or '''false''' as a result.<br />
<br />
{{Symbols}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Less_than&diff=18871Less than2007-05-11T09:56:28Z<p>Dejvid: </p>
<hr />
<div>The less than symbol, '''''<''''' is used to compare whether the value on the left side of the symbol is smaller than that of the value to the right side of the symbol, and returns the boolean value of true or false as a result.<br />
<br />
{{Symbols}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Greater_than&diff=18870Greater than2007-05-11T09:56:10Z<p>Dejvid: </p>
<hr />
<div>The greater than symbol, '''''>''''' is used to compare whether the value on the left side of the symbol, exceeds that of the value to the right side of the symbol, and returns the boolean value of true or false as a result.<br />
<br />
{{Symbols}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Equal&diff=18869Equal2007-05-11T09:55:54Z<p>Dejvid: Category:Symbols</p>
<hr />
<div>The symbol = (pronounced "equal") is used to test two values for equality. Unlike other languages, the symbol is not used to assign a value, for that, you use the symbol [[Becomes|:=]] ([[colon immediately followed by equal, which is pronounced [[Becomes|becomes]]).<br />
<br />
{{Symbols}}<br />
{{Stub}}<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Colon&diff=18868Colon2007-05-11T09:55:16Z<p>Dejvid: Category:Symbols</p>
<hr />
<div>The symbol : (pronounced "colon") is used in pascal in several ways.<br />
<br />
It is used to indicate a label.<br />
<br />
It is used to indicate a case statement entry.<br />
<br />
It is used to indicate the type of an identifier in a [[Var|var]] or [[Const|const]] declaration.<br />
<br />
<br />
{{Symbols}}<br />
{{Stub}}<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=*&diff=18867*2007-05-11T09:54:34Z<p>Dejvid: Category:Symbols</p>
<hr />
<div>The symbol '''''*''''' (pronounced "asterisk") is used in Pascal to indicate multiplication of numbers, or is used to indicate membership (for sets)<br />
<br />
{{stub}}<br />
{{Symbols}}<br />
<br />
[[Category:Symbols]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Robot_-_The_Game&diff=18866Robot - The Game2007-05-11T09:29:07Z<p>Dejvid: category:Example programs</p>
<hr />
<div>===The Game===<br />
The idea came from an old DOS-game from TOM-productions (homepage with download (it's a nice game!)): http://www.tom-games.de/eng/download.htm). You control your body through a world consisting of 5*5 rooms consisting of 20*20 fields. Every field contains a special game-object; this can be a wall, a bad robot who wants to kill you (because of the robots, the game is named Robot), a door (for each door, you need a special key) and so on.<br />
<br />
===Screenshots===<br />
Inside of Robot 1.5 while playing:<br />
<br />
[[Image:robot1.5-shot1.png]]<br />
<br />
Startscreen of Robot 1.7:<br />
<br />
[[Image:robot1.7-shot1.png]]<br />
<br />
Editor-mode of Robot 1.7:<br />
<br />
[[Image:robot1.7-shot2.png]]<br />
<br />
===Code===<br />
The code was written to demonstrate the possibilities with a very small set of programming techniques. The code doesn't use OOP, it is completly procedural (but embedded into one little form). I started this project while I was showing Lazarus to a friend to show him how easy it is to program a little application. It is not very difficult to teach a little bit of procedural and imperative programming so that you can do a little bit stuff. Then I said, "I show you what you can do with this"...<br />
<br />
The code is well documented in english.<br />
<br />
===Control===<br />
Movement is controlled by the arrow-keys. You can pick up things lying around by simply going to them. They will be put into your knapsack. Select the thing in your knapsack you want to use with spacebar. You can use them by pressing enter. There are corrosive liquids filled in bottles lying around. You can remove walls with them (only the bright walls). To go thru a door you must have a key of the right color. You won't need to select the key - just having the right key in your knapsack is sufficient. There are 3 diamonds lying around somewhere. You need them to defeat the evil king. You have to put them near of the diamond-places (go there and select it in your knapsack). You can save the game with the clocks lying around. ...<br />
<br />
===Control of Editor===<br />
Go into the editor mode (menu). All game-objects are listed in your knapsack. Simply select an object and place it in the present room by left-clicking at the wanted position. You can also select the objects in the knapsack by clicking on them. With a right-click, you can remove an object. With Ctrl+Arrowkey, you can switch to other rooms. Don't forget to come back to the room where your body is, because else, you can't resume playing on exiting the editor mode.<br />
<br />
===Versions===<br />
The game itself is in german, that means, that all messages inside of the game (a little description and a few state messages) are german. Perhaps you can translate it? :)<br />
<br />
It should not disturb so much (I give you a description...) and the interessing part is also the source code.<br />
<br />
There are versions from 1.5 to 1.7 available at the moment. The code is extended and buxfixed a little bit in the newer version, but there is no real big difference. Version 1.7 contains an ingame-editor now and there is a new world since version 1.7 (feel free to design own worlds or perhaps a whole new game based on this engine; it should not be difficult).<br />
<br />
===Download===<br />
You can download the source and binaries for Windows, Linux (i386 and powerpc) of Robot 1.7 from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=440138 Lazarus-CCR Sourceforge site].<br />
<br />
The homepage of the project is http://www.az2000.de/projects/robot2/?lang=en, where you can find additional informations.<br />
<br />
If you have questions, simply ask me.<br />
<br />
===License===<br />
It is under the LGPL.<br />
<br />
===Author===<br />
[[User:Albert|Albert Zeyer]]<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Kaldemonia&diff=18865Kaldemonia2007-05-11T09:28:08Z<p>Dejvid: category:Example programs</p>
<hr />
<div>== Kaldemonia ==<br />
'''<br />
<br />
=== About ===<br />
<br />
Kaldemonia is a program where user can enter a search string that is used to search Google and return the results to three files: A Cache file, A Results file, and one large Htm file containing the full result from Google. This Program was done as practice for me in coding.<br />
<br />
Kaldemonia compiles on Linux and can be Cross-Compiled to Win32 system from Linux with minor modification.<br />
Kaldemonia shows using a worker thread and Gui thread so GUI remains responsive while still functioning.<br />
<br />
Kaldemonia2 used a new component instead of TidyPas due to TidyPas not compiling with fpc 2.0.4.<br />
The new component works very well to parse html document to extract the links needed.<br />
<br />
Kaldemonia3 goes a step further by having 10 threads fetching the Google pages simultanously.<br />
The Threaded part is seperated into it's own unit besides the main unit. Again everything works on both Linux and Win32.<br />
<br />
I used several examples to make it and asked many questions. So I hope you like it.<br />
<br />
<br />
=== Requirements for Kaldemonia===<br />
<br />
1. TidyPas can be downloaded here: [http://curlpas.sourceforge.net/ TidyPas]<br />
N.B. TidyPas requires TidyLib to be installed and on win32 LibTidy.dll.<br />
<br />
2. Synapse can be found here: [http://www.ararat.cz/synapse/ Ararat Synapse]<br />
I used Synapse version 37b6. I don't see that version anymore so I include it.<br />
<br />
=== Requirements for Kaldemonia2===<br />
<br />
1. Synapse can be found here: [http://www.ararat.cz/synapse/ Ararat Synapse]<br />
Latest release should be fine.<br />
<br />
2. THTMLPars can be downloaded here: [http://www.spreendigital.de/DevTools.3.0.html HTMLPars]<br />
I included it, with very minor modification to work on Linux.<br />
<br />
=== Requirements for Kaldemonia3===<br />
<br />
1. Synapse can be found here: [http://www.ararat.cz/synapse/ Ararat Synapse]<br />
Latest release should be fine.<br />
<br />
2. THTMLPars can be downloaded here: [http://www.spreendigital.de/DevTools.3.0.html HTMLPars]<br />
I included it, with very minor modification to work on Linux.<br />
<br />
=== Notes on Kaldemonia ===<br />
* Kaldemonia was compiled with FPC 2.0.2 and Lazarus 0.9.16<br />
* TidyPas release date 2005-12-25<br />
* Synapse version 37b6<br />
<br />
=== Notes on Kaldemonia2 ===<br />
* Kaldemonia2 was compiled with FPC 2.0.4 and Lazarus 0.9.16 & 0.9.18<br />
* Synapse release number 37<br />
* THTMLParser v1.17 <br />
<br />
=== Notes on Kaldemonia3 ===<br />
* Kaldemonia3 was compiled with FPC 2.0.4 and Lazarus 0.9.21 beta<br />
* Synapse release number 38b4<br />
* THTMLParser v1.17<br />
<br />
=== Additional Notes ===<br />
Kaldemonia compiles on both Linux and Windows without errors. <br />
However, it runs well on Linux, but not on Windows. The reason is<br />
not clear, but debugging it shows Synchronize(@ShowStatus) to be <br />
where the problem occurs.<br />
<br />
Added October, 9th 2006:<br />
Although Lazarus executable for Win32 freezes, same source <br />
(with very minor modification) compiles with delphi and runs <br />
fine on Win32 environment.<br />
<br />
<br />
=== Author ===<br />
<br />
[[User:Blue1|Blue1]]<br />
<br />
=== License ===<br />
<br />
GPL.<br />
<br />
=== Download ===<br />
<br />
You can download Kaldemonia source from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=448040 Lazarus CCR].<br />
<br />
You can download Kaldemonia2 source from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=454415 Lazarus CCR].<br />
<br />
You can download Kaldemonia3 source from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=484669 Lazarus CCR].<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=OpenGL_Tutorial&diff=18864OpenGL Tutorial2007-05-11T09:27:39Z<p>Dejvid: category:Example programs</p>
<hr />
<div>{{OpenGL Tutorial}}<br />
<br />
OpenGL is the premier environment for developing portable, interactive 2D and 3D graphics applications. Since its introduction in 1992, OpenGL has become the industry's most widely used and supported 2D and 3D graphics application programming interface (API), bringing thousands of applications to a wide variety of computer platforms. OpenGL fosters innovation and speeds application development by incorporating a broad set of rendering, texture mapping, special effects, and other powerful visualization functions. Developers can leverage the power of OpenGL across all popular desktop and workstation platforms, ensuring wide application deployment.<br />
<br />
You can find more information about OpenGL [http://www.opengl.org/about/overview/ here].<br />
<br />
GLUT (pronounced like the glut in gluttony) is the OpenGL Utility Toolkit, a window system independent toolkit for writing OpenGL programs. It implements a simple windowing application programming interface (API) for OpenGL. GLUT makes it considerably easier to learn about and explore OpenGL programming. GLUT provides a portable API so you can write a single OpenGL program that works across all PC and workstation OS platforms.<br />
<br />
You can find more information about GLUT [http://www.opengl.org/resources/libraries/glut/ here].<br />
<br />
Many OS comes with preinstalled GLUT, but if yours don’t have one you can easily find it using [http://www.google.com/ Google].<br />
<br />
Windows binaries can be downloaded from [http://www.xmission.com/~nate/glut.html www.xmission.com].<br />
<br />
= Creating your first GLUT program =<br />
<br />
In order to use GLUT, you must first initialize it. This is done using <b>glutInit</b> function. This function can parse the command line and set parameters for the main window, but it expects input in C/C++ style. You'll have to write your own function to make the conversion from ParamCount and ParamStr to C/C++ like command line parameters.<br />
<br />
procedure glutInitPascal(ParseCmdLine: Boolean); <br />
var<br />
Cmd: array of PChar;<br />
CmdCount, I: Integer;<br />
begin<br />
if ParseCmdLine then<br />
CmdCount := ParamCount + 1<br />
else<br />
CmdCount := 1;<br />
SetLength(Cmd, CmdCount);<br />
for I := 0 to CmdCount - 1 do<br />
Cmd[I] := PChar(ParamStr(I));<br />
glutInit(@CmdCount, @Cmd);<br />
end;<br />
<br />
In essence, you create an array and fill it with strings from ParamStr. This function also takes a parameter that can control what is passed to glutInit -- either the whole command line or just the executable file name.<br />
<br />
More about glutInit: http://www.opengl.org/resources/libraries/glut/spec3/node10.html <br />
<br />
Next, you need to create a main window.<br />
Set the display mode for the main window using <b>glutInitDisplayMode</b>. It only takes one parameter which is a combination of flags. Usually <b>GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH</b> is all you will need.<br />
<br />
More about glutInitDisplayMode: http://www.opengl.org/resources/libraries/glut/spec3/node12.html <br />
<br />
The position and size of the window is controlled using <b>glutInitWindowPosition</b> and <b>glutInitWindowSize</b>. They take 2 parameters. X and Y coordinates in the former, and width and height in the latter. You can use <b>glutGet</b> to find the screen size and center the window.<br />
<br />
More about glutInitWindowPosition, glutInitWindowSize and glutGet: http://www.opengl.org/resources/libraries/glut/spec3/node11.html <br />
http://www.opengl.org/documentation/specs/glut/spec3/node70.html <br />
<br />
Finally, the window should be created using the <b>glutCreateWindow</b> function. It will create the window and set its caption through a parameter. As a result it will return the window's handle. This can be used with other functions that require it.<br />
<br />
More about glutCreateWindow: http://www.opengl.org/resources/libraries/glut/spec3/node16.html <br />
<br />
Before your program can enter the main loop, you must set some callbacks. You will a need callback for drawing the window, for resizing and for getting keyboard input. These callbacks are set using <b>glutDisplayFunc</b>, <b>glutReshapeFunc</b> and <b>glutKeyboardFunc</b>.<br />
<br />
More about setting callbacks: http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000 <br />
<br />
Your drawing function might look like this:<br />
<br />
procedure DrawGLScene; cdecl;<br />
begin<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
glutSwapBuffers;<br />
end;<br />
<br />
This will only clear the window to the background color and reset the zbuffer (don't worry about zbuffer... more about that later).<br />
<br />
Your resize function might look like this: <br />
<br />
procedure ReSizeGLScene(Width, Height: Integer); cdecl;<br />
begin<br />
if Height = 0 then<br />
Height := 1;<br />
<br />
glViewport(0, 0, Width, Height);<br />
glMatrixMode(GL_PROJECTION);<br />
glLoadIdentity;<br />
gluPerspective(45, Width / Height, 0.1, 1000);<br />
<br />
glMatrixMode(GL_MODELVIEW);<br />
glLoadIdentity;<br />
end;<br />
<br />
With this code, you tell OpenGL where in the window it should draw and set matrices to the desired values (matrix functions will be explained later).<br />
<br />
Keyboard input is evaluated with the following callback:<br />
<br />
procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;<br />
begin<br />
if Key = 27 then<br />
Halt(0);<br />
end;<br />
<br />
This function will instruct your program to exit if you press ESC key. GLUT is event driven and the only way to terminate your program is to call <b>Halt</b> inside one of your callback functions. If you close the window in some other way, it will disappear, but the program will continue to loop through the main routine indefinitely.<br />
<br />
To start the main loop, call <b>glutMainLoop</b>. It will enter a loop that never ends, which calls all your callback functions.<br />
<br />
The main part of your program might look like this:<br />
<br />
const <br />
AppWidth = 640; <br />
AppHeight = 480; <br />
<br />
procedure InitializeGL; <br />
begin <br />
glClearColor(0.18, 0.20, 0.66, 0); <br />
end; <br />
<br />
var <br />
ScreenWidth, ScreenHeight: Integer; <br />
begin <br />
glutInitPascal(True); <br />
glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH); <br />
glutInitWindowSize(AppWidth, AppHeight); <br />
ScreenWidth := glutGet(GLUT_SCREEN_WIDTH); <br />
ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT); <br />
glutInitWindowPosition((ScreenWidth - AppWidth) div 2,<br />
(ScreenHeight - AppHeight) div 2); <br />
glutCreateWindow('OpenGL Tutorial 1'); <br />
<br />
InitializeGL; <br />
<br />
glutDisplayFunc(@DrawGLScene); <br />
glutReshapeFunc(@ReSizeGLScene); <br />
glutKeyboardFunc(@GLKeyboard); <br />
<br />
glutMainLoop; <br />
end.<br />
<br />
The next tutorial will add some code that will draw a simple shape. <br />
<br />
Download source code or a linux/windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Drawing a simple shape=<br />
<br />
This time we shall add just a few lines of code and focus on explanation of some of the OpenGL functions.<br />
<br />
Let us explain code you allready have.<br />
<br />
.<br />
.<br />
.<br />
glMatrixMode(GL_PROJECTION);<br />
glLoadIdentity;<br />
gluPerspective(45, Width / Height, 0.1, 1000);<br />
<br />
glMatrixMode(GL_MODELVIEW);<br />
glLoadIdentity;<br />
end;<br />
<br />
Using <b>glMatrixMode</b> function you chose which matrix you want to change. OpenGL works with 3 matrices:<br />
<b>GL_MODELVIEW</b>: this one is used to move vertex to model space.<br />
<b>GL_PROJECTION</b>: this one is used to convert 3d coordinate to 2d coordinate for finall pixel position.<br />
<b>GL_TEXTURE</b>: this one is used to alter texture coordinates.<br />
<br />
Once you chose matrix you want to change, you can call functions that affect matrix values. <b>glLoadIdentity</b> will reset matrix so it doesn't affect vertex position. Since almost all matrix functions multiply current matrix with a generated one, you sometimes need to clear matrix with this function.<br />
<br />
In order to set perspective matrix, you can use <b>gluPerspective</b> function. Four parameters present the field of view, aspect ratio, near and far plane. It's that simple.<br />
<br />
Now, you'll change model matrix... for this time, you just set it to identity.<br />
<br />
OK... and now, the code for drawing the first shape:<br />
<br />
procedure DrawGLScene; cdecl;<br />
begin<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(0, 0, -5);<br />
<br />
glBegin(GL_TRIANGLES);<br />
glColor3f(1, 0, 0);<br />
glVertex3f(-1, -1, 0);<br />
<br />
glColor3f(0, 1, 0);<br />
glVertex3f(1, -1, 0);<br />
<br />
glColor3f(0, 0, 1);<br />
glVertex3f(0, 1, 0);<br />
glEnd;<br />
<br />
glutSwapBuffers;<br />
end;<br />
<br />
We have allready used glClear function. It will just reset buffers. We'll skip next two functions and head for drawing ones.<br />
<br />
<b>glBegin</b> marks beginning of drawing block. After this function you can start entering vertices. Parameter describes how are vertices used when drawing:<br />
GL_POINTS: Treats each vertex as a single point. Vertex n defines point n. N points are drawn.<br />
<br />
GL_LINES: Treats each pair of vertices as an independent line segment. Vertices 2n-1 and 2n define line n. n/2 lines are drawn.<br />
<br />
GL_LINE_STRIP: Draws a connected group of line segments from the first vertex to the last. n-1 lines are drawn.<br />
<br />
GL_LINE_LOOP: Draws a connected group of line segments from the first vertex to the last, then back to the first. Vertices n and n+1 define line n. The last line, however, is defined by vertices n and 1. n lines are drawn.<br />
<br />
GL_TRIANGLES: Treats each triplet of vertices as an independent triangle. Vertices 3n-2, 3n-1 and 3n define triangle n. n/3 triangles are drawn.<br />
<br />
GL_TRIANGLE_STRIP: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n+1 and n+2 define triangle n. For even n, vertices n+1, n and n+2 define triangle n. n-2 triangles are drawn.<br />
<br />
GL_TRIANGLE_FAN: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1. n+1 and n+2 define triangle n. n-2 triangles are drawn.<br />
<br />
GL_QUADS: Treats each group of four vertices as an independent quadrilateral. Vertices 4n-3, 4n-2, 4n-1 and 4n define quadrilateral n. n/4 quadrilaterals are drawn.<br />
<br />
GL_QUAD_STRIP: Draws a connected group of quadrilaterals. One quadrilateral is defined for each pair of vertices presented after the first pair. Vertices 2n-1, 2n, 2n+2 and 2n+1 define quadrilateral n. n/2-1 quadrilaterals are drawn. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.<br />
<br />
GL_POLYGON: Draws a single, convex polygon. Vertices 1 through n define this polygon.<br />
<br />
[[Image:SimpleShapePic1.jpg|thumb]] You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. <b>glVertex3f</b> function defines the position of a vertex you want to draw. There are more glVertex* functions. Only difference is number and type of parameters they take. For instance... glVertex2i takes two parameters (x and y) of integer type. glVertex3f will almost always be just what you need.<br />
<br />
Before glVertex you can set color, material, texture... For simplicity you'll just specify color for each vertex in this tutorial. Color is set using <b>glColor3f</b> function. glColor can also take different set of parameters like glVertex.<br />
<br />
As we look through code we can see that Z is set to 0 for all vertices. Since you set near plane to 0.1, triangle will not be visible. That is where those two functions we skipped in the beginning jump in. We already know that glLoadIdentity reset matrix. <b>glTranslatef</b> moves triangles by X, Y and Z values you provide. Since you set Z to -5 (negative Z is farther from camera) all vertices will be drawn 5 units far from point of view and will be visible.<br />
<br />
In the end you call <b>glEnd</b> functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Using display lists=<br />
<br />
Sometimes you'll need to draw some object multiple times on scene. OpenGL has ability to build <b>display lists</b> which make drawing a bit faster. Creating display list is very easy... just draw vertices as you did in previous tutorial and enclose them with <b>glNewList</b> and <b>glEndList</b> calls.<br />
<br />
const<br />
LIST_OBJECT = 1;<br />
<br />
procedure CreateList;<br />
begin<br />
glNewList(LIST_OBJECT, GL_COMPILE);<br />
glBegin(GL_TRIANGLE_FAN);<br />
glColor3f(1, 0, 0);<br />
glVertex3f(0, 0.5, 0);<br />
<br />
glColor3f(1, 1, 0);<br />
glVertex3f(-0.5, -0.5, 0.5);<br />
<br />
glColor3f(1, 1, 1);<br />
glVertex3f(0.5, -0.5, 0.5);<br />
<br />
glColor3f(0, 1, 1);<br />
glVertex3f(0.5, -0.5, -0.5);<br />
<br />
glColor3f(0, 0, 1);<br />
glVertex3f(-0.5, -0.5, -0.5);<br />
<br />
glColor3f(0, 1, 0);<br />
glVertex3f(-0.5, -0.5, 0.5);<br />
glEnd;<br />
<br />
glBegin(GL_QUADS);<br />
glColor3f(1, 1, 0);<br />
glVertex3f(-0.5, -0.5, 0.5);<br />
<br />
glColor3f(1, 1, 1);<br />
glVertex3f(0.5, -0.5, 0.5);<br />
<br />
glColor3f(0, 1, 1);<br />
glVertex3f(0.5, -0.5, -0.5);<br />
<br />
glColor3f(0, 0, 1);<br />
glVertex3f(-0.5, -0.5, -0.5);<br />
<br />
glColor3f(0, 1, 0);<br />
glVertex3f(-0.5, -0.5, 0.5);<br />
glEnd;<br />
glEndList;<br />
end;<br />
<br />
<b>glNewList</b> creates new display list and all drawing functions will be recorded until <b>glEndList</b> is called. The first parameter for glNewList function is list ID. Every list is defined by it's ID. If list with given ID is already created it fill be cleared before recording. If the second parameter is GL_COMPILE then all drawing functions are just recorded, but if it is GL_COMPILE_AND_EXECUTE then they are recorded and executed automaticly.<br />
<br />
<b>glIsList</b> function can help you with display lists. It can tell if some list ID is already filled with data.<br />
Another useful function is <b>glGenLists</b>. It will create multiple empty display lists. You pass number of display lists you need and you get ID of the first one. If you require n lists, and get r ID, generated display lists are: r, r+1, r+2,..., r+n-1<br />
<br />
All created lists should be deleted. You will do that before program exits:<br />
<br />
procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;<br />
begin<br />
if Key = 27 then<br />
begin<br />
glDeleteLists(LIST_OBJECT, 1);<br />
Halt(0);<br />
end;<br />
end;<br />
<br />
<b>glDeleteLists</b> takes 2 parameters, ID of display list and number of lists to delete. If ID is r, and number of lists to delete is n, deleted lists are: r, r+1, r+2,..., r+n-1<br />
<br />
Now you know how to create and delete display lists... let's see how to draw them:<br />
<br />
procedure DrawGLScene; cdecl;<br />
begin<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(-2, 0, -5);<br />
glRotatef(40, 1, 0, 1);<br />
glCallList(LIST_OBJECT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(1, -2, -10);<br />
glRotatef(62, 0, 1, 0);<br />
glCallList(LIST_OBJECT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(-4, 0.5, -15);<br />
glRotatef(200, 1, 0, 0);<br />
glCallList(LIST_OBJECT);<br />
<br />
glutSwapBuffers;<br />
end;<br />
<br />
[[Image:DisplayListsPic1.jpg|thumb]] Using <b>glCallList</b> you can draw only one display list. In this tutorial, before drawing display list, you change model matrix and draw object in different places.<br />
Some times you would like to draw multiple lists at once. That is possible using <b>glCallLists</b> function. It takes number of lists you want to draw, type of array that contains display list IDs and array with display list IDs. Type of list can be one of the following:<br />
<br />
GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.<br />
<br />
GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.<br />
<br />
GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.<br />
<br />
GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.<br />
<br />
GL_INT: lists is treated as an array of signed four-byte integers.<br />
<br />
GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.<br />
<br />
GL_FLOAT: list is treated as an array of four-byte floating-point values.<br />
<br />
GL_2_BYTES: list is treated as an array of unsigned bytes. Each pair of bytes specifies a single display list ID. The value of the pair is computed as 256 times the unsigned value of the first byte plus the unsigned value of the second byte.<br />
<br />
GL_3_BYTES: list is treated as an array of unsigned bytes. Each triplet of bytes specifies a single display list ID. The value of the triplet is computed as 65536 times the unsigned value of the first byte, plus 256 times the unsigned value of the second byte, plus the unsigned value of the third byte.<br />
<br />
GL_4_BYTES: list is treated as an array of unsigned bytes. Each quadruplet of bytes specifies a single display list ID. The value of the quadruplet is computed as 16777216 times the unsigned value of the first byte, plus 65536 times the unsigned value of the second byte, plus 256 times the unsigned value of the third byte, plus the unsigned value of the fourth byte.<br />
<br />
That is for now. Next tutorial will show how to create little planetary system. We'll talk about matrices and how to make animated scene that doesn't depend of number of frames per second.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Full screen animation=<br />
<br />
Entering full screen mode is easy with GLUT. Let's change main part of the program:<br />
<br />
const<br />
FSMode = '800x600:32@75';<br />
<br />
begin<br />
glutInitPascal(False);<br />
glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);<br />
glutGameModeString(FSMode);<br />
glutEnterGameMode;<br />
glutSetCursor(GLUT_CURSOR_NONE);<br />
<br />
InitializeGL;<br />
<br />
glutDisplayFunc(@DrawGLScene);<br />
glutReshapeFunc(@ReSizeGLScene);<br />
glutKeyboardFunc(@GLKeyboard);<br />
glutIdleFunc(@DrawGLScene);<br />
<br />
glutMainLoop;<br />
end.<br />
<br />
Since we don't want GLUT to parse command line this time we call glutInitPascal with False parameter. As you can see, there is no code for window creation. GLUT have <b>glutEnterGameMode</b> that create full screen window. To specify what kind of full screen mode you want, you call <b>glutGameModeString</b> function which takes string that defines mode you like.<br />
Format of that string is:<br />
<br />
[width "x" height][":" bpp]["@" hertz]<br />
<br />
In FSMode string we declared that full screen mode should be 800x600, with 32bit pallete and 75Hz refresh. It is possible to skip one of the group. If you omit size, GLUT will try to use current one or first smaller that can work. That policy is used and for other parameters.<br />
<br />
Usually in full screen mode cursor is not visible. To hide cursor you use <b>glutSetCursor</b> function. It takes only one parameter which describes cursor you would like to see:<br />
GLUT_CURSOR_RIGHT_ARROW<br />
GLUT_CURSOR_LEFT_ARROW<br />
GLUT_CURSOR_INFO<br />
GLUT_CURSOR_DESTROY<br />
GLUT_CURSOR_HELP<br />
GLUT_CURSOR_CYCLE<br />
GLUT_CURSOR_SPRAY<br />
GLUT_CURSOR_WAIT<br />
GLUT_CURSOR_TEXT<br />
GLUT_CURSOR_CROSSHAIR<br />
GLUT_CURSOR_UP_DOWN<br />
GLUT_CURSOR_LEFT_RIGHT<br />
GLUT_CURSOR_TOP_SIDE<br />
GLUT_CURSOR_BOTTOM_SIDE<br />
GLUT_CURSOR_LEFT_SIDE<br />
GLUT_CURSOR_RIGHT_SIDE<br />
GLUT_CURSOR_TOP_LEFT_CORNER<br />
GLUT_CURSOR_TOP_RIGHT_CORNER<br />
GLUT_CURSOR_BOTTOM_RIGHT_CORNER<br />
GLUT_CURSOR_BOTTOM_LEFT_CORNER<br />
GLUT_CURSOR_FULL_CROSSHAIR<br />
GLUT_CURSOR_NONE<br />
GLUT_CURSOR_INHERIT<br />
<br />
<b>glutIdleFunc</b> defines callback function that you want to be called every time you program has no messages to process. Since we just want to render new frame if there is nothing to do, just set idle function to DrawGLScene.<br />
Some other tutorials show that idle function should send refresh message insted of drawing, but that way I have 50-100 frames less than using method I described.<br />
<br />
Now, let's look at the program termination where you need to exit full screen mode:<br />
<br />
procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;<br />
begin<br />
if Key = 27 then<br />
begin<br />
glutLeaveGameMode;<br />
Halt(0);<br />
end;<br />
end;<br />
<br />
As you can see, all you need to do is to call <b>glutLeaveGameMode</b>.<br />
<br />
Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:<br />
<br />
procedure ReSizeGLScene(Width, Height: Integer); cdecl;<br />
begin<br />
.<br />
.<br />
.<br />
<br />
glMatrixMode(GL_MODELVIEW);<br />
glLoadIdentity;<br />
gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);<br />
end;<br />
<br />
<b>gluLookAt</b> create matrix that will define from where are you look to objects. First 3 parameters are X, Y and Z coordinate of position of camera. Next 3 parameters are X, Y and Z coordinate of point where camera look at, and last 3 parameters defines "up" vector (where is "up" for the camera). Usually, up is positive y axis.<br />
<br />
OK, let's draw now. Since you set matrix with gluLookAt that should be used with all objects, you can't just use glLoadIdentity to reset matrix for next object... you'll save previous matrix state and restore it after object is drawn:<br />
<br />
procedure DrawGLScene; cdecl;<br />
var<br />
T: Single;<br />
begin<br />
T := glutGet(GLUT_ELAPSED_TIME) / 1000;<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
<br />
glPushMatrix;<br />
glRotatef(5 * T, 0, 1, 0);<br />
glColor3f(1, 1, 0);<br />
glutWireSphere(2, 20, 20);<br />
glPopMatrix;<br />
<br />
glPushMatrix;<br />
glRotatef(90 * T, 0, 1, 0);<br />
glTranslatef(5, 0, 0);<br />
glRotatef(40 * T, 0, 1, 0);<br />
glColor3f(1, 0, 0);<br />
glutWireSphere(0.6, 10, 10);<br />
glPopMatrix;<br />
<br />
glPushMatrix;<br />
glRotatef(60 * T, 0, 1, 0);<br />
glTranslatef(-3, 0, 9);<br />
glRotatef(50 * T, 0, 1, 0);<br />
glColor3f(0, 1, 0);<br />
glutWireSphere(1, 16, 16);<br />
<br />
glPushMatrix;<br />
glRotatef(360 * T, 0, 1, 0);<br />
glTranslatef(-1.7, 0, 0);<br />
glRotatef(50 * T, 0, 1, 0);<br />
glColor3f(0, 0, 1);<br />
glutWireSphere(0.4, 10, 10);<br />
glPopMatrix;<br />
<br />
glPopMatrix;<br />
<br />
glutSwapBuffers;<br />
end;<br />
<br />
[[Image:FullScreenAnimationPic1.jpg|thumb]] <b>glPushMatrix</b> i <b>glPopMatrix</b> are used to save and restore matrix state. As you can see, we save matrix state, then change matrix in order to draw object in right place, and then restore old matrix state.<br />
<br />
You may wonder what is <b>T</b> variable for. Well, it is used to determen animation speed. Every change that depends on time is multiplied with T. That way animation speed is constant on every frame rate.<br />
<b>glutGet</b> function with <b>GLUT_ELAPSED_TIME</b> parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.<br />
<br />
<b>glRotatef</b> function create rotation matrix. First parameter is angle in degrees, and last 3 parameters defines axis around which rotation will be done.<br />
Since you multiplied angle with T, object will be rotated by that angle in exactly 1 second.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Light=<br />
<br />
This tutorial will introduce some light to the scene. You'll make rotating cube and one light which will add some realism to the scene, but first let's make some utility unit.<br />
<br />
For now it will have only basic functions to help us getting current and delta (time that elapsed from one render to other render call) times and for calculating frames per second.<br />
<br />
unit utils;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
glut;<br />
<br />
function GetTotalTime: Single;<br />
function GetDeltaTime: Single;<br />
procedure FrameRendered(Count: Integer = 1);<br />
function GetFPS: Single;<br />
<br />
implementation<br />
<br />
var<br />
OldTime: Integer = 0;<br />
FPSTime: Integer = 0;<br />
FPSCount: Integer = 0;<br />
<br />
function GetTotalTime: Single;<br />
begin<br />
Result := glutGet(GLUT_ELAPSED_TIME) / 1000;<br />
end;<br />
<br />
function GetDeltaTime: Single;<br />
var<br />
NewTime: Integer;<br />
begin<br />
NewTime := glutGet(GLUT_ELAPSED_TIME);<br />
Result := (NewTime - OldTime) / 1000;<br />
OldTime := NewTime;<br />
end;<br />
<br />
procedure FrameRendered(Count: Integer);<br />
begin<br />
Inc(FPSCount, Count);<br />
end;<br />
<br />
function GetFPS: Single;<br />
var<br />
NewTime: Integer;<br />
begin<br />
NewTime := glutGet(GLUT_ELAPSED_TIME);<br />
<br />
Result := FPSCount / ((NewTime - FPSTime) / 1000);<br />
<br />
FPSTime := NewTime;<br />
FPSCount := 0;<br />
end;<br />
<br />
end.<br />
<br />
As you can see, there is nothing complicated in this unit. Time is simply saved betwen calls and difference is returned. FrameRendered should be called every time you draw scene so function can calculate FPS.<br />
<br />
Now, let's have fun with lights.<br />
<br />
OpenGL have several types of light... ambient, diffuse, point, spot, specular and emissive light.<br />
<br />
Ambient light is something like Sun. When sun rays pass through the window of a room they hit the walls and are reflected and scattered into all different directions which averagely brightens up the whole room. All vertices are lit with ambient light.<br />
<br />
Diffuse light can be represented as parallel light rays comming from far away. They will lit only vertices that are oriented towards the light source.<br />
<br />
Point light lights all around it. It is like a fire ball, it send light rays all around it and lights vertices that are oriented towards light source and that are close enough.<br />
<br />
Spot light is like light from flashlight. It is simply a point light source with a small light cone radius. All vertices that falls inside of cone and are close enough are lit.<br />
<br />
Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.<br />
<br />
Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.<br />
<br />
For simplicity we'll use only diffuse light in this tutorial. Later on, some other lights may appear in tutorials :)<br />
<br />
Let's see how to enable light in scene:<br />
<br />
const<br />
DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);<br />
<br />
glEnable(GL_LIGHTING);<br />
glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);<br />
glEnable(GL_LIGHT0);<br />
<br />
As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with <b>glLightfv</b> function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL what light parameter to change, and the last one is new parameter for light.<br />
You'll set just diffuse color for light in this tutorial. After that, you can enable light and there will be light in the scene... but... that is not all.<br />
<br />
More about glLightfv: http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html<br />
<br />
If you want to use lights you can't just set color for vertex... you must set material for vertices. Let's setup material for drawing:<br />
<br />
glEnable(GL_COLOR_MATERIAL);<br />
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);<br />
<br />
[[Image:LightPic1.jpg|thumb]] You expected something more complicated, do you? :) Well, this code allows us to use glColor function to set material to vertices. By using glEnable function and GL_COLOR_MATERIAL flag, you can define what material properties will glColor change. <b>glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)</b> tells OpenGL that glColor changes ambient and diffuse material. We'll discus materials more in later tutorials.<br />
<br />
One more thing that is important when using lights... every vertex must have normal associated with it. Normal is used to find the direction of vertex so light can be calculated properly. You'll use GLUT function to draw cube and it provides normals for us, so this time we'll just walk by normals.<br />
<br />
After all those setting ups, light will shine up your cube :)<br />
<br />
Part of the text is copied from [http://www.falloutsoftware.com/tutorials/gl/gl8.htm The OpenGL Light Bible]<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Bitmap fonts=<br />
<br />
Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.<br />
<br />
First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.<br />
<br />
Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:<br />
<br />
function glGetViewportWidth: Integer;<br />
var<br />
Rect: array[0..3] of Integer;<br />
begin<br />
glGetIntegerv(GL_VIEWPORT, @Rect);<br />
Result := Rect[2] - Rect[0];<br />
end;<br />
<br />
function glGetViewportHeight: Integer;<br />
var<br />
Rect: array[0..3] of Integer;<br />
begin<br />
glGetIntegerv(GL_VIEWPORT, @Rect);<br />
Result := Rect[3] - Rect[1];<br />
end;<br />
<br />
We just get left/right, top/bottom and calculate width/height by subtracting them.<br />
<br />
There must be functions for entering and leaving 2D mode:<br />
<br />
procedure glEnter2D;<br />
begin<br />
glMatrixMode(GL_PROJECTION);<br />
glPushMatrix;<br />
glLoadIdentity;<br />
gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);<br />
<br />
glMatrixMode(GL_MODELVIEW);<br />
glPushMatrix;<br />
glLoadIdentity;<br />
<br />
glDisable(GL_DEPTH_TEST);<br />
end;<br />
<br />
procedure glLeave2D;<br />
begin<br />
glMatrixMode(GL_PROJECTION);<br />
glPopMatrix;<br />
glMatrixMode(GL_MODELVIEW);<br />
glPopMatrix;<br />
<br />
glEnable(GL_DEPTH_TEST);<br />
end;<br />
<br />
When entering 2D mode, we save current matrices and set 2D matrix using <b>gluOrtho2D</b> function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.<br />
<br />
Leaving 2D mode just returns old matrices and enable ZBuffer.<br />
<br />
Now, we can create function for text drawing:<br />
<br />
procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);<br />
var<br />
I: Integer;<br />
begin<br />
glRasterPos2f(X, Y);<br />
for I := 1 to Length(Text) do<br />
glutBitmapCharacter(Font, Integer(Text[I]));<br />
end;<br />
<br />
<b>glutBitmapCharacter</b> can draw only one character of selected font. First parameter is desired font (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 or GLUT_BITMAP_HELVETICA_18) and other one is character.<br />
<br />
Character will be drawn at current raster position. To set desired raster position we call <b>glRasterPos</b> function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.<br />
<br />
This new functions will make text drawing very easy:<br />
<br />
procedure DrawGLScene; cdecl;<br />
begin<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(0, 0, -5);<br />
glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);<br />
<br />
glColor3f(1, 0, 0);<br />
glutSolidCube(2);<br />
<br />
glEnter2D;<br />
<br />
glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);<br />
glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,<br />
Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));<br />
<br />
glColor3f(1, 1, 1);<br />
glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');<br />
glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');<br />
glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');<br />
glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');<br />
glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');<br />
glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');<br />
glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');<br />
<br />
glColor3f(0.5, 0.5, 1);<br />
glWrite(<br />
glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,<br />
10, GLUT_BITMAP_9_BY_15, LazText);<br />
<br />
glLeave2D;<br />
<br />
glutSwapBuffers;<br />
<br />
FrameRendered;<br />
end;<br />
[[Image:BitmapFontsPic1.jpg|thumb]]<br />
We draw red cube and rotate it, and some text to show how various bitmap fonts look like.<br />
<b>glutBitmapLength</b> function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.<br />
<br />
Note: See how cube looks without light.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Textures=<br />
<br />
It's time to use textures :)<br />
<br />
This tutorial will show how to draw textured polygons and how to blend textures using multipass technic.<br />
Since OpenGL has no builtin mechanism for loading textures, we'll use external library: [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].<br />
We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.<br />
<br />
Let's get started... we'll create display list for drawing textured rectangle:<br />
<br />
procedure CreateList;<br />
begin<br />
glNewList(LIST_OBJECT, GL_COMPILE);<br />
glBegin(GL_QUADS);<br />
glTexCoord2f(1, 0);<br />
glVertex3f( 2, 2, 0);<br />
glTexCoord2f(0, 0);<br />
glVertex3f(-2, 2, 0);<br />
glTexCoord2f(0, 1);<br />
glVertex3f(-2,-2, 0);<br />
glTexCoord2f(1, 1);<br />
glVertex3f( 2,-2, 0);<br />
glEnd;<br />
glEndList;<br />
end;<br />
<br />
Notice <b>glTexCoord</b> functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.<br />
<br />
Texture loading is extremely easy with Vampyre Imaging Library:<br />
<br />
var<br />
Tex1, Tex2: GLuint;<br />
<br />
procedure InitializeGL;<br />
begin<br />
glClearColor(0, 0, 0, 0);<br />
Tex1 := LoadGLTextureFromFile('ashwood.bmp');<br />
Tex2 := LoadGLTextureFromFile('Flare.bmp');<br />
glEnable(GL_TEXTURE_2D);<br />
end;<br />
<br />
<b>LoadGLTextureFromFile</b> loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering.<br />
Last line just enables 2D textures.<br />
<br />
To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):<br />
<br />
...<br />
glLoadIdentity;<br />
glTranslatef(-5, 0, -15);<br />
glBindTexture(GL_TEXTURE_2D, Tex1);<br />
glCallList(LIST_OBJECT);<br />
...<br />
<br />
<b>glBindTexture</b> function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)<br />
<br />
So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:<br />
<br />
...<br />
glLoadIdentity;<br />
glTranslatef(5, 0, -15);<br />
glBindTexture(GL_TEXTURE_2D, Tex1);<br />
glCallList(LIST_OBJECT);<br />
<br />
glEnable(GL_BLEND);<br />
glBlendFunc(GL_ZERO, GL_SRC_COLOR);<br />
glLoadIdentity;<br />
glTranslatef(5, 0, -15);<br />
glBindTexture(GL_TEXTURE_2D, Tex2);<br />
glCallList(LIST_OBJECT);<br />
glDisable(GL_BLEND);<br />
...<br />
<br />
As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling <b>glEnable(GL_BLEND)</b>. Blending means that finall pixel color is calculated like this:<br />
<br />
DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND<br />
<br />
SRCBLEND and DESTBLEND are defined using <b>glBlendFunc</b> function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:<br />
<br />
DrawingColor * 0 + BackgroundColor * DrawingColor<br />
[[Image:TexturesPic1.jpg|thumb]]<br />
<br />
This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this<br />
<br />
Next time, we'll use extensions to show how to use singlepass multitexturing.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Multitexturing (extensions)=<br />
<br />
When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. All you have to do is to setup texture stages and to render object.<br />
<br />
Let's see how code looks like:<br />
<br />
procedure InitializeGL;<br />
begin<br />
Load_GL_ARB_multitexture;<br />
glClearColor(0, 0, 0, 0);<br />
Tex1 := LoadGLTextureFromFile('Lazarus.bmp');<br />
Tex2 := LoadGLTextureFromFile('Mask.bmp');<br />
glActiveTextureARB(GL_TEXTURE0_ARB);<br />
glEnable(GL_TEXTURE_2D);<br />
glBindTexture(GL_TEXTURE_2D, Tex1);<br />
glActiveTextureARB(GL_TEXTURE1_ARB);<br />
glEnable(GL_TEXTURE_2D);<br />
glBindTexture(GL_TEXTURE_2D, Tex2);<br />
end;<br />
<br />
First we need load OpenGL extension that will allow us to use multitexture functions. <b>Load_GL_ARB_multitexture</b> will try to load those extensions and will return TRUE if operation was successful.<br />
<br />
To select texture stage you want to work on, use <b>glActiveTextureARB</b> function. It takes only one parameter that define which stage you want. After that all texture functions (enabling, disabling, binding, creating...) will affect that stage.<br />
<br />
Since we setup every thing in initialization function, all we have to do is to draw object:<br />
<br />
procedure DrawGLScene; cdecl;<br />
begin<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
<br />
glLoadIdentity;<br />
glTranslatef(0, 0, -5);<br />
<br />
glBegin(GL_QUADS);<br />
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);<br />
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);<br />
glVertex3f(2.516, 2, 0);<br />
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);<br />
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);<br />
glVertex3f(-2.516, 2, 0);<br />
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);<br />
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);<br />
glVertex3f(-2.516,-2, 0);<br />
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);<br />
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);<br />
glVertex3f(2.516,-2, 0);<br />
glEnd;<br />
<br />
glutSwapBuffers;<br />
end;<br />
[[Image:MultitexturePic1.jpg|thumb]]<br />
As you can see, difference is only in defining texture coordinations. We now use <b>glMultiTexCoord2fARB</b> function that takes texture stage and texture coordinations. Every thing else is unchanged.<br />
<br />
Today almost all graphic cards supports at least 2 texture stages. Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Render to texture=<br />
<br />
This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.<br />
<br />
First, we must create empty texture which well use to capture scene:<br />
<br />
procedure SetupRenderTexture;<br />
var<br />
Data: Pointer;<br />
begin<br />
GetMem(Data, 256*256*3);<br />
glGenTextures(1, @RenderTexture);<br />
glBindTexture(GL_TEXTURE_2D, RenderTexture);<br />
glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);<br />
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);<br />
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);<br />
FreeMem(Data);<br />
end;<br />
<br />
Buffer for 256*256 RGB image is created and it is used to setup 2D texture.<br />
<br />
Main part is in drawing function:<br />
<br />
procedure DrawGLScene; cdecl;<br />
var<br />
TotalTime: Single;<br />
begin<br />
glClearColor(0, 0, 0, 0);<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
glEnable(GL_LIGHTING);<br />
glDisable(GL_TEXTURE_2D);<br />
glViewport(0, 0, 256, 256);<br />
<br />
TotalTime := GetTotalTime;<br />
<br />
glLoadIdentity;<br />
glTranslatef(0, 0, -5);<br />
glRotatef(50 * TotalTime, 1, 0, 0);<br />
glRotatef(100 * TotalTime, 0, 1, 0);<br />
glRotatef(50 * TotalTime, 0, 0, 1);<br />
<br />
glColor3f(1, 1, 1);<br />
glutSolidCube(2);<br />
<br />
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);<br />
<br />
glClearColor(0.18, 0.20, 0.66, 0);<br />
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);<br />
glDisable(GL_LIGHTING);<br />
glEnable(GL_TEXTURE_2D);<br />
glViewport(0, 0, AppWidth, AppHeight);<br />
<br />
glLoadIdentity;<br />
glTranslatef(0, 0, -7);<br />
glRotatef(20 * TotalTime, 1, 0, 0);<br />
glRotatef(50 * TotalTime, 0, 1, 0);<br />
<br />
glBegin(GL_QUADS);<br />
glTexCoord2f(1, 0);<br />
glVertex3f(2, 2, 0);<br />
glTexCoord2f(0, 0);<br />
glVertex3f(-2, 2, 0);<br />
glTexCoord2f(0, 1);<br />
glVertex3f(-2,-2, 0);<br />
glTexCoord2f(1, 1);<br />
glVertex3f(2,-2, 0);<br />
glEnd;<br />
<br />
glutSwapBuffers;<br />
end;<br />
[[Image:RenderToTexturePic1.jpg|thumb]]<br />
First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. <b>glCopyTexImage2D</b> is used to capture scene to currently selected texture.<br />
<br />
When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.<br />
<br />
P.S. Captured texture can be saved using <b>SaveGLTextureToFile</b> function from [http://imaginglib.sourceforge.net/ Vampyre Imaging Library].<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
=Vertex array=<br />
<br />
OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.<br />
<br />
In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.<br />
<br />
First, let's define some types and constants:<br />
<br />
type<br />
TVertex3f = record<br />
X, Y, Z: Single;<br />
end;<br />
<br />
TColor3f = record<br />
R, G, B: Single;<br />
end;<br />
<br />
VertexBuffer: array [0..5] of TVertex3f = (<br />
(X : 1; Y : 1; Z : 0),<br />
(X : -1; Y : 1; Z : 0),<br />
(X : -1; Y : -1; Z : 0),<br />
(X : 1; Y : 1; Z : 0),<br />
(X : -1; Y : -1; Z : 0),<br />
(X : 1; Y : -1; Z : 0)<br />
);<br />
ColorBuffer: array [0..5] of TColor3f = (<br />
(R : 1; G : 0; B : 1),<br />
(R : 0; G : 0; B : 1),<br />
(R : 0; G : 1; B : 0),<br />
(R : 1; G : 0; B : 1),<br />
(R : 0; G : 1; B : 0),<br />
(R : 1; G : 1; B : 0)<br />
);<br />
<br />
We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.<br />
<br />
Drawing primitives using buffers is easy:<br />
<br />
glEnableClientState(GL_VERTEX_ARRAY);<br />
glEnableClientState(GL_COLOR_ARRAY);<br />
glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);<br />
glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);<br />
<br />
glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));<br />
<br />
glDisableClientState(GL_VERTEX_ARRAY);<br />
glDisableClientState(GL_COLOR_ARRAY);<br />
<br />
First we enable buffers we want to use using <b>glEnableClientState</b> function. Than we can select buffers we want to use. Every buffer type has own function for selecting (<b>glColorPointer</b>, <b>glEdgeFlagPointer</b>, <b>glIndexPointer</b>, <b>glNormalPointer</b>, <b>glTexCoordPointer</b>, <b>glVertexPointer</b>).<br />
First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate.<br />
Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE).<br />
Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:<br />
<br />
type<br />
TBufferData = record<br />
DataBefore: TDataBefore;<br />
Vertex: TVertex;<br />
DataAfter: TDataAfter;<br />
end;<br />
<br />
Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)<br />
<br />
Last parameter if pointer to the begginig of buffer.<br />
<br />
When buffers are selected we can draw them using <b>glDrawArrays</b> functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).<br />
<br />
When buffers are not needed you can disable them.<br />
<br />
To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:<br />
<br />
type<br />
TMesh = class<br />
private<br />
FVertices: array of TVertex3f;<br />
FColors: array of TColor3f;<br />
FIndices: array of Integer;<br />
procedure FreeBuffers;<br />
public<br />
constructor Create;<br />
destructor Destroy; override;<br />
procedure LoadMesh(FileName: String);<br />
procedure DrawMesh;<br />
end;<br />
<br />
FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.<br />
<br />
First we'll write some code that deals with creation and destruction of class:<br />
<br />
procedure TMesh.FreeBuffers;<br />
begin<br />
FVertices := nil;<br />
FColors := nil;<br />
FIndices := nil;<br />
end;<br />
<br />
constructor TMesh.Create;<br />
begin<br />
FreeBuffers;<br />
end;<br />
<br />
destructor TMesh.Destroy;<br />
begin<br />
FreeBuffers;<br />
inherited Destroy;<br />
end;<br />
<br />
File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:<br />
<br />
3 3<br />
-1 -1 0 1 1 1<br />
1 -1 0 1 1 1<br />
0 1 0 1 1 1<br />
0<br />
1<br />
2<br />
<br />
This means that there is 3 vertices and 3 indices defined in file. First vrtex is at -1, -1, 0 and has color 1, 1, 1 and so on. Indices defines that order in which vertices are drawn (in this case vertices are drawn in the same order as they are defined).<br />
<br />
Code for loading this data will loke like this:<br />
<br />
procedure TMesh.LoadMesh(FileName: String);<br />
var<br />
MeshFile: TextFile;<br />
VertexCount, IndexCount: Integer;<br />
iV, iI: Integer;<br />
begin<br />
FreeBuffers;<br />
<br />
AssignFile(MeshFile, FileName);<br />
Reset(MeshFile);<br />
<br />
ReadLn(MeshFile, VertexCount, IndexCount);<br />
<br />
SetLength(FVertices, VertexCount);<br />
SetLength(FColors, VertexCount);<br />
SetLength(FIndices, IndexCount);<br />
<br />
for iV := 0 to VertexCount - 1 do<br />
ReadLn(MeshFile,<br />
FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,<br />
FColors[iV].R, FColors[iV].G, FColors[iV].B);<br />
<br />
for iI := 0 to IndexCount - 1 do<br />
ReadLn(MeshFile, FIndices[iI]);<br />
<br />
CloseFile(MeshFile);<br />
end;<br />
<br />
After loading data, we have everything for drawing:<br />
<br />
procedure TMesh.DrawMesh;<br />
begin<br />
glEnableClientState(GL_VERTEX_ARRAY);<br />
glEnableClientState(GL_COLOR_ARRAY);<br />
glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);<br />
glColorPointer(3, GL_FLOAT, 0, @FColors[0]);<br />
<br />
glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);<br />
<br />
glDisableClientState(GL_VERTEX_ARRAY);<br />
glDisableClientState(GL_COLOR_ARRAY);<br />
end;<br />
<br />
As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use <b>glDrawElements</b> function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.<br />
<br />
[[Image:VertexArrayPic1.jpg|thumb]]Full source code comes with mesh data file that this class can use to generates rectangle that is identical with one that is drawn using non-indexed mode. Mesh data file looks like this:<br />
<br />
4 6<br />
1 1 0 1 0 1<br />
-1 1 0 0 0 1<br />
-1 -1 0 0 1 0<br />
1 -1 0 1 1 0<br />
0<br />
1<br />
2<br />
0<br />
2<br />
3<br />
<br />
As you can see, there is data for only 4 vertices and 6 indices. So, first triangle is defined by vertices 0, 1 and 2, and the seccond one by vertices 0, 2 and 3. By using indexed mode we don't have to duplicate vertices.<br />
<br />
Download source code, linux executable or windows executable from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145 Lazarus CCR SourceForge].<br />
<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Contacts_Database&diff=18863Contacts Database2007-05-11T09:26:12Z<p>Dejvid: /* Available Downloads */</p>
<hr />
<div>== About ==<br />
<br />
Contacts Database is a small application that builds on the example address book application that comes packaged with Lazarus. This version has an altered database structure and allows the user to print either the current record or all of the database records.<br />
<br />
It makes use of the '''TDBF''' component and also the '''LazReport''' components.<br />
<br />
The Windows version comes wrapped in an installer. It contains both the Binary executable and the full source code of the application.<br />
<br />
The Linux version comes as two seperate downloadable files. The executable binary, with a small database, in .tar.gz format and the source code also in .tar.gz format.<br />
<br />
<br />
== License ==<br />
<br />
Open Source. Use as you see fit.<br />
<br />
== Current State - Version 1 ==<br />
<br />
* The GUI is in a suitable state.<br />
* Print routines have been implemented.<br />
* LazReport has been used to print out the full database.<br />
* There is no documentation at the moment. The souce code has some rudimentary explanations.<br />
* The Linux version source code is a little different to account for slight differences in screen display and printer settings.<br />
<br />
== Available Downloads ==<br />
<br />
Binary downloads for windows and Linux and the source packages are available at [http://www.wibblytim.co.uk/contact.html The WibblyTim site] or [https://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=436502 Lazarus CCR].<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Libview&diff=18862Libview2007-05-11T09:25:44Z<p>Dejvid: category:Example programs</p>
<hr />
<div>=== About ===<br />
<br />
This example software can load a library and displays all function names it exports.<br />
<br />
Currently the software works only on Windows, and it was designed<br />
to help generating pascal bindings for c / c++ libraries which put strange<br />
prefixes and suffixes to the exported function names.<br />
<br />
It someone decides to add more functionality to this software, like the display<br />
of more information about the dll or support for other platforms, please consider<br />
sending us the project on the Lazarus mailling list to be the next version of libview.<br />
<br />
=== Screenshot ===<br />
<br />
[[Image:Libview.PNG]]<br />
<br />
Libview 0.1 showing all exported functions from httpd.dll library.<br />
<br />
=== Authors ===<br />
<br />
[[User:Sekelsenmat|Felipe Monteiro de Carvalho]]<br />
<br />
=== License ===<br />
<br />
Public domain.<br />
<br />
=== Download ===<br />
<br />
http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=197380<br />
<br />
=== CVS ===<br />
<br />
Not yet available.<br />
<br />
=== Change Log ===<br />
<br />
*16.07.06 Libview version 0.1 released<br />
# Working well under Windows.<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Sudoku&diff=18861Sudoku2007-05-11T09:25:05Z<p>Dejvid: category:Example programs</p>
<hr />
<div>===About===<br />
There is very little to tell about this program. It is just an attempt to make a program which solves Sudoku puzzles.<br />
<br />
===License===<br />
See the sources. I just hope it is a useful demo. Useful as in you can learn something from it.<br />
<br />
===Author===<br />
[[User:Matthijs|Matthijs Willemstein]]<br />
<br />
===Download===<br />
The application can be found on the [http://sourceforge.net/project/showfiles.php?group_id=92177 Lazarus CCR Files page].<br />
<br />
===Usage===<br />
If you run the program you will see a grid, and two buttons. If you press the "Fill" button, you are able to edit the grid and place the numbers in the grid according to your puzzle.<br><br />
When you are finished entering the digits press "Solve" and the grid will be filled with the solution.<br />
<br />
===Description===<br />
The programs solves all Sudoku puzzles that can be solved by logic. It will not solve the puzzle where you need to guess for a solution.<br />
<br />
So it follows these rules (taken from [http://www.websudoku.com/ WebSudoku]):<br />
<br />
The rules of Sudoku are simple. Enter digits from 1 to 9 into the blank spaces. Every row must contain one of each digit. So must every column, as must every 3x3 square.<br />
Each Sudoku has a unique solution that can be reached logically without guessing.<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Micro-mainframe_Transmission_Command_Generator_for_IBM_Mainframes&diff=18860Micro-mainframe Transmission Command Generator for IBM Mainframes2007-05-11T09:24:32Z<p>Dejvid: category:Example programs</p>
<hr />
<div>==About==<br />
<br />
The application was written to run under windows. I suppose that it runs under any version of windows. It uses basic constructions and commands of Lazarus.<br />
<br />
==License==<br />
This application can be used under Lazarus modified LGPL and MPL, this allows making commercial projects that use this code but NO WARANTY, you are the only responsable if this code doesn't work or causes any problems to your computer. (I have doubts about this mr. Snijders)<br />
<br />
==Author==<br />
<br />
[[User:Nightrider|Arí Ricardo Ody - Sao Paulo - Brazil]]<br />
<br />
==Download==<br />
You can download zip with windows executable and source or just a source zip from the [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=378755 Lazarus CCR sourceforge download area].<br />
<br />
==Change log==<br />
* 16 December 2005: Initial version.<br />
* Last version: may, 2006.<br />
<br />
==Usage==<br />
To use the program simply run it. In the first screen click the left screen button to go to the next screen(The idea is to implement new functions in this screen in the future). In the second screen you must inform the name of a partitioned or a sequential data set existing in the mainframe disks. One of these data sets will be used as input or output of the transmissions. You will inform the type of transmission(texto for ASCII-EBCDIC and vice versa convertion or binario for no convertion), the type of emulator you will use and the direction of the transmission. You can go to the next screen, clean the fields of the current screen or go to one level above. Depending upon the selection you do the processing goes to one of two screens:<br />
<br />
===Micro-Mainframe Transmission===<br />
<br />
In this screen, when you click the first button on the top of the screen, you will receive a dialog for select the file(s) you want to transmit. You can click this button more than once and select another files in another directories. You can select files in the list and exclude them with enter or space keys. When you click the “Confirma” button the program shows a dialog where you must inform the name of the file where you want the program to write the output of its processing(it will be the command that the emulator will use). You can clean the screen via “Limpa” button or exit and go to one level above via “Sair” button.<br />
<br />
===MainFrame-Micro Transmission===<br />
<br />
In this screen you'll inform a name of a text file that will contain the list of file names that you want to download from the mainframe(these files must be recorded in one of the data sets mentioned in the second screen above). Next you will inform the file extension the downloaded files must be recorded in the destination hard disk. Next you will inform the directory where the downloaded files will be written in the destination hard disk. And, finally you will inform the path and the name of the file where the command to the emulator will be written. When you click “executa” the command is generated and written in the file you have informed early in this screen. You can clean the screen via “Limpa” button or exit and go to one level above via “Sair” button.<br />
<br />
If some people really need the screens of this program translated to english I can do this. But only if you'll really use it, please.<br />
<br />
==Description==<br />
These tool permits us a huge work economy in massive transmitions to and from the IBM mainframes we use. I work in a company that make migration from old databases to IBM DB2.<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=SysRec&diff=18859SysRec2007-05-11T09:22:40Z<p>Dejvid: </p>
<hr />
<div>===About===<br />
* The application currently supports all Windows Operating Systems using the [[Glossary#VFW|VFW]] [[Glossary#API|API]] available on all 32 bit [[Glossary#Windows|Windows]].<br />
* Supports Preview and Overlay modes with any capture device such as Cameras and TV Tuners, though it has been designed for use with a WebCam.<br />
* Allows silent movie capturing with any resolution and color depth, you can also chose a codec to record smaller files and a function has been added to allow grabbing single images, adding audio recording should not be a problem.<br />
* A caller application has been added to demonstrate message based Inter Process Comunication.<br />
* This application is designed with [[Glossary#Lazarus|Lazarus]] but a few files and directives have been added to allow compilation in [[Glossary#Delphi|Delphi]]<br />
* The application uses the [http://www.delphi-jedi.org Delphi-JEDI] [[Glossary#VFW|VFW]] unit licensed under MPL, however you need some small modifications if you want to download it again, search for "FPC" in the current unit to see them. You can download the unit [ftp://delphi-jedi.org/api/vfw.zip here].<br />
<br />
===License===<br />
* This application can be used under Lazarus modified LGPL and MPL, this allows making commercial projects that use this code but NO WARANTY, you are the only responsable if this code doesn't work or causes any problems to your computer.<br />
<br />
===Author===<br />
[[User:Lightning|Razvan Adrian Bogdan]]<br />
<br />
===Download===<br />
The application can be found on the [http://sourceforge.net/project/showfiles.php?group_id=92177 Lazarus CCR Files page].<br />
<br />
===Usage===<br />
To use the application simply open the .lpi files from your File Browser or using the Open Project option from the Lazarus IDE the press Run, the Main form should display and if you have a Capture Driver installed you should be able to see some images, in the Status Bar you will see if your device supports Overlay or just Software Preview, in Overlay mode the application use less CPU and with more dynamic rendering.<br />
<br><br />
You should also see some buttons: Source, Format, Quality, Connect, Record/Stop.<br />
<br><br />
The first 3 buttons open various dialogs wich are dependant on your configuration. <br />
<br><br />
The Source button allows selecting and configuring a video source wich is capable of using a WDM driver.<br />
<br><br />
The Format button allows selecting the resolution and number of colors for the captured images.<br />
<br><br />
The Quality button allows selecting and configuring a codec, this is dependant on your installed codecs.<br />
<br><br />
The Connect button is a workaround for a Windows bug wich does not detect Overlay/Preview modes properly when changing the video source using Select.<br />
<br><br />
The Record button switches from Record to Stop when clicked and starts recording, the files are saved in the same directory and have the start and stop time in their name and the .avi extension.<br />
<br><br />
===Description===<br />
'''If any of the links do not work ask MS why did they move the website again and why is it so sloooow :)'''<br><br><br />
How does it work ?<br><br />
* [[Glossary#VFW|VFW]] is one of the simplest parts of the [[Glossary#Windows|Windows]] [[Glossary#API|API]], this application simply creates a special window using [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capcreatecapturewindow.asp capCreateCaptureWindow], you can call various functions using the handle resulted by creating this window.<br />
* After creating the window you have to connect it to a valid driver, for newer devices this is the default WDM driver, use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capdriverconnect.asp capDriverConnect] to connect the Capture window to a capture driver.<br />
* You will have to find if the driver supports hardware acceleration or '''Overlay''', this is done by [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capdrivergetcaps.asp capDriverGetCaps], use the '''fHasOverlay''' member of the [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capdrivercaps_str.asp TCapDriverCaps] structure.<br />
* Next you have to decide if you want live preview from the capture device, to activate it use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capoverlay.asp capOverlay] or [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_cappreview.asp capPreview] depending on your driver capabilities, if you have overlay then set [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_cappreviewrate.asp capPreviewRate] to 0 otherwise set it to a resonable value, you might also want the image to be stretched to the window size, to do this use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_cappreviewscale.asp capPreviewScale], to stop the preview use '''capOverlay''' or '''capPreview''' (depending on '''fHasOverlay''' member) and pass '''false''' as the second parameter.<br />
* To record you should use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capfilesetcapturefile.asp capFileSetCaptureFile], [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capcapturesequence.asp capCaptureSequence] and [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capfilesaveas.asp capFileSaveAs] if you did not use '''capFileSetCaptureFile''', to stop recording you should use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capcapturestop.asp capCaptureStop].<br />
* To get a single frame use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capgrabframenostop.asp capGrabFrameNoStop] and [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capeditcopy.asp capEditCopy] or use the internal '''CapGrabFrame''' wich captures one frame as a '''TBitmap''' from the clipboard so you can process/save it later.<br />
* You will have to also disconnect the driver, to do so use [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_capdriverdisconnect.asp capDriverDisconnect].<br />
* Before you close the program you also need to free the window with [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/destroywindow.asp DestroyWindow]<br />
* How to use a hook and process messages from various system windows or dynamic messages obtained with [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/messagesandmessagequeues/messagesandmessagequeuesreference/messagesandmessagequeuesfunctions/registerwindowmessage.asp RegisterWindowMessage].<br />
<br />
[[category:Example programs]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Robot_-_The_Game&diff=18858Robot - The Game2007-05-11T09:17:40Z<p>Dejvid: /* Versions */ the</p>
<hr />
<div>===The Game===<br />
The idea came from an old DOS-game from TOM-productions (homepage with download (it's a nice game!)): http://www.tom-games.de/eng/download.htm). You control your body through a world consisting of 5*5 rooms consisting of 20*20 fields. Every field contains a special game-object; this can be a wall, a bad robot who wants to kill you (because of the robots, the game is named Robot), a door (for each door, you need a special key) and so on.<br />
<br />
===Screenshots===<br />
Inside of Robot 1.5 while playing:<br />
<br />
[[Image:robot1.5-shot1.png]]<br />
<br />
Startscreen of Robot 1.7:<br />
<br />
[[Image:robot1.7-shot1.png]]<br />
<br />
Editor-mode of Robot 1.7:<br />
<br />
[[Image:robot1.7-shot2.png]]<br />
<br />
===Code===<br />
The code was written to demonstrate the possibilities with a very small set of programming techniques. The code doesn't use OOP, it is completly procedural (but embedded into one little form). I started this project while I was showing Lazarus to a friend to show him how easy it is to program a little application. It is not very difficult to teach a little bit of procedural and imperative programming so that you can do a little bit stuff. Then I said, "I show you what you can do with this"...<br />
<br />
The code is well documented in english.<br />
<br />
===Control===<br />
Movement is controlled by the arrow-keys. You can pick up things lying around by simply going to them. They will be put into your knapsack. Select the thing in your knapsack you want to use with spacebar. You can use them by pressing enter. There are corrosive liquids filled in bottles lying around. You can remove walls with them (only the bright walls). To go thru a door you must have a key of the right color. You won't need to select the key - just having the right key in your knapsack is sufficient. There are 3 diamonds lying around somewhere. You need them to defeat the evil king. You have to put them near of the diamond-places (go there and select it in your knapsack). You can save the game with the clocks lying around. ...<br />
<br />
===Control of Editor===<br />
Go into the editor mode (menu). All game-objects are listed in your knapsack. Simply select an object and place it in the present room by left-clicking at the wanted position. You can also select the objects in the knapsack by clicking on them. With a right-click, you can remove an object. With Ctrl+Arrowkey, you can switch to other rooms. Don't forget to come back to the room where your body is, because else, you can't resume playing on exiting the editor mode.<br />
<br />
===Versions===<br />
The game itself is in german, that means, that all messages inside of the game (a little description and a few state messages) are german. Perhaps you can translate it? :)<br />
<br />
It should not disturb so much (I give you a description...) and the interessing part is also the source code.<br />
<br />
There are versions from 1.5 to 1.7 available at the moment. The code is extended and buxfixed a little bit in the newer version, but there is no real big difference. Version 1.7 contains an ingame-editor now and there is a new world since version 1.7 (feel free to design own worlds or perhaps a whole new game based on this engine; it should not be difficult).<br />
<br />
===Download===<br />
You can download the source and binaries for Windows, Linux (i386 and powerpc) of Robot 1.7 from [http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=148359&release_id=440138 Lazarus-CCR Sourceforge site].<br />
<br />
The homepage of the project is http://www.az2000.de/projects/robot2/?lang=en, where you can find additional informations.<br />
<br />
If you have questions, simply ask me.<br />
<br />
===License===<br />
It is under the LGPL.<br />
<br />
===Author===<br />
[[User:Albert|Albert Zeyer]]</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Talk:Multiplatform_Programming_Guide&diff=18848Talk:Multiplatform Programming Guide2007-05-10T08:15:55Z<p>Dejvid: /* Linux in Europe */</p>
<hr />
<div>==see also==<br />
<br />
See also these [[doc:rtl/sysutils/ | SysUtils]] functions: <br />
* [[doc:rtl/sysutils/getappconfigdir.html | GetAppConfigDir]]<br />
* [[doc:rtl/sysutils/getappconfigfile.html | GetAppConfigFile]]<br />
[[User:Vincent|Vincent]] 21:52, 12 Dec 2005 (CET)<br />
==Linux in Europe==<br />
For example, on Europe, if you're developing desktop software you may decide that it only makes sense to develop for Windows and OS X. OS X is the most popular Unix in the world and is surging on the desktop, whereas Linux has mostly stalled there.<br />
Really? Living in both Britain and Croatia I would say that Linux is far more important than OS X. [[User:Dejvid|Dejvid]] 10:15, 10 May 2007 (CEST)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Talk:Multiplatform_Programming_Guide&diff=18847Talk:Multiplatform Programming Guide2007-05-10T08:15:38Z<p>Dejvid: </p>
<hr />
<div>==see also==<br />
<br />
See also these [[doc:rtl/sysutils/ | SysUtils]] functions: <br />
* [[doc:rtl/sysutils/getappconfigdir.html | GetAppConfigDir]]<br />
* [[doc:rtl/sysutils/getappconfigfile.html | GetAppConfigFile]]<br />
[[User:Vincent|Vincent]] 21:52, 12 Dec 2005 (CET)<br />
==Linux in Europe==<br />
For example, on Europe, if you're developing desktop software you may decide that it only makes sense to develop for Windows and OS X. OS X is the most popular Unix in the world and is surging on the desktop, whereas Linux has mostly stalled there.<br />
Really. Living in both Britain and Croatia I would say that Linux is far more important than OS X. [[User:Dejvid|Dejvid]] 10:15, 10 May 2007 (CEST)</div>Dejvidhttps://wiki.freepascal.org/index.php?title=Grids_Reference_Page&diff=18843Grids Reference Page2007-05-09T23:22:11Z<p>Dejvid: /* Customizing grids look */ lecture > reading off</p>
<hr />
<div>{{Grids Reference Page}}<br />
<br />
== Objective ==<br />
This text will try to show the user some aspects of the grids components in lazarus. Also is intended to serve as a guide for users who never used grids before (as experienced users generally only need a reference for new functionality).<br />
So this text will try to reach the following objectives:<br />
# To introduce the grids components to people with little or not previous delphi contact.<br />
# To document the differences with respect to delphi grids components.<br />
# To document the new functionality in lazarus grids.<br />
# Create reference and examples for the components.<br />
<br />
== Overview ==<br />
A grid is a component that provides a mean for display data in tabular format. The most obvious characteristic of grids is that they are composed of cells forming rows and columns.<br />
<br />
The type of information that can be shown in a grid is very ample, mainly depends on what the user wants to show, generally this information is translated in text, colors, images or one combination of them. <br />
<br />
Given the great variety of information that can be represented, a series of grids exits whose purpose is to facilitate to the user showing a specific kind of information.<br />
== Inheritence Tree ==<br />
<pre><br />
[TCustomControl] <br />
| <br />
| <br />
TCustomGrid <br />
| <br />
+-------------+------------+ <br />
| | <br />
TCustomDrawGrid TCustomDbGrid<br />
| | <br />
+--------+--------+ | <br />
| | TDbGrid <br />
TDrawGrid TCustomStringGrid <br />
| <br />
| <br />
TStringGrid <br />
</pre><br />
<br />
== A Starting Example ==<br />
As one of the objectives of this is to be nice with people with little or no previous lazarus knowledge lets do a quick starting example of grids in action. Why not, lets make a traditional "hello world" example using the TStringGrid Component.<br />
#Create a new application. <br />
#*From the main menu select: project->New Project<br />
#*In the Create New Project dialog press the "Create Button"<br />
#*A new empty form will be shown.<br />
#Place a grid on the form<br />
#*From the component palette select the "additional" tab<br />
#*Click over the TStringGrid icon []<br />
#*Click over the form, near to the top left corner. A new empty grid appears.<br />
#Place a button on the form<br />
#*From the component palette select the "Standard" tab<br />
#*Click over the TButton icon []<br />
#*Click over a empty area of the form. A new button appears.<br />
#Doubleclick the button from the step 3, and write down the following code in the click button handler: <br />
#*Stringgrid1.Cells[1,1] := 'Hi World!';<br />
#Run the program by clicking the play icon []<br />
#*by pressing the button1, the hello world text should appear in cell column 1, row 1.<br />
== Differences between Lazarus and Delphi grids ==<br />
The current grids components present several differences with respect to the delphi's grids. This is mainly because the lazarus grids were created from scratch primarily without trying to make them fully compatible. <br />
<br />
At a later stage, compatibility with delphi's grids became a desired objective and the grids starting to conform more closely the delphi grid's interface, but even then this was done without a strong effort to make every single property or method match it's delphi counterpart.<br />
Also, because the grid's internals are very different some things are not possible or need to be done in a different way in the lazarus' grids.<br />
As the grids have evolved, greater compatibility has been achieved and this is desireable. <br />
=== Differences ===<br />
Here the known differences will be listed, in no special order.<br />
*Cell Editors <br />
*Designtime Behaviour<br />
=== New Functionality ===<br />
*Columns<br />
*Events<br />
*Grid Editor<br />
=== Adjustments for improving Delphi grids compatibility ===<br />
Here is a list of properties and adjustments that can be made in order to make Lazarus grids look or behave similar to Delphi grids. These adjustments are based in a newly created grid. Entries tagged with [Code] need to be set in code, [Design] entries can be changed at design time.<br />
<br />
*[Design] TitleStyle:=tsStandard;<br />
*[Design] DefaultRowHeight:=24; <br />
*[Code] EditorBorderStyle:=bsNone; // this might work only on windows.<br />
*[Code] UseXORFatures:=true;<br />
*[Code] AllowOutBoundClicks:=False; (r10992 or later)<br />
*[Code] FastEditing:=False; (supported in dbgrid. StringGrid req. r10992 or later) <br />
*[Design] AutoAdvance:=aaNone;<br />
<br />
== Grids Reference ==<br />
=== Information ===<br />
The starting point for reference about TCustomGrid, TDrawGrid, TCustomDrawGrid, TCustomStringGrid and TStringGrid is the [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference] <br />
<br />
For TCustomDBGrid and TDBgrid is the <br />
[http://lazarus-ccr.sourceforge.net/docs/lcl/dbgrids/index.html unit dbgrids.pas reference]<br />
<br />
Currently there are not too much content in these links, due partly, to the lack of a tool that allows easily maintaining its content, but such tool is under construction and eventually the gaps will be filled.<br />
<br />
In general, any Delphi referece about the grids should help to use Lazarus grids (don't forget that there are several differences between Delphi and Lazarus grids being documented), with this in mind and as a temporal place for reference information, this place will be used to document things that doesn't work the same as in Delphi or that is new functionality.<br />
<br />
[TODO: the rest of this section will dissapear, it's content will be moved to [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/index.html unit grids.pas reference]<br />
<br />
=== TCustomGrid ===<br />
See the full [[doc:lcl/grids/tcustomgrid.html|TCustomGrid Reference]]<br />
==== property OnBeforeSelection: TOnSelectEvent ====<br />
==== property OnCompareCells: TOnCompareCells ====<br />
==== property OnPrepareCanvas: TOnPrepareCanvasEvent ====<br />
==== property OnDrawCell: TOnDrawCell ====<br />
==== property OnEditButtonClick: TNotifyEvent ====<br />
==== property OnSelection: TOnSelectEvent ====<br />
==== property OnSelectEditor: TSelectEditorEvent ====<br />
==== property OnTopLeftChanged: TNotifyEvent ====<br />
==== procedure AutoAdjustColumns; ====<br />
==== procedure BeginUpdate; ====<br />
==== procedure Clear; ====<br />
==== procedure DeleteColRow(IsColumn: Boolean; index: Integer); ====<br />
==== function EditorByStyle(Style: TColumnButtonStyle): TWinControl; ====<br />
==== procedure EndUpdate(UO: TUpdateOption); overload; ====<br />
==== procedure EndUpdate(FullUpdate: Boolean); overload; ====<br />
==== procedure EndUpdate; overload; ====<br />
==== procedure ExchangeColRow(IsColumn: Boolean; index, WithIndex:Integer); ====<br />
==== function IscellSelected(aCol,aRow: Integer): Boolean; ====<br />
==== function IscellVisible(aCol, aRow: Integer): Boolean; ====<br />
==== procedure LoadFromFile(FileName: string); ====<br />
==== function MouseToCell(Mouse: TPoint): TPoint; overload; ====<br />
==== function MouseToLogcell(Mouse: TPoint): TPoint; ====<br />
==== function MouseToGridZone(X,Y: Integer): TGridZone; ====<br />
==== function CellToGridZone(aCol,aRow: Integer): TGridZone; ====<br />
==== procedure MoveColRow(IsColumn: Boolean; FromIndex, ToIndex: Integer); ====<br />
==== procedure SaveToFile(FileName: string); ====<br />
==== procedure SortColRow(IsColumn: Boolean; index:Integer); overload; ====<br />
==== procedure SortColRow(IsColumn: Boolean; index,FromIndex,ToIndex: Integer); overload; ====<br />
<br />
=== TCustomStringGrid ===<br />
TCustomStringGrid serves as the base for TStringGrid. It can be used for derived TStringGrid components that want to hide published properties. See [[new intermediate grids]] for more information.<br />
<br />
These properties or methods are public and are also available to TStringGrid.<br />
<br />
See the full [http://lazarus-ccr.sourceforge.net/docs/lcl/grids/tcustomstringgrid.html TCustomStringGrid Reference]<br />
==== procedure AutoSizeColumn(aCol: Integer); ====<br />
This procedure sets the column width to the size of the widest text it finds in all rows for the column aCol. Tip: see the goDblClickAutoSize option to allow columns to be automatically resized when doubleClicking the column border.<br />
<br />
==== procedure AutoSizeColumns; ====<br />
Automatically resizes all columns by adjusting them to fit in the longest text in each column. This is a quick method of applying AutoSizeColumn() for every column in the grid.<br />
==== procedure Clean; overload; ====<br />
Cleans all cells in the grid, fixed or not.<br />
==== procedure Clean(CleanOptions: TCleanOptions); overload; ====<br />
Cleans all cells in the grid subject to the given CleanOptions. see [[TCleanOptions]] for more information. Some examples:<br />
*Clean all cells: grid.Clean([]); (the same as grid.clean)<br />
*Clean all non fixed cells: grid.Clean([gzNormal]);<br />
*Clean all cells but don't touch grid column headers: Grid.Clean[gzNormal, gzFixedRows]);<br />
==== procedure Clean(StartCol,StartRow,EndCol,EndRow: integer; CleanOptions:TCleanOptions); overload; ====<br />
does the same as Clean(CleanOptions:TCleanOptions) but restricted to the given StartCol,StartRow,EndCol and EndRow. Examples:<br />
*Clean column index 4 to 6 but don't touch grid column headers: many variations, Grid.Clean(4,Grid.FixedRows,6,Grid.RowCount-1,[]); Grid.Clean(4,0,6,Grid,RowCount-1, [gzNormal]); etc.<br />
==== procedure Clean(aRect: TRect; CleanOptions: TCleanOptions); overload; ====<br />
The same as Clean(StartCol,StartRow,EndCol,EndRow, CleanOptions), just taking a TRect instead of individual cell coordinates. Useful to clean the selection: grid.Clean(Grid.Selection,[]);<br />
==== property Cols[index: Integer]: TStrings read GetCols write SetCols; ====<br />
Get/set a list of strings from/to the given grid's column index starting from row index 0 to RowCount-1. <br />
===== Examples =====<br />
*Set Example: Set the content of the third column in the grid from a ListBox:<br />
Grid.Cols[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's column index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Cols[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the column content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
See the Get Example.<br />
<br />
==== property Rows[index: Integer]: TStrings read GetRows write SetRows; ====<br />
Get/set a list of strings from/to the given grid's row index starting from column index 0 to column ColCount-1. <br />
===== Notes. =====<br />
This property works in a different way in Lazarus than in Delphi when getting the data from the grid. <br />
In Lazarus a temporary TStringList object is created for retrieving the row content. It is the responsibility of the user to free this object after use. <br />
<br />
This means also that changes in the returned list will not affect the grids content or layout. <br />
<br />
===== Examples =====<br />
*Set Example: Set the content of the third row in the grid from a ListBox:<br />
Grid.Rows[2] := ListBox1.Items;<br />
*Get Example: Set the content of a Listbox from the grid's row index 4:<br />
procedure TForm1.FillListBox1;<br />
var <br />
StrTempList: TStringList;<br />
begin<br />
StrTempList := TStringList(Grid.Rows[4]);<br />
if StrTempList<>nil then begin<br />
ListBox1.Items.Assign(StrTempList);<br />
StrTempList.Free;<br />
end;<br />
end; <br />
*Not Working Example and Fix: Retrieved string list is read only<br />
// this will not work and will cause memory leak<br />
// because returned StringList is not being freed<br />
Grid.Rows[1].CommaText := '1,2,3,4,5';<br />
Grid.Rows[2].Text := 'a'+#13#10+'s'+#13#10+'d'+#13#10+'f'+#13#10+'g'; <br />
<br />
// fixing the first case<br />
Lst:=TStringList.Create;<br />
Lst.CommaText := '1,2,3,4,5';<br />
Grid.Rows[1] := Lst;<br />
Lst.Free;<br />
<br />
==== property UseXORFeatures; ====<br />
Boolean property, default value: false;<br />
<br />
This property controls how the dotted focus rectangle appears in the grid. When true, the rectangle is painted using the XOR raster operation. This allow us to see the focus rectangle no matter what the cells' background color is. When false, the user can control the color of the dotted focus rectangle using the [[FocusColor property]]<br />
<br />
It also controls the look of the column/row resizing. When true, a line shows visually the size that the the column or row will have if the user ends the operation. When false, the column or row resizing takes effect just as the user drags the mouse.<br />
<br />
== Grids Howto ==<br />
=== Look and Feel ===<br />
(this is a collection of notes to write this section, is not the final format)<br />
<br />
==== Customizing grids look ====<br />
(some properties that will be described in this section, change format later)<br />
<br />
LOOK:<br />
<br />
By modifying some properties user can adapt the grids to look different than default.<br><br />
properties that can be changed at design time:<br />
<br />
AlternateColor: With this the user can change the background color appears on alternated rows. This is to allow easy reading off of grid rows data.<br><br />
Color: This sets the primary color used to draw non fixed cells background.<br><br />
FixedColor: This is the color used to draw fixed cells background.<br><br />
flat: this eliminates the 3d look of fixed cells.<br><br />
TitleFont: Font used to draw the text in fixed cells.<br><br />
TitleStyle: This property changes the 3D look of fixed cells, there are 3 settings: tsLazarus, this is the default look<br><br />
tsNative, this tries to set a look that is in concordance with current widgetset theme.<br><br />
tsStandard, this tries a more contrasted look, like delphi grids.<br><br />
<br />
at runtime we have many other possiblities:<br><br />
AltColorStartNormal: boolean, if true alternate color is always in the second row after fixed rows, the first row after fixed rows will be always color, if false default color is set to the first row as if there were no fixed rows.<br><br />
BorderColor: This sets the grid's border color used when Flat:=True and BorderStyle:=bsSingle;<br><br />
EditorBorderStyle: if set to bsNone under windows the cell editors will not have the border, like in delphi, set to bsSingle by default because the border can be theme specific in some widgetsets and to allow a uniform look.<br><br />
FocusColor: The color used to draw the current focused cell if UseXORFeatures is not set, by default this is clRed.<br><br />
FocusRectVisible: turns on/off the drawing of focused cell.<br><br />
GridLineColor: color of grid lines in non fixed area.<br><br />
GridLineStyle: Pen style used to draw lines in non fixed area, possible choices are: psSolid, psDash, psDot, psDashDot, psDashDotDot, psinsideFrame, psPattern,psClear. default is psSolid.<br><br />
SelectedColor: Color used to draw cell background on selected cells.<br><br />
UseXORFeatures: if set focus rect is draw using XOR mode so it should make visible the focus rect in any cell color background. it affects the look of moving column.<br><br />
<br />
Customizing look:<br><br />
(this information is <for customizing the grid without writing derived ones)<br><br />
<br />
DefaultDrawing: boolean. Normally the grids prepare the grid canvas using some properties according to the kind of cell is being painted. If user write an OnDrawCell event handler, DefaultDrawing if set also paints the cell background, if user is drawing fully the cell is better turn off this property so painting is not duplicated. In a StringGrid if DefaultDrawing is set it draws the text in each cell.<br><br />
<br />
OnDrawCell event: the user would want to write an event handler for this if needs to draw custom things in a cell, it could be text, a graphic a picture or anything else. If the grid doesn't to store the cell text in the grid but rather the text or content is in another structure it's probably better to use TDrawGrid for this. TStringGrid is specialized on handling Text on cells but also can be compleatly customized by writing an OnDrawCell event handler.<br><br />
if DefaultDrawing is true, the canvas brush, pen and font it's already prepared and even the cell background it's already filled so turn off this if you are drawing pictures or gradients in cell.<br><br />
if user needs to customize only certain cells in the grid (for example for a stringgrid) then (s)he could do special drawing on specific cells and for the rest, call grid.DefaultDrawCell() function which will do write cells normally.<br><br />
<br />
OnPrepareCanvas: Sometimes users needs to custom draw specific cells with simply things like a different cell color, a different font or a different text layout in case of stringgrid, users doesn't have to write an OnDrawCell handler to do this there they need to actually draw the text or fill the background using canvas primitives, instead users could write an OnPrepareCanvas so they affect canvas properties like Font, brush, pen and TextStyle, this could be done for just for specific cells, the grid will reset this canvas properties for the other cells. So no need to anything.<br><br />
<br />
(this information is for writing derived grids.)<br><br />
<br />
Derivd grids usually have to override following methods:<br><br />
DrawAllRows: Draws all visible rows.<br><br />
DrawRow: Draw All Cells in a Row.<br><br />
DrawRow draws all cells in the row by first checking if cell is withing clipping region, and only draws the cell if it is.<br><br />
DrawCell:<br><br />
DrawCellGrid:<br><br />
DrawCellText:<br><br />
DrawFocusRect:<br><br />
(write me).<br><br />
<br />
==== Customizing grids use ====<br />
<br />
FEEL<br />
<br />
AutoAdvance: where the cell cursor will go when pressing enter or tab/shift tab, or after editing.<br><br />
ExtendedColSizing: if true user can resize columns not just at the headers but along the columns height.<br><br />
OnSelectCell: <br><br />
(write me)<br />
<br />
=== Operations ===<br />
==== Save and Retrieve Grid Content ====<br />
<br />
The '''SaveToFile''' procedure allows you save the TStringGrid format, attributes & values to a XML file.<br />
Previously you must set the SaveOptios property as follow:<br />
<br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
The '''LoadFromFile''' procedure allows you to load into a StringGrid instance, attributes, formats & values, from a '''XML''' file.<br />
First, you must set some of this options of the SaveOptios property (on your TStringGrid instance) [[SaveOptions]] <br />
soDesign: Save & Load ColCount,RowCount,FixedCols,FixedRows,<br />
ColWidths, RowHeights and Options (TCustomGrid)<br />
soPosition: Save & Load Scroll Position, Row, Col and Selection (TCustomGrid)<br />
soAttributes: Save & Load Colors, Text Alignment & Layout, etc. (TCustomDrawGrid)<br />
soContent: Save & Load Text (TCustomStringGrid)<br />
<br />
----<br />
<br />
'''Example:'''<br />
1) First Open a new project "Application".<br />
2) Put an empty TStringGrid.<br />
3) Put a TButton.<br />
4) Put a TOpenDialog <br />
5) Add the event OnCreate for the Form<br />
5) Add the event OnClick for the Button<br />
<pre><br />
unit Unit1; <br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, Grids,<br />
Buttons, StdCtrls, XMLCfg;<br />
<br />
type<br />
<br />
{ TForm1 }<br />
TForm1 = class(TForm)<br />
StringGrid1: TStringGrid;<br />
Button1: TButton;<br />
OpenDialog1: TOpenDialog;<br />
procedure Button1Click(Sender: TObject);<br />
procedure Form1Create(Sender: TObject);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
Form1: TForm1; <br />
<br />
implementation<br />
<br />
{ TForm1 }<br />
<br />
procedure TForm1.Form1Create(Sender: TObject);<br />
begin<br />
//sets the SaveOptions at creation time of the form <br />
stringgrid1.SaveOptions := [soDesign,soPosition,soAttributes,soContent];<br />
end;<br />
<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
begin<br />
//Ask if thew Execute method of the OpenDialog was launched <br />
//when this occurs, the user selects an XML file to Load<br />
//wich name was stored in the FileName prop.<br />
<br />
if opendialog1.Execute then<br />
Begin<br />
//Clear the grid <br />
StringGrid1.Clear;<br />
//Load the XML<br />
StringGrid1.LoadFromFile(OpenDialog1.FileName);<br />
//Refresh the Grid<br />
StringGrid1.Refresh;<br />
End;<br />
end;<br />
<br />
initialization<br />
{$I unit1.lrs}<br />
<br />
end.<br />
</pre><br />
----<br />
'''The sample xml file:'''<br />
(Copy the text below into a txt file Don't forget put the xml header :-))<br />
<pre><br />
''<?xml version="1.0"?><br />
<CONFIG><br />
<grid version="3"><br />
<saveoptions create="True" position="True" content="True"/><br />
<design columncount="2" rowcount="5" fixedcols="1" fixedrows="1" defaultcolwidth="64" defaultRowHeight="20"><br />
<options><br />
<goFixedVertLine value="True"/><br />
<goFixedHorzLine value="True"/><br />
<goVertLine value="True"/><br />
<goHorzLine value="True"/><br />
<goRangeSelect value="True"/><br />
<goDrawFocusSelected value="False"/><br />
<goRowSizing value="False"/><br />
<goColSizing value="False"/><br />
<goRowMoving value="False"/><br />
<goColMoving value="False"/><br />
<goEditing value="False"/><br />
<goTabs value="False"/><br />
<goRowSelect value="False"/><br />
<goAlwaysShowEditor value="False"/><br />
<goThumbTracking value="False"/><br />
<goColSpanning value="False"/><br />
<goRelaxedRowSelect value="False"/><br />
<goDblClickAutoSize value="False"/><br />
<goSmoothScroll value="True"/><br />
</options><br />
</design><br />
<position topleftcol="1" topleftrow="1" col="1" row="1"><br />
<selection left="1" top="1" right="1" bottom="1"/><br />
</position><br />
<content><br />
<cells cellcount="10"><br />
<cell1 column="0" row="0" text="Title Col1"/><br />
<cell2 column="0" row="1" text="value(1.1)"/><br />
<cell3 column="0" row="2" text="value(2.1)"/><br />
<cell4 column="0" row="3" text="value(3.1)"/><br />
<cell5 column="0" row="4" text="value(4.1)"/><br />
<cell6 column="1" row="0" text="Title Col2"/><br />
<cell7 column="1" row="1" text="value(1.2)"/><br />
<cell8 column="1" row="2" text="value(2.2)"/><br />
<cell9 column="1" row="3" text="value(3.2)"/><br />
<cell10 column="1" row="4" text="value(4.2)"/><br />
</cells><br />
</content><br />
</grid><br />
</CONFIG>''<br />
</pre><br />
----<br />
--[[User:Raditz|Raditz]] 21:06, 11 Jan 2006 (CET) from '''ARGENTINA'''<br />
<br />
=== Grid Cell Editors ===<br />
<br />
The grid uses cell editors to change the content of cells. For a specilized grid like TStringGrid, the editor is the habitual single line text editor control, sometimes it's desireable to have other means to enter information. For example, to call the open file dialog to find the location of a file so the user doesn't have to type the full path manually, if the text in the cell represents a date, it would be more friendly if we could popup a calendar so we can choose a specific date easily. Sometimes the information the user should enter in a cell is restricted to a limited list of words, in this case typing the information directly might introduce errors and validating routines might need to be implemented, we can avoid this by using a cell editor that present the user a list with only the legal values. This is also the case of generic grids like TDrawGrid where user have to have some kind of structure to hold the data that will be shown in the grid, in this situation, the information that is entered in the cell editor updates the internal structure to reflect the changes in the grid. <br />
<br />
==== Builtin cell editors ====<br />
<br />
The grids.pas unit already include some of the most used cell editors ready for use in grids, there is also the posibility to create new cell editors (custom cell editors) if the builtin editors are not appropiated for a specific tasks.<br />
<br />
The builtin cell editors are Button, Edit, and Picklist.<br />
<br />
==== Using cell editors ====<br />
<br />
Users can specify what editor will be used for a cell using one of two methods.<br />
#Using a custom column and selecting the ButtonStyle property of column. In this method the user can select the style of the editor that will be shown. Available values are: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn<br />
#Using OnSelectEditor grid event, here the user specify in the Editor parameter which editor to use for a cell identified for column aCol and row ARow in a TCustomDrawGrid derived grid or TColumn in TCustomDBGrid. For this purpose there is a useful public function of grids, EditorByStyle() that takes as parameter one of the following values: cbsAuto, cbsEllipsis, cbsNone, cbsPickList, cbsCheckboxColumn. This method takes precedence over the first one using custom columns. A Custom cell editor can be specified here. This event is also the place to setup the editor with values specific to the cell, row or column.<br />
<br />
==== Description of editor styles ====<br />
<br />
The following is the description of each editor style, they are enumeated values of type TColumnButtonStyle and so they are prefixed by 'cbs'. This type was used so it remains compatible with delphi's dbgrid.<br />
<br />
*'''cbsAuto'''<br />
:This is the default editor style for TCustomGrid derived grids. The actual editor class that will be used to edit the cell content, depends on several factors. For TCustomGrids it uses a TStringCelleditor class derived from TCustomMaskEdit, this editor is speciallized to edit single line strings. It is then used in TStringGrid and TDrawGrid by default. When using Custom Columns and programmer filled the Column's PickList property, this behaves as if cbsPickList editor style was set. For a TCustomDBGrid that have a field of type boolean, behaves as if cbsCheckBoxColumn editor style was specified. This is the recomended value for Custom Cell Editors TODO: related OnEditingDone.<br />
*'''cbsEllipsis'''<br />
:This editor style is the most generic one. When used, a button appears in the editing cell, programmers could use the OnEditButtonClick grid event to detect when user has pressed the button and take any action was programmed for such cell. For example a programmer could use this editor style to pop up a calendar dialog so user can easily pick a specific date, another could be to show a file open dialog to find files, a calculator so user can enter the numeric result of calcs, etc. <br />
<br />
:OnEditButtonClick is just a notification, so to find out which cell a button has been clicked, take a look at grid.Row and grid.Col properties.<br />
<br />
:A DbGrid has specific properties to retrieve the active column or field and because this event occurs in active record, it could update the information in active field.<br />
<br />
:This editor style is implemented using TButtonCellEditor a direct descendant of TButton.<br />
*'''cbsNone'''<br />
:This editor style instruct the grid to not use any editor for a specific cell or column, it behaves then, as if the grid is readonly for such cell or column.<br />
*'''cbsPickList'''<br />
:Used to present the user a list of values that can be entered, this editor style is implemented using TPickListCellEditor a component derived from TCustomComboBox. The list of values that are shown, are filled in two ways depending of the method used to select the editor style.<br />
:#When using custom columns, programmers can enter a list of values using the column's PickList property. [FOR BEGINNERS: TODO: exact procedure to edit the list]<br />
:#In OnSelectEditor, programmers get the TPickListCellEditor instance using the function EditorByStyle(cbsPickList), an example would be: var Lst:TPickListCellEditor; begin [...] Lst:=TPickListCellEditor(EditorByStyle(cbsPickList)); Lst.clear; Lst.Items.add('One');lst.items.add('Two'); Editor:=Lst; end;<br />
:The value in a TStringGrid grid will automatically reflect the value selected, if necessary the programmer could detect the moment the value is selected by writing an event handler for grid's OnPickListSelect event, so additional steps can be taken for example, to process the new value. TODO: related OnEditingDone.<br />
*'''cbsCheckboxColumn'''<br />
:This editor style is at the moment only available in TDbGrid. This editor style can be useful when field content associated with the column are restricted to a pair of values, for example, yes-no, true-false, on-off, 1-0, etc. Instead of forcing the user to type the values for this kind of field in a StringCellEditor or to choose one from a list, cbsCheckboxColumn is used to modify the field content of a column by using a checkbox representation that user can toggle by using a mouse click or pressing the SPACE key.<br />
<br />
:If columns' ButtonStyle property is set to cbsAuto and DbGrid detects that the field associated to the column is a boolean field, then the grid use this editor style automatically, this automatic selection can be disabled or enabled using dbgrid's OptionsExtra property, setting dgeCheckboxColumn element to false disable this feature.<br />
<br />
:The values that are used to recognize the checked or unchecked states are set in column's properties ValueChecked and ValueUnchecked.<br />
<br />
:In any moment, the field value can be in one to three states: Unchecked, checked or grayed. Internally this states are identified by the following values of type TDBGridCheckBoxState: gcbpUnChecked, gcbpChecked and gcbpGrayed.<br />
<br />
:This editor style doesn't use real TCheckbox components to handle user interaction, the visual representation is given by three builtin bitmap images that corresponds to the possible states of checkbox. The used bitmaps can be customized by writing a handler for DBGrid event OnUserCheckboxBitmap, the handler of this event gets the state of the checkbox in parameter CheckedState of type TDbGridCheckboxState and a bitmap parameter that programmer could use to specify custom bitmaps.<br />
<br />
====Example: How to set a custom cell editor====<br />
<br />
See lazarus/examples/gridcelleditor/gridcelleditor.lpi<br />
<br />
== Todo ==<br />
*TInplaceEditor Support<br />
=== known problems ===<br />
29-marzo-2005:<br />
*mouse autoedit<br />
** linux: clicking a selected cell doesn't trigger the autoedit function (the editor lost focus inmmediatelly)<br />
** windows: it should work only if the grid has the focus, if not it should focus and a second click should autoedit (cannot detect if the grid was previously focused or not)<br />
*ColumnWidths: linux, windows: dbgrid start with default column widths instead of calculated ones (it's disabled because early canvas creation causes strange effects if the parent is a notebook page)<br />
*Resizing Columns: linux, windows. Resizing a column should not hide the editor (specially in dbgrid if we are inserting)<br />
*MouseWheel:<br />
**linux: mousewheel doesn't work as it should, (seems it's calling the default mousewheel handler)<br />
**windows: patch was sent.<br />
*Double painting: Apparently dbgrid is painting twice the cell background (one with fillrect and one in textRect)<br />
*AutoFillColumns: sometimes the clientwidth is evaluated incorrectly (sometimes there are phantom scrollbars)</div>Dejvid