TTreeView: what changed
Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | - | ||
| 1 | + | A **TTreeView** [](</File:ttreeview.png>) is a graphical control element that presents a hierarchical view of information. Each item can have a number of subitems. | |
| 2 | 2 | ||
| 3 | - | ||
| 3 | + | An item can be expanded to reveal subitems, if any exist, and collapsed to hide subitems. | |
| 4 | 4 | ||
| 5 | 5 | ## A Short example of using TTreeview | |
| 6 | 6 | ||
| 7 | - | ||
| 7 | + | Here is a simple example: | |
| 8 | 8 | ||
| 9 | - | ||
| 10 | - | ||
| 11 | - | ||
| 9 | + | * Create a new application. | |
| 10 | + | * Add an empty treeview to the form, a button1 with caption "Add Child" and a button2 with caption "Delete" | |
| 11 | + | * Assign the following code to the buttons' OnClick events. Compile and run. | |
| 12 | 12 | ||
| 13 | - | ||
| 13 | + | ``` | |
| 14 | 14 | procedure TForm1.Button1Click(Sender: TObject); | |
| 15 | - | ||
| 16 | - | ||
| 17 | - | ||
| 18 | - | ||
| 19 | - | ||
| 20 | - | ||
| 21 | - | ||
| 22 | - | ||
| 23 | - | ||
| 24 | - | ||
| 25 | - | ||
| 26 | - | ||
| 27 | - | ||
| 28 | - | ||
| 29 | - | ||
| 30 | - | ||
| 31 | - | ||
| 32 | - | ||
| 33 | - | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - | ||
| 37 | - | ||
| 38 | - | ||
| 39 | - | ||
| 40 | - | ||
| 41 | - | ||
| 42 | - | ||
| 43 | - | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - | ||
| 47 | - | ||
| 48 | - | ||
| 49 | - | ||
| 50 | - | ||
| 51 | - | ||
| 52 | - | ||
| 53 | - | ||
| 54 | - | ||
| 15 | + | var | |
| 16 | + | n: integer; | |
| 17 | + | s: string; | |
| 18 | + | begin | |
| 19 | + | // If there are no nodes, create a root node with a parent of nil | |
| 20 | + | if TreeView1.Items.Count = 0 then | |
| 21 | + | begin | |
| 22 | + | Treeview1.Items.Add(nil,'Root Node'); | |
| 23 | + | exit; | |
| 24 | + | end; | |
| 25 | + | ||
| 26 | + | // Set up a simple text for each new node - "Node1", "Node2" etc | |
| 27 | + | n := Treeview1.Items.Count; | |
| 28 | + | s := 'Node ' + IntToStr(n); | |
| 29 | + | ||
| 30 | + | // Add a new node to the currently selected node | |
| 31 | + | if TreeView1.Selected <> nil then | |
| 32 | + | Treeview1.Items.AddChild(Treeview1.Selected, s); | |
| 33 | + | end; | |
| 34 | + | ||
| 35 | + | procedure TForm1.Button2Click(Sender: TObject); | |
| 36 | + | ||
| 37 | + | // A procedure to recursively delete nodes | |
| 38 | + | procedure DeleteNode(ANode: TTreeNode); | |
| 39 | + | begin | |
| 40 | + | while ANode.HasChildren do | |
| 41 | + | DeleteNode(ANode.GetLastChild); | |
| 42 | + | TreeView1.Items.Delete(ANode); | |
| 43 | + | end; | |
| 44 | + | ||
| 45 | + | begin | |
| 46 | + | if TreeView1.Selected = nil then | |
| 47 | + | exit; | |
| 48 | + | ||
| 49 | + | // If the selected node has child nodes, first ask for confirmation | |
| 50 | + | if Treeview1.Selected.HasChildren then | |
| 51 | + | if MessageDlg('Delete node and all children?', mtConfirmation, [mbYes,mbNo], 0 ) <> mrYes then | |
| 52 | + | exit; | |
| 53 | + | DeleteNode(TreeView1.Selected); | |
| 54 | + | end; | |
| 55 | 55 | ``` | |
| 56 | 56 | ||
| 57 | - | ||
| 57 | + | When running, the treeview is empty. If you click "Add Child", a root node is created. After that a child will be added to any selected node by clicking "Add Child" again. | |
| 58 | 58 | ||
| 59 | - | ||
| 59 | + | The "Delete" button will delete the currently selected node. If it doesn't have children, it will delete it immediately, but if it has children, it will first ask. | |
| 60 | 60 | ||
| 61 | 61 | ## Adding a new item in code | |
| 62 | 62 | ||
| 63 | - | ||
| 64 | - | ||
| 65 | - | ||
| 66 | - | ||
| 63 | + | To add a sibling node to pre-existing node, use TTreeView.ITems.Add. | |
| 67 | 64 | ``` | |
| 68 | - | ||
| 69 | - | ||
| 70 | - | ||
| 71 | - | ||
| 72 | - | ||
| 65 | + | TreeView1.Items.Add(ATreeNode, 'Added node'); | |
| 73 | 66 | ``` | |
| 74 | 67 | ||
| 75 | - | ||
| 68 | + | To add the first node, it becomes: | |
| 69 | + | ``` | |
| 70 | + | TreeView1.Items.Add(nil, 'First node'); | |
| 71 | + | ``` | |
| 76 | 72 | ||
| 77 | - | ||
| 73 | + | Use TTreeView.Items.AddChild or AddChildObject to add child node. | |
| 74 | + | ``` | |
| 78 | 75 | TreeView1.Items.AddChild(ATreeNode, 'Child node'); | |
| 79 | 76 | ``` | |
| 80 | 77 | ||
| 81 | 78 | ## Creating a TreeView which loads items only when expanding | |
| 82 | 79 | ||
| 83 | - | ||
| 84 | - | ||
| 85 | - | ||
| 80 | + | To add the expansion symbol to a node without subitems use: | |
| 81 | + | ``` | |
| 86 | 82 | MyNode.HasChildren := True; | |
| 87 | 83 | ``` | |
| 88 | 84 | ||
| 89 | - | ||
| 85 | + | And then set an event handler for the OnExpanding event. In this even you should return if the expansion can actually be made or not and when yes, you should add subitems to the node. If the expansion cannot be done, the expansion symbol will be automatically removed even if you have previously set HasChildren to true. | |
| 90 | 86 | ||
| 91 | 87 | ## How to move a node in TTreeview | |
| 92 | 88 | ||
| 93 | - | ||
| 94 | - | ||
| 95 | - | ||
| 89 | + | Assuming you have UP and Down buttons to move the selected node in the TTreeview, these procedures will achieve moving the selected node. | |
| 90 | + | ``` | |
| 96 | 91 | procedure TForm1.UpBtnClick(Sender: TObject); | |
| 97 | - | ||
| 98 | - | ||
| 99 | - | ||
| 100 | - | ||
| 101 | - | ||
| 102 | - | ||
| 103 | - | ||
| 104 | - | ||
| 105 | - | ||
| 106 | - | ||
| 107 | - | ||
| 108 | - | ||
| 109 | - | ||
| 110 | - | ||
| 111 | - | ||
| 112 | - | ||
| 113 | - | ||
| 114 | - | ||
| 92 | + | begin | |
| 93 | + | // Ensure a node is selected | |
| 94 | + | if(Treeview1.Selected <> nil) then | |
| 95 | + | // Ensure there is a previous sibling node | |
| 96 | + | if Treeview1.Selected.GetPrevSibling <> nil then | |
| 97 | + | // If we have made it this far, move it UP | |
| 98 | + | Treeview1.Selected.MoveTo(Treeview1.Selected.GetPrevSibling, naInsert); | |
| 99 | + | end; | |
| 100 | + | ||
| 101 | + | procedure TForm1.DownBtnClick(Sender: TObject); | |
| 102 | + | begin | |
| 103 | + | // Ensure a node is selected | |
| 104 | + | if(Treeview1.Selected <> nil) then | |
| 105 | + | // Ensure there is a next sibling node | |
| 106 | + | if Treeview1.Selected.GetNextSibling <> nil then | |
| 107 | + | // If we have made it this far, move it DOWN | |
| 108 | + | Treeview1.Selected.MoveTo(Treeview1.Selected.GetNextSibling, naInsertBehind); | |
| 109 | + | end; | |
| 115 | 110 | ``` | |
| 116 | 111 | ||
| 117 | - | ||
| 118 | - | ||
| 119 | - | ||
| 120 | - | ||
| 121 | - | ||
| 122 | - | ||
| 123 | - | ||
| 124 | - | ||
| 112 | + | Note: These procedures will only move nodes which are at the same level in the TTreeview. | |
| 125 | 113 | ||
| 114 | + | Table of attachment mode of Nodes | |
| 115 | + | --- | |
| 116 | + | Mode | Effect | |
| 117 | + | naAdd | add as last sibling of target node | |
| 118 | + | naAddFirst | add as first sibling of target node | |
| 119 | + | naAddChild | add as last child of target node | |
| 120 | + | naAddChildFirst | add as first child of target node | |
| 121 | + | naInsert | insert in front of target node | |
| 122 | + | naInsertBehind | insert behind target node | |
| 123 | + | ||
| 126 | 124 | ## Using multiple node selection | |
| 127 | 125 | ||
| 128 | - | ||
| 129 | - | ||
| 130 | - | ||
| 126 | + | To allow multiple selection, you need to set the `tvoAllowMultiSelect` flag of the `Options` property. This will allow you to select additional nodes by `Ctrl`+`Mouse click`. | |
| 131 | 127 | ||
| 132 | - | ||
| 128 | + | To get a list of selected nodes from code, you can use the `TreeView1.Selections` property: | |
| 129 | + | ``` | |
| 133 | 130 | procedure TForm1.Button1Click(Sender: TObject); | |
| 134 | - | ||
| 135 | - | ||
| 136 | - | ||
| 137 | - | ||
| 138 | - | ||
| 139 | - | ||
| 140 | - | ||
| 131 | + | var | |
| 132 | + | i: integer; | |
| 133 | + | begin | |
| 134 | + | Memo1.Lines.Clear; | |
| 135 | + | for i := 0 to TreeView1.SelectionCount - 1 do | |
| 136 | + | Memo1.Lines.Add(TreeView1.Selections[i].GetTextPath); | |
| 137 | + | end; | |
| 141 | 138 | ``` | |
| 142 | 139 | ||
| 143 | 140 | ## Freeing TreeNode Data | |
| 144 | 141 | ||
| 145 | - | ||
| 146 | - | ||
| 147 | - | ||
| 142 | + | Use the TreeView's OnDeletion event to free your object. | |
| 143 | + | ``` | |
| 148 | 144 | procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode); | |
| 149 | - | ||
| 150 | - | ||
| 151 | - | ||
| 152 | - | ||
| 145 | + | begin | |
| 146 | + | TMyObject(Node.Data).Free; | |
| 147 | + | Node.Data := nil; | |
| 148 | + | end; | |
| 153 | 149 | ``` | |
| 154 | 150 | ||
| 155 | 151 | ## Using Drag and Drop in a TreeView | |
| 156 | 152 | ||
| 157 | - | ||
| 158 | - | ||
| 159 | - | ||
| 160 | - | ||
| 161 | - | ||
| 162 | - | ||
| 163 | - | ||
| 153 | + | If you want to allow a drag and drop function in your treeview, you need to : | |
| 164 | 154 | ||
| 165 | - | ||
| 166 | - | ||
| 155 | + | 1. Set to DmAutomatic, the property "Drag Mode" of your treeview | |
| 156 | + | 2. Create events for `onDragDrop` and `OnDragOver`: | |
| 167 | 157 | ||
| 158 | + | ``` | |
| 168 | 159 | ... | |
| 169 | - | ||
| 170 | - | ||
| 171 | - | ||
| 172 | - | ||
| 173 | - | ||
| 174 | - | ||
| 175 | - | ||
| 176 | - | ||
| 160 | + | ||
| 161 | + | Var | |
| 162 | + | Node: TTreeNode; | |
| 163 | + | ||
| 164 | + | ... | |
| 165 | + | ||
| 166 | + | procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer); | |
| 177 | 167 | begin | |
| 178 | - | ||
| 179 | - | ||
| 180 | - | ||
| 181 | - | ||
| 182 | - | ||
| 183 | - | ||
| 184 | - | ||
| 185 | - | ||
| 168 | + | Treeview1 := TTreeView(Sender); { Sender is TreeView where the data is being dropped } | |
| 169 | + | Node := Treeview1.GetNodeAt(x,y); { x,y are drop coordinates (relative to the Sender) } | |
| 170 | + | { since Sender is TreeView we can evaluate } | |
| 171 | + | { a tree at the X,Y coordinates } | |
| 172 | + | if Source = Sender then { drop is happening within a TreeView } | |
| 173 | + | begin | |
| 174 | + | if Assigned(Treeview1.Selected) and { check if any node has been selected } | |
| 175 | + | (Node <> Treeview1.Selected) then { and we're dropping to another node } | |
| 176 | + | begin | |
| 177 | + | if Node <> nil then | |
| 178 | + | Treeview1.Selected.MoveTo(Node, naAddChild) { complete drop, by moving selected node } | |
| 179 | + | else | |
| 180 | + | Treeview1.Selected.MoveTo(Node, naAdd); { complete drop, by moving selected node in root } | |
| 181 | + | end; | |
| 182 | + | end; | |
| 186 | 183 | end; | |
| 187 | - | ||
| 188 | - | ||
| 189 | - | ||
| 190 | - | ||
| 191 | - | ||
| 192 | - | ||
| 184 | + | ||
| 185 | + | procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); | |
| 186 | + | begin | |
| 187 | + | Accept := true; | |
| 188 | + | end; | |
| 193 | 189 | ``` | |
| 194 | 190 | ||
| 195 | 191 | ## Comments on Events | |
| 196 | 192 | ||
| 197 | - | ||
| 193 | + | Event handlers are convenient places for developing LCL applications. There are some peculiar events for TTreeview. | |
| 198 | 194 | ||
| 199 | 195 | **When selected node is changed** | |
| 200 | - | ||
| 201 | - | ||
| 196 | + | ``` | |
| 202 | 197 | procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode); | |
| 203 | 198 | ``` | |
| 204 | 199 | ||
| 205 | - | ||
| 206 | - | ||
| 207 | - | ||
| 200 | + | The `OnChange` event is fired when another node is selected. It can be used in a program to update some data. | |
| 201 | + | ``` | |
| 208 | 202 | procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean); | |
| 209 | 203 | ``` | |
| 210 | 204 | ||
| 211 | - | ||
| 205 | + | The `OnChanging` event is called before `OnChange`, which allows you to prevent node selection under certain conditions. To do this, just set `AllowChange` to `false`. It is not necessary to set to `true` as this is the default value. | |
| 212 | 206 | ||
| 213 | - | ||
| 207 | + | Please note that in Delphi the `Node` variable points to the node that leaves the selection. In Lazarus, the `Node` variable points to the node receiving the selection, but you can use the `Selected` property to find out the "previous" node. | |
| 214 | 208 | ||
| 215 | - | ||
| 209 | + | In both events, the `Node` and `Selected` can be equal to `nil` (if there was no selection or it is being removed). | |
| 216 | 210 | ||
| 217 | 211 | **When a node is inserted** | |
| 218 | 212 | ||
| 219 | - | ||
| 220 | - | ||
| 221 | - | ||
| 222 | - | ||
| 223 | - | ||
| 224 | - | ||
| 225 | - | ||
| 213 | + | When a new node is inserted, related events occur in the following order. | |
| 226 | 214 | ``` | |
| 215 | + | 1) OnNodeChanged | |
| 216 | + | 2) (OnChanging): This is called if there was a previously selected node. | |
| 217 | + | 3) OnSelectionChanged | |
| 218 | + | 4) OnChange | |
| 219 | + | ``` | |
| 227 | 220 | ||
| 228 | 221 | **When deleting a node** | |
| 229 | 222 | ||
| 230 | - | ||
| 231 | - | ||
| 232 | - | ||
| 233 | - | ||
| 234 | - | ||
| 235 | - | ||
| 236 | - | ||
| 223 | + | When the selected node is deleted, the following events occur. | |
| 237 | 224 | ``` | |
| 225 | + | 1) OnDelete | |
| 226 | + | 2) OnChanging | |
| 227 | + | 3) SelectionChanged | |
| 228 | + | 4) OnChange : This is called because when you delete a node, another node is selected. | |
| 229 | + | ``` | |
| 238 | 230 | ||
| 239 | - | ||
| 231 | + | If an unselected node is deleted, e.g. programmatically, then only the OnDelete event handler is called. | |
| 240 | 232 | ||
| 241 | 233 | **When Form is Closed** | |
| 242 | 234 | ||
| 243 | - | ||
| 235 | + | OnChanging event occurs, as the selected node is "de"selected. But OnChanging eventhander is executed AFTER the form is closed. This means that you cannot access the form's other data (other controls, other variables, etc.) within the OnChanging event handler procedure. | |
| 244 | 236 | ||
| 245 | - | ||
| 237 | + | **When Drag &Drop is done** | |
| 246 | 238 | ||
| 247 | - | ||
| 239 | + | When you drag a node to another node and drop, the following events occur. | |
| 240 | + | ``` | |
| 241 | + | 1) OnSelectionChanged | |
| 242 | + | 2) OnNodeChanged | |
| 243 | + | 3) OnChange | |
| 244 | + | ``` | |
| 248 | 245 | ||
| 249 | - | ||
| 250 | - | ||
| 251 | - | ||
| 252 | - | ||
| 246 | + | ## Painting of the TreeView | |
| 247 | + | ||
| 248 | + | The treeview can draw itself in three ways: | |
| 249 | + | ||
| 250 | + | * **Themed painting** (default) is done by dedicated, theme-specific drawing routines of the operating system/widgetset. You have litte control over the appearance of the tree (mostly the colors), but it looks consistently across applications, in particular concerning selection and hot-tracking. | |
| 251 | + | * **Non-themed painting** is activated by removing the element `tvoThemedDraw` from the treeview's `Options` property. It gives you more control over the drawing process. For example, you can set the color of the selection background (`SelectionColor`) and text (`SelectionFontColor`), or the color of the hot-tracked node text (`HotTrackColor`). | |
| 252 | + | * **Custom painting** gives you full control over the drawing process by providing handlers for specific events. Depending on the return value of the event parameter `DefaultDraw`, you can determine whether you want to draw everything yourself (`DefaultDraw = false`) or whether you still want to use the built-in routines, but use different parameters that you specify in the event handler (`DefaultDraw = true`). | |
| 253 | + | ||
| 254 | + | Basically the drawing process of the treeview consists of two phases: | |
| 255 | + | ||
| 256 | + | * Drawing of the **background** : You can custom-draw the background by providing a handler for the `OnCustomDraw` and/or `OnAdvancedCustomDraw` events. | |
| 257 | + | * Drawing of the **individual tree nodes** over this background: You can custom-draw the nodes by providing a handler for the `OnCustomDrawItem` and/or `OnAdvancedCustomDrawItem` events. | |
| 258 | + | ||
| 259 | + | The "advanced" events are fired several times during the painting process. There is a `Stage` parameter which signals what is happening at the moment. It is an enumeration of the following elements: | |
| 260 | + | ||
| 261 | + | * `cdPrePaint` and `cdPostPaint` are set by the `OnAdvancedCustomDraw` and `OnAdvancedCustomDrawItem` events before any drawing action begins (`cdPrePaint`) / after drawing has been completed (`cdPostPaint`), respectively. Be careful when setting `DefaultDraw` to `false` here because the drawing procedure will be left after the event handler - it is assumed that you had done all the following steps yourself. | |
| 262 | + | * `cdPreErase` and `cdPostErase` have not been used for a long time (also in Delphi). But they were implemented in Lazarus v4.99 to get more control over the custom drawing process of the nodes in the `OnAdvancedCustomDrawItem` event: | |
| 263 | + | * `cdPreErase` is set in `OnAdvancedCustomDrawItem` before the normal background of a tree node is painted. | |
| 264 | + | * `cdPostErase` is set in`OnAdvancedCustomDrawItem` before the background and the text of a selected or hot-tracked node is painted. | |
| 265 | + | ||
| 266 | + | They still are not used by the `OnAdvancedCustomDraw` event. | |
| 267 | + | ||
| 268 | + | In contrast to `cdPrePaint`, setting `DefaultDraw` to `false` here only inhibits painting of the background of the current node. | |
| 269 | + | ||
| 270 | + | The `OnCustomDraw*` events are equivalent to the "advanced" events in the `cdPrePaint` stage. | |
| 271 | + | ||
| 272 | + | ### Example: Custom-painting a treeview with a background gradient | |
| 273 | + | ||
| 274 | + | The gradient must be painted at the very beginning of the drawing cyle, i.e. we must provide a handler for the `OnAdvancedCustomDraw` event at the `cdPrePaint` stage: | |
| 253 | 275 | ``` | |
| 276 | + | procedure TForm1.BackgroundGradient_AdvancedCustomDraw(Sender: TCustomTreeView; | |
| 277 | + | const ARect: TRect; Stage: TCustomDrawStage; var DefaultDraw: Boolean); | |
| 278 | + | begin | |
| 279 | + | if Stage = cdPrePaint then | |
| 280 | + | begin | |
| 281 | + | // Draw the gradient (sky-blue at the top, to white at the bottom) | |
| 282 | + | Sender.Canvas.GradientFill(ARect, clSkyBlue, clWhite, gdVertical); | |
| 283 | + | // Avoid painting the normal background below the last node | |
| 284 | + | Sender.Canvas.Brush.Color := clNone; | |
| 285 | + | end; | |
| 286 | + | // We must not set DefaultDraw to false here because the CustomDraw | |
| 287 | + | // PrePaint stage would force us to paint everything by ourselves. | |
| 288 | + | end; | |
| 289 | + | ``` | |
| 254 | 290 | ||
| 291 | + | However, that's not all because when the default painting routine of the node background is executed the gradient is overwritten by the node rectangle over the entire tree width. Therefore we must inhibit drawing of the normal background in `OnAdvancedCustomDrawItem` in the `cdPreErase` stage: | |
| 292 | + | ``` | |
| 293 | + | procedure TMainForm.BackgroundGradient_AdvancedCustomDrawItem(Sender: TCustomTreeView; | |
| 294 | + | Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage; | |
| 295 | + | var PaintImages, DefaultDraw: Boolean); | |
| 296 | + | begin | |
| 297 | + | if Stage = cdPreErase then | |
| 298 | + | // Avoid overwriting the gradient with the node background color | |
| 299 | + | DefaultDraw := false; | |
| 300 | + | end; | |
| 301 | + | ``` | |
| 302 | + | ||
| 303 | + | When `tvoThemedDraw` is contained in the treeview's `Options`, this draws a themed treeview, but with gradient background. If you want to custom-draw also the selected and hot-tracked nodes you must remove `tovThemedDraw` from the `Options` and also set the corresponding canvas properties in the `cdPostErase` stage: | |
| 304 | + | ``` | |
| 305 | + | procedure TMainForm.BackgroundGradient_AdvancedCustomDrawItem(Sender: TCustomTreeView; | |
| 306 | + | Node: TTreeNode; State: TCustomDrawState; Stage: TCustomDrawStage; | |
| 307 | + | var PaintImages, DefaultDraw: Boolean); | |
| 308 | + | begin | |
| 309 | + | case Stage of | |
| 310 | + | cdPreErase: | |
| 311 | + | // Avoid overwriting the gradient with the node background color | |
| 312 | + | DefaultDraw := false; | |
| 313 | + | cdPostErase: | |
| 314 | + | // Set selected and hot-track properties as usual | |
| 315 | + | if ([cdsFocused, cdsSelected] * State <> []) then // selected node | |
| 316 | + | begin | |
| 317 | + | Sender.Canvas.Brush.Color := clNavy; | |
| 318 | + | Sender.Canvas.Font.Color := clYellow; | |
| 319 | + | Sender.Canvas.Font.Style := [fsBold]; | |
| 320 | + | end else | |
| 321 | + | if (cdsHot in State) then // hot-tracked node | |
| 322 | + | begin | |
| 323 | + | Sender.Canvas.Brush.Color := clGray; | |
| 324 | + | Sender.Canvas.Font.Color := clHighlightText; | |
| 325 | + | Sender.Canvas.Font.Style := []; | |
| 326 | + | end else // normal node | |
| 327 | + | // Setting Brush.Color to clNone inhibits drawing the rectangle for the normal nodes. | |
| 328 | + | // Don't use Sender.Canvas.BrushStyle = bsClear because it will be reset by changing the brush color. | |
| 329 | + | Sender.Canvas.Brush.Color := clNone; | |
| 330 | + | end; | |
| 331 | + | end; | |
| 332 | + | ``` | |
| 333 | + | ||
| 334 | + | All this looks rather complicated... Yes, it is. Therefore, you can find more worked-out examples in the demo project in the _examples/treeview/customdrawing_ folder of the Lazarus installation. | |
| 335 | + | ||
| 336 | + | ## Saving and loading a TTreeView | |
| 337 | + | ||
| 338 | + | Using TTreeView's method SaveTFile (or SaveToStream) you can save the Items of a TTreeView. | |
| 339 | + | Notice that this will save only the Text of each Item in a plain text format. | |
| 340 | + | A file (or stream) created as such, can be loaded using the LoadFromFile (or LoadFromStream) method. | |
| 341 | + | ||
| 342 | + | As of Lazarus 4.99 you can use the procedures in the TreeStorage unit to save and restore the items of a TTreeView along with most of the item's properties. | |
| 343 | + | By default it will save the properties `Expanded`, `Selected`, `Focused`, `Visible`, `Enabled`, `ImageIndex`, `SelectedIndex`, `StateIndex` and `tsoOverlayIndex`. | |
| 344 | + | You can supply a callback event to these procedures to load (and save) arbitrary data. | |
| 345 | + | The example below saves the contents of a TTreeView into a stream and shows the stream in a TSynEdit with additional information using a callback event. | |
| 346 | + | ||
| 347 | + | ``` | |
| 348 | + | uses | |
| 349 | + | ..., TreeStorage, Laz2_Dom, ... | |
| 350 | + | ||
| 351 | + | function DbgS(AGraphicsDrawEffect: TGraphicsDrawEffect): String; overload; | |
| 352 | + | begin | |
| 353 | + | WriteStr(Result, AGraphicsDrawEffect); | |
| 354 | + | end; | |
| 355 | + | ||
| 356 | + | procedure TForm1.WriteCallBack(ATreeNode: TTreeNode; ADomNode: TDomNode); | |
| 357 | + | begin | |
| 358 | + | TDomElement(ADomNode).Attribute['NodeEffect'] := Dbgs(ATreeNode.NodeEffect) | |
| 359 | + | end; | |
| 360 | + | ||
| 361 | + | procedure TForm1.Button2Click(Sender: TObject); | |
| 362 | + | var | |
| 363 | + | F: TextFile; | |
| 364 | + | S: String; | |
| 365 | + | Str: TMemoryStream; | |
| 366 | + | begin | |
| 367 | + | Str := TMemoryStream.Create; | |
| 368 | + | try | |
| 369 | + | TreeStorage.TreeSaveToXML(TreeView1, Str, TreeItemStorageDefaultOptions, @WriteCallBack); | |
| 370 | + | Str.Position := 0; | |
| 371 | + | SynEdit1.Lines.LoadFromStream(Str); | |
| 372 | + | finally | |
| 373 | + | Str.Free; | |
| 374 | + | end; | |
| 375 | + | end; | |
| 376 | + | ``` | |
| 377 | + | ||
| 255 | 378 | ## See also | |
| 256 | 379 | ||
| 257 | - | ||
| 258 | - | ||
| 259 | - | ||
| 260 | - | ||
| 380 | + | * [[Drag and Drop sample]] | |
| 381 | + | * [ TTreeView doc](<http://lazarus-ccr.sourceforge.net/docs//lcl/comctrls/ttreeview.html> "doc:/lcl/comctrls/ttreeview.html") | |
| 382 | + | * [[TListView]] | |
| 383 | + | * [[VirtualTreeview]] | |
| 261 | 384 | ||
| 385 | + | ||
| 386 | + | ||
| 387 | + | [[LCL Components]] Component Tab | Components | |
| 388 | + | ---|--- | |
| 389 | + | [[Standard tab|Standard]] | [[TMainMenu]] • [[TPopupMenu]] • [[TButton]] • [[TLabel]] • [[TEdit]] • [[TMemo]] • [[TToggleBox]] • [[TCheckBox]] • [[TRadioButton]] • [[TListBox]] • [[TComboBox]] • [[TScrollBar]] • [[TGroupBox]] • [[TRadioGroup]] • [[TCheckGroup]] • [[TPanel]] • [[TFrame]] • [[TActionList]] | |
| 390 | + | [[Additional tab|Additional]] | [[TBitBtn]] • [[TSpeedButton]] • [[TStaticText]] • [[TImage]] • [[TShape]] • [[TBevel]] • [[TPaintBox]] • [[TNotebook]] • [[TLabeledEdit]] • [[TSplitter]] • [[TTrayIcon]] • [[TControlBar]] • [[TFlowPanel]] • [[TMaskEdit]] • [[TCheckListBox]] • [[TScrollBox]] • [[TApplicationProperties]] • [[TStringGrid]] • [[TDrawGrid]] • [[TPairSplitter]] • [[TColorBox]] • [[TColorListBox]] • [[TValueListEditor]] | |
| 391 | + | [[Common Controls tab|Common Controls]] | [[TTrackBar]] • [[TProgressBar]] • TTreeView • [[TListView]] • [[TStatusBar]] • [[TToolBar]] • [[TCoolBar]] • [[TUpDown]] • [[TPageControl]] • [[TTabControl]] • [[THeaderControl]] • [[TImageList]] • [[TPopupNotifier]] • [[TDateTimePicker]] | |
| 392 | + | [[Dialogs tab|Dialogs]] | [[TOpenDialog]] • [[TSaveDialog]] • [[TSelectDirectoryDialog]] • [[TColorDialog]] • [[TFontDialog]] • [[TFindDialog]] • [[TReplaceDialog]] • [[TTaskDialog]] • [[TOpenPictureDialog]] • [[TSavePictureDialog]] • [[TCalendarDialog]] • [[TCalculatorDialog]] • [[TPrinterSetupDialog]] • [[TPrintDialog]] • [[TPageSetupDialog]] | |
| 393 | + | [[Data Controls tab|Data Controls]] | [[TDBNavigator]] • [[TDBText]] • [[TDBEdit]] • [[TDBMemo]] • [[TDBImage]] • [[TDBListBox]] • [[TDBLookupListBox]] • [[TDBComboBox]] • [[TDBLookupComboBox]] • [[TDBCheckBox]] • [[TDBRadioGroup]] • [[TDBCalendar]] • [[TDBGroupBox]] • [[TDBGrid]] • [[TDBDateTimePicker]] | |
| 394 | + | [[Data Access tab|Data Access]] | [[TDataSource]] • [[TCSVDataSet]] • [[TSdfDataSet]] • [[TBufDataset]] • [[TFixedFormatDataSet]] • [[TDbf]] • [[TMemDataset]] | |
| 395 | + | [[System tab|System]] | [[TTimer]] • [[TIdleTimer]] • [[TLazComponentQueue]] • [[THTMLHelpDatabase]] • [[THTMLBrowserHelpViewer]] • [[TAsyncProcess]] • [[TProcessUTF8]] • [[TProcess]] • [[TSimpleIPCClient]] • [[TSimpleIPCServer]] • [[TXMLConfig]] • [[TEventLog]] • [[TServiceManager]] • [[TCHMHelpDatabase]] • [[TLHelpConnector]] | |
| 396 | + | [[Misc tab|Misc]] | [[TColorButton]] • [[TSpinEdit]] • [[TFloatSpinEdit]] • [[TArrow]] • [[TCalendar]] • [[TEditButton]] • [[TFileNameEdit]] • [[TDirectoryEdit]] • [[TDateEdit]] • [[TTimeEdit]] • [[TCalcEdit]] • [[TFileListBox]] • [[TFilterComboBox]] • [[TComboBoxEx]] • [[TCheckComboBox]] • [[TButtonPanel]] • [[TShellTreeView]] • [[TShellListView]] • [[TXMLPropStorage]] • [[TINIPropStorage]] • [[TJSONPropStorage]] • [[TIDEDialogLayoutStorage]] • [[TMRUManager]] • [[TStrHolder]] | |
| 397 | + | [[LazControls tab|LazControls]] | [[TCheckBoxThemed]] • [[TDividerBevel]] • [[TExtendedNotebook]] • [[TListFilterEdit]] • [[TListViewFilterEdit]] • [[TLvlGraphControl]] • [[TShortPathEdit]] • [[TSpinEditEx]] • [[TFloatSpinEditEx]] • [[TTreeFilterEdit]] • [[TExtendedTabControl]] • | |
| 398 | + | [[RTTI tab|RTTI]] | [[TTIEdit]] • [[TTIComboBox]] • [[TTIButton]] • [[TTICheckBox]] • [[TTILabel]] • [[TTIGroupBox]] • [[TTIRadioGroup]] • [[TTICheckGroup]] • [[TTICheckListBox]] • [[TTIListBox]] • [[TTIMemo]] • [[TTICalendar]] • [[TTIImage]] • [[TTIFloatSpinEdit]] • [[TTISpinEdit]] • [[TTITrackBar]] • [[TTIProgressBar]] • [[TTIMaskEdit]] • [[TTIColorButton]] • [[TMultiPropertyLink]] • [[TTIPropertyGrid]] • [[TTIGrid]] | |
| 399 | + | [[SQLdb tab|SQLdb]] | [[TSQLQuery]] • [[TSQLTransaction]] • [[TSQLScript]] • [[TSQLConnector]] • [[TMSSQLConnection]] • [[TSybaseConnection]] • [[TPQConnection]] • [[TPQTEventMonitor]] • [[TOracleConnection]] • [[TODBCConnection]] • [[TMySQL40Connection]] • [[TMySQL41Connection]] • [[TMySQL50Connection]] • [[TMySQL51Connection]] • [[TMySQL55Connection]] • [[TMySQL56Connection]] • [[TMySQL57Connection]] • [[TSQLite3Connection]] • [[TIBConnection]] • [[TFBAdmin]] • [[TFBEventMonitor]] • [[TSQLDBLibraryLoader]] | |
| 400 | + | [[Pascal Script tab|Pascal Script]] | [[TPSScript]] • [[TPSScriptDebugger]] • [[TPSDllPlugin]] • [[TPSImport Classes]] • [[TPSImport DateUtils]] • [[TPSImport ComObj]] • [[TPSImport DB]] • [[TPSImport Forms]] • [[TPSImport Controls]] • [[TPSImport StdCtrls]] • [[TPSCustomPlugin]] | |
| 401 | + | [[SynEdit tab|SynEdit]] | [[TSynEdit]] • [[TSynCompletion]] • [[TSynAutoComplete]] • [[TSynMacroRecorder]] • [[TSynExporterHTML]] • [[TSynPluginSyncroEdit]] • [[TSynPasSyn]] • [[TSynFreePascalSyn]] • [[TSynCppSyn]] • [[TSynJavaSyn]] • [[TSynPerlSyn]] • [[TSynHTMLSyn]] • [[TSynXMLSyn]] • [[TSynLFMSyn]] • [[TSynDiffSyn]] • [[TSynUNIXShellScriptSyn]] • [[TSynCssSyn]] • [[TSynPHPSyn]] • [[TSynTeXSyn]] • [[TSynSQLSyn]] • [[TSynPythonSyn]] • [[TSynVBSyn]] • [[TSynAnySyn]] • [[TSynMultiSyn]] • [[TSynBatSyn]] • [[TSynIniSyn]] • [[TSynPoSyn]] | |
| 402 | + | [[Chart tab|Chart]] | [[TChart]] • [[TListChartSource]] • [[TRandomChartSource]] • [[TUserDefinedChartSource]] • [[TCalculatedChartSource]] • [[TDbChartSource]] • [[TChartToolset]] • [[TChartAxisTransformations]] • [[TChartStyles]] • [[TChartLegendPanel]] • [[TChartNavScrollBar]] • [[TChartNavPanel]] • [[TIntervalChartSource]] • [[TDateTimeIntervalChartSource]] • [[TChartListBox]] • [[TChartExtentLink]] • [[TChartImageList]] | |
| 403 | + | [[IPro tab|IPro]] | [[TIpFileDataProvider]] • [[TIpHtmlDataProvider]] • [[TIpHttpDataProvider]] • [[TIpHtmlPanel]] | |
| 404 | + | [[Virtual Controls tab|Virtual Controls]] | [[TVirtualDrawTree]] • [[TVirtualStringTree]] • [[TVTHeaderPopupMenu]] | |
| 405 | + | ||
| 262 | 406 | --- | |
| 263 | - | ||
| 407 | + | *From the Free Pascal wiki, page "TTreeView", as the Internet Archive kept it on 2025-06-16, last changed on 2025-05-17. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
