Mega Code Archive

 
Categories / Delphi / VCL
 

A Gardeners Guide To The Tree View Part II (Source Code)

Title: A Gardener's Guide To The Tree View: Part II (Source Code) Question: This is a supplement to the Part II tutorial on the TreeView. It provides the implementation section of the project attached to the original Part II, to help out users who may not have Delphi readily available, or are simply not interested in downloading from unknown sources. Answer: procedure TfrmTreeViewDemo2.tvwDemoChange(Sender: TObject; Node: TTreeNode); begin // When items are added or deleted from the TreeView, this event // fires. It enables or disables the panel containing TTreeNode // operations, because we don't have one selected to work with. // Syntactically, we could do an if..then statement here to set // the enabled property, but the common rules of regular // expressions allow us to set one datatype to another, no // matter the function or operation used. In this case, we're // comparing a boolean to a boolean value. If there is no item // selected, pnlTreeNode will be disabled. If there is a // selection, it will be enabled. pnlTreeNode.Enabled := not( tvwDemo.Selected = nil ); // I understand that for many readers, I am seriously overstating // the above concept. However, the implementation of a concept // can often come long before understanding it. For example, I // have used pointers in Delphi for a long time, and really // didn't know all that much about them. When you take the // time to bring a simple concept from familiarity to a deep // understanding, you will find that abstracts will find their // way into completely unrelated topics. Indeed, this simple // dissertation into a TreeView has caused me to look in new // ways at a component I have been familiar with for several // years. end; // ******************************************************************* // TTreeView methods and properties. // ******************************************************************* procedure TfrmTreeViewDemo2.chbTVHideSelectionClick(Sender: TObject); // The HideSelection property determines whether or not the selected // TreeNode will be highlighted when the TreeView control is not // focused. begin // There are a couple of ways to handle this OnClick event of a // TCheckBox component. Since the only component that is tied // to this procedure is a TCheckBox (chbTVHideSelection), we // could very well use the following code to set the TreeView // property: // // tvwDemo.HideSelection := chbTVHideSelection.Checked; // // However, as we become more complex in our interface, and in our // encapsulation, we will almost certainly use these sorts of // procedures that: // // A) Have more than one TCheckBox component associated with // it. // B) Have a component that is not a TCheckBox component // associated with it. // // Therefore, for consistency's sake, we'll design our generic // event procedures that way. // First, we check to see is the component that fired off this // procedure is of class TCheckBox or is a decendant of it. if ( Sender is TCheckBox ) then begin // If it is, then we will set the HideSelection property equal // the Checked property of Sender. We have to typecast // Sender as a TCheckBox to have access to the Checked // property. If we didn't, then the compiler would be // expecting that TObject had a Checked property. tvwDemo.HideSelection := ( TCheckBox( Sender ).Checked ); end; end; procedure TfrmTreeViewDemo2.chbTVHotTrackClick(Sender: TObject); // The HotTrack property determines whether or not the user will // receive visual feedback as to the item that his mouse is // hovering over. This will display the TreeNode that is being // hovered over in blue underlined text. begin // Refer to chbTVHideSelectionClick for an explanation of this // code. if ( Sender is TCheckBox ) then begin tvwDemo.HotTrack := ( TCheckBox( Sender ).Checked ); end; end; procedure TfrmTreeViewDemo2.chbTVReadOnlyClick(Sender: TObject); // The ReadOnly property prevents user editing of TreeNode captions. // If this property is true, then the user can press F2 with a // TreeNode selected to edit its caption. begin // Refer to chbTVHideSelectionClick for an explanation of this // code. if ( Sender is TCheckBox ) then begin tvwDemo.ReadOnly := ( TCheckBox( Sender ).Checked ); end; end; procedure TfrmTreeViewDemo2.chbTVShowLinesClick(Sender: TObject); // The ShowLines property determines whether or not connector lines // are displayed between TreeNodes and their children/siblings. begin // Refer to chbTVHideSelectionClick for an explanation of this // code. if ( Sender is TCheckBox ) then begin tvwDemo.ShowLines := ( TCheckBox( Sender ).Checked ); end; end; procedure TfrmTreeViewDemo2.chbTVShowRootClick(Sender: TObject); // The ShowRoot property determines whether or not connectors to the // root (top) TreeNode(s) are shown. begin // Refer to chbTVHideSelectionClick for an explanation of this // code. if ( Sender is TCheckBox ) then begin tvwDemo.ShowRoot := ( TCheckBox( Sender ).Checked ); end; end; procedure TfrmTreeViewDemo2.btnTVSetTopItemClick(Sender: TObject); // The TopItem property allows you to set the top node displayed in // the visible portion in the list. var // We will want to obtain a pointer to the current selected // TreeNode. SelectedNode: TTreeNode; begin // We will set the above pointer to the TreeNode specified by the // TTreeView.Selected property. Note that we do not have to // use the Create method of TTreeNode to perform this. This is // an important concept to understand. In Delphi, SelectedNode // is a pointer, and only a pointer. The Create method would // initialize a memory area according to the class structure of // TTreeNode, and would return a pointer to that area. In // this case, however, the TreeNode already exists in memory, // and is owned by the TreeView's TreeNodes collection. SelectedNode := tvwDemo.Selected; // We will evaluate SelectedNode. If it is equal to nil, then it // means that no item is selected in the TreeView. For // purposes of this tutorial, all of our operations that // require a TreeNode rely on an item being selected. if not( SelectedNode = nil ) then begin // If an item is selected, then we will set the TreeView's // TopItem property to it. tvwDemo.TopItem := SelectedNode; end; end; procedure TfrmTreeViewDemo2.btnTVFullExpandClick(Sender: TObject); // The FullExpand method of TTreeView expands all nodes in the // TreeView to show their children. begin tvwDemo.FullExpand; end; procedure TfrmTreeViewDemo2.btnTVFullCollapseClick(Sender: TObject); // The FullCollapse method of TTreeView collapses all nodes in the // TreeView. begin tvwDemo.FullCollapse; end; procedure TfrmTreeViewDemo2.btnTVLoadFromFileClick(Sender: TObject); // The TTreeView class, as well as several other classes in Delphi, // supports the ability to Load and Save from files or streams. // This affords for some excellent power, particularly in the area // of COM. For the time being, we will concern ourselves with the // File aspect. var // We will use the TOpenDialog class to allow the user to select a // file to load from. TOpenDialog will create a Windows // standard open dialog box, and if a file is selected by the // user will store the fully qualified filename for use in load // operations. OpenDialog: TOpenDialog; begin // We will create a new instance of TOpenDialog, and set the // OpenDialog variable as a pointer to it. We create this // object without an owner by specifying nil to the Create // procedure. This means that we will be responsible for // freeing the memory allocated ourselves. OpenDialog := TOpenDialog.Create( nil ); // We will place the following code inside a try..finally block. // The reason for this is that an exception for one reason or // another after creation of the object above would lead to a // memory leak. Essentially, the procedure would exit after // the exception, and our pointer refence would be lost. // By using a try..finally block, we ensure that no matter what // errors may occur, the memory block initialized as a // TOpenDialog will be cleared. try // All of the lines of code we are placing here perform some // sort of read or write on the OpenDialog object. Instead // of using an object reference over and over, as in the // following example: // // OpenDialog.Method1; // OpenDialog.Method2; // OpenDialog.Property1 := True; // // We can instead wrap all of the statements in... // // with ( object ) do // // ...for all code between begin and end for this with // statement, the specified object is assumed. with ( OpenDialog ) do begin // The DefaultExt property of the TOpenDialog class // specifies the extension that the file will be loaded // with if none is specified. In this case it isn't // completely necessary since we are only going to allow // files of type RTV to load. It is good practice, // however, and most likely you would provide a user with // the ability to choose "All Files (*.*)" as a load // mask. Without DefaultExt specified, the OpenDialog // could be unable to determine a file to load. For // example, assume that you have file1.rtv and file1.txt // in the directory, and while viewing all files you // type "file1" into the filename box. Without a // DefaultExt property, the OpenDialog would be unsure of // which of the two files you intended to load. DefaultExt := 'rtv'; // The Title property of the TOpenDialog class specifies the // dialog caption. Title := 'Choose an RTV file to load.'; // The Filter property of the TOpenDialog class specifies // the items that will appear in the "Files of Type" // section of the OpenDialog. Each entry has two parts, // and both entries and parts are delimited with the pipe // ( | ) character. The first part of an entry is the // text that the user will see in the combo box, and the // second part is the internal file mask that is // associated with it. Filter := 'RCS TreeView files (*.rtv)|*.rtv'; // Check the return value of the Execute function of the // TObjectClass. Execute will display the dialog to the // user with all specified properties, and will return // true if a file was selected, or false if the operation // was cancelled. if ( Execute ) then begin // If a file was selected, then we will use the // TreeView's LoadFromFile method, passing it // OpenDialog.Filename as the sole parameter. The // TreeView will automatically clear and re-populate // itself with the contents of the file if it is a // valid TreeView. If it is not, an exception will be // generated. We'll handle the user interface side of // this exception handling in a future tutorial. tvwDemo.LoadFromFile( Filename ) end; end; finally // Whether the code inside Try completes successfully or an // exception is generated, the code inside Finally will // always execute. In this case, we ensure that the memory // block initialized for our OpenDialog is freed. OpenDialog.Free; end; end; procedure TfrmTreeViewDemo2.btnTVSaveToFileClick(Sender: TObject); // This is very much the same process as the LoadFromFile method, // save that the filename will be written to instead of read from, // with the contents of the TreeView. var // We will obtain a pointer to a SaveDialog object. TSaveDialog // is quite similar in operation to TOpenDialog. SaveDialog: TSaveDialog; begin // Refer to the btnTVLoadFromFileClick procedure for an // explanation of this code. SaveDialog := TSaveDialog.Create( nil ); try SaveDialog.DefaultExt := 'rtv'; SaveDialog.Title := 'Choose an RTV file to save to.'; SaveDialog.Filter := 'RCS TreeView files (*.rtv)|*.rtv'; if ( SaveDialog.Execute ) then begin tvwDemo.SaveToFile( SaveDialog.Filename ) end; finally SaveDialog.Free; end; end; procedure TfrmTreeViewDemo2.chbTVAutoExpandClick(Sender: TObject); // The AutoExpand property of the TTreeView class determines whether // or not unselected items will be automatically collapsed and // expanded as they are selected. begin // Refer to chbTVHideSelectionClick for an explanation of this // code. if ( Sender is TCheckBox ) then begin tvwDemo.AutoExpand := chbTVAutoExpand.Checked; end; end; procedure TfrmTreeViewDemo2.btnTVAlphaSortClick(Sender: TObject); // The AlphaSort method sorts all TreeNodes within their parents // alphabetically based on their caption. begin tvwDemo.AlphaSort; end; // ******************************************************************* // TTreeNodes methods and properties. // ********************************************************************** procedure TfrmTreeViewDemo2.btnTNSAddClick(Sender: TObject); // The Add method of the TTreeNodes class will add a TreeNode as the // next sibling of the specified node, or to the root if nil is // specified. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; // The first parameter is the node that our new TreeNode will be // created as a sibling (same-level) of. The second parameter // is the caption of the new TreeNode. Add returns a pointer // to the newly-created TreeNode, but we don't have a use for // it for the purposes of this tutorial. tvwDemo.Items.Add( SelectedNode, edtNodeCaption.Text ); end; procedure TfrmTreeViewDemo2.btnTNSAddFirstClick(Sender: TObject); // The AddFirst method of the TTreeNodes class will add a TreeNode as // the first sibling of the specified node, or to the root if nil // is specified. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; // The parameters for all add methods are conceptually the same. // Refer to the detailed explanation in btnTNSAddClick for an // explanation. tvwDemo.Items.AddFirst( SelectedNode, edtNodeCaption.Text ); end; procedure TfrmTreeViewDemo2.btnTNSAddChildClick(Sender: TObject); // The AddChild method of the TTreeNodes class will add a TreeNode as // the next child of the specified node, or to the root if nil is // specified. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; // The parameters for all add methods are conceptually the same. // Refer to the detailed explanation in btnTNSAddClick for an // explanation. tvwDemo.Items.AddChild( SelectedNode, edtNodeCaption.Text ); end; procedure TfrmTreeViewDemo2.btnTNSAddChildFirstClick(Sender: TObject); // The AddChild method of the TTreeNodes class will add a TreeNode as // the first child of the specified node, or to the root if nil is // specified. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; // The parameters for all add methods are conceptually the same. // Refer to the detailed explanation in btnTNSAddClick for an // explanation. tvwDemo.Items.AddChildFirst( SelectedNode, edtNodeCaption.Text ); end; procedure TfrmTreeViewDemo2.btnTNSGetCountClick(Sender: TObject); // The Count property of the TTreeNodes class returns the total // number of TreeNodes in the TreeView. begin // We will set the labels caption to the string value of the Count // property. lblTNSGetCount.Caption := IntToStr( tvwDemo.Items.Count ); end; procedure TfrmTreeViewDemo2.btnTNSItemsClick(Sender: TObject); // The Items property, like any array, can be indexed by number and // will return a pointer to the stored object. To demostrate use // of the Items array property, we will allow the user to enter an // index number and select the associated TreeNode. begin try tvwDemo.Selected := ( tvwDemo.Items[ StrToInt( edtTNSItems.Text ) ] ); except // If either the specified index is out of range, or is not a // valid number an exception will occur. For now, we'll // present a generic error message to the user reflecting // this fact. ShowMessage( 'You must enter a valid number.' ); // Next, we will set focus back to the edit control where the // index value can be specified. edtTNSItems.SetFocus; // Finally, we'll select all of the text inside the edit // control. This way, the user can begin typing in the new // value without deleting the old one. edtTNSItems.SelectAll; end; end; // ******************************************************************* // TTreeNode methods and properties. // ******************************************************************* procedure TfrmTreeViewDemo2.btnTNAlphaSortClick(Sender: TObject); // The AlphaSort method of a TTreeNode differs slightly from the // AlphaSort method of the TTreeView. In this case, the method // will alphabetically sort all decendants of the specified node. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; SelectedNode.AlphaSort; end; procedure TfrmTreeViewDemo2.btnTNCollapseClick(Sender: TObject); // The Collapse method of a TTreeNode will collapse all children of // the specified node. It takes a single parameter of type // boolean. If this parameter is true, then all decendants of the // specified TreeNode will be collapsed as well. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; SelectedNode.Collapse( chbTVExpandCollapseRecurse.Checked ); end; procedure TfrmTreeViewDemo2.btnTNExpandClick(Sender: TObject); // The Expand method of a TTreeNode functions the same as the // Collapse method, save that it performs the opposite operation // on the TreeNode's decendants. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; SelectedNode.Expand( chbTVExpandCollapseRecurse.Checked ); end; procedure TfrmTreeViewDemo2.btnTNGetFirstChildClick(Sender: TObject); // The GetFirstChild method of a TTreeNode will return a pointer to // the it's first child, if one exists. If not, it will return // nil. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; tvwDemo.Selected := SelectedNode.GetFirstChild; end; procedure TfrmTreeViewDemo2.btnTNGetNextSiblingClick(Sender: TObject); // The GetFirstChild method of a TTreeNode will return a pointer to // the next TreeNode item on the same level, if one exists. If // not, it will return nil. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; tvwDemo.Selected := SelectedNode.GetNextSibling; end; procedure TfrmTreeViewDemo2.btnTNGetNextChildClick(Sender: TObject); // The GetNextChild method of a TTreeNode will return a pointer to the // next child of the Node's parent. This makes the method // essentially identical to GetNextSibling. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; tvwDemo.Selected := SelectedNode.GetNextChild( SelectedNode ); end; procedure TfrmTreeViewDemo2.btnTNGetNextClick(Sender: TObject); // The GetNext method of a TTreeNode will return a pointer to the // next node in the TreeView, be it a child, sibling, or // otherwise. Essentially, it will take the index of the current // TreeNode in the TreeNodes collection, and return the next index. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; tvwDemo.Selected := SelectedNode.GetNext; end; procedure TfrmTreeViewDemo2.btnTNGetNextVisibleClick(Sender: TObject); // The GetNextVisible method of a TTreeNode will return a pointer to // the next node that is visible to the user. For example, if a // node is collapsed, then it will return a pointer to it's next // sibling. If it is expanded, it will return a pointer to its // first child. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; tvwDemo.Selected := SelectedNode.GetNextVisible; end; procedure TfrmTreeViewDemo2.btnTNDeleteClick(Sender: TObject); // The Delete method of a TTreeNode will delete it and all decendants. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; SelectedNode.Delete; end; procedure TfrmTreeViewDemo2.btnTNDeleteChildrenClick(Sender: TObject); // The Delete method of a TTreeNode will delete its decendants, but // leave the source node intact. var SelectedNode: TTreeNode; begin SelectedNode := tvwDemo.Selected; SelectedNode.Delete; end;