Views


This section starts off with a couple of views provided by the .NET Framework: ListView and TreeView. If you have used Windows for any amount of time, then you have seen and used both of these views as they are used quite extensively. The reason is that these views provide, when used correctly, a better way of displaying data to the user.

A point that may not be apparent about views is that they are controls. This means that they are inheritable and derive from both component and control classes. Thus, they have all the benefits provided by both.

ListView

The ListView is a powerful (but slightly complicated) control that displays a list of items. You can see what a ListView control looks like by opening up Windows Explorer. The ListView is the right-hand panel if two panels are being displayed. The items can consist of a combination of a record (array) of text, a large icon, and/or a small icon. I cover working with icons later in the chapter in the "ToolBar" section.

You can display a ListView in one of four different View property modes:

  • View::LargeIcon displays a large icon with text underneath in a grid layout.

  • View::SmallIcon displays a small icon with text along the side in columns.

  • View::List displays the root text associated with the item in a single column.

  • View::Details displays the root text and subtext in multiple columns.

Providing the functionality of the ListView requires a number of properties, many of which you have seen before. Here are some of the common ones unique to the ListView:

  • Activation is an ItemActivation enum that represents whether one or two clicks are required to activate an item. The default is two clicks or ItemActivation::Standard.

  • AllowColumnReorder is a Boolean that represents whether the headings can be dragged to reorder the columns. The default is false.

  • AutoArrange is a Boolean that represents whether the items are automatically arranged. The default is true.

  • Columns is a ListView::ColumnHeaderCollection that represents a collection of column headers to be used if the View property mode is set to View::Details.

  • Focus Item is a ListViewItem that represents the item that currently has focus. If no item has focus, null is returned.

  • FullRowSelect is a Boolean that represents whether clicking an item selects all its subitems as well. The default is false.

  • GridLines is a Boolean that represents whether grid lines are displayed. The default is false.

  • HeaderStyle is a ColumnHeaderStyle enum that represents whether the header is displayed and if it is clickable. The default is displayed and click-able: ColumnHeaderStyle::Clickable.

  • HoverSelection is a Boolean that represents whether the item is automatically selected when the cursor hovers over it for a few seconds. The default is false.

  • LabelEdit is a Boolean that represents whether the label of an item can be edited. The default is false.

  • LabelWrap is a Boolean that represents whether the label wraps when displayed. The default is true.

  • LargeImageList is an ImageList of the large icons to be used if the View property is set to View::LargeIcon.

  • SmallImageList is an ImageList of the small icons to be used if the View property is set to View::SmallIcon.

Along with these properties, the ListView provides a number of methods. These are some of the common methods unique to ListView:

  • ArrangeIcons() arranges the icons in large and small icon views.

  • EnsureVisible() ensures that an item is visible even if the ListView must scroll to make it visible.

  • GetItemAt() gets an item at a specified x and y location.

Listing 10-1 shows a ListView of fruit, their price, and the month when they are available for harvest. (The data was derived using a high-tech research facility. Okay, you caught me—I made it up.) When an item is selected, its price is displayed in a label.

Listing 10-1: A ListView of Fruit

start example
 namespace ListView1 {     using namespace System;     using namespace System::ComponentModel;     using namespace System::Collections;     using namespace System::Windows::Forms;     using namespace System::Data;     using namespace System::Drawing;     public _gc class Form1 : public System::Windows::Forms::Form     {     public:         Form1(void)         {             InitializeComponent();             FillListView();         }     protected:         void Dispose(Boolean disposing)         //...     private: System::Windows::Forms::ColumnHeader *  Fruit;     private: System::Windows::Forms::ColumnHeader *  Price;     private: System::Windows::Forms::ColumnHeader *  Available;     private: System::Windows::Forms::ListView *  lView;     private: System::Windows::Forms::Label *  label;     private: System::ComponentModel::Container * components;         void InitializeComponent(void)         {             this->lView = new System::Windows::Forms::ListView();             this->Fruit = new System::Windows::Forms::ColumnHeader();             this->Price = new System::Windows::Forms::ColumnHeader();             this->Available = new System::Windows::Forms::ColumnHeader();             this->label = new System::Windows::Forms::Label();             this->SuspendLayout();             //             // lView             //             this->lView->Anchor = (System::Windows::Forms::AnchorStyles)                 ((System::Windows::Forms::AnchorStyles::Top |                    System::Windows::Forms::AnchorStyles::Left) |                    System::Windows::Forms::AnchorStyles::Right);             System::Windows::Forms::ColumnHeader* __mcTemp__1[] =                 new System::Windows::Forms::ColumnHeader*[3];             __mcTemp__1[0] = this->Fruit;             __mcTemp__1[1] = this->Price;             __mcTemp__1[2] = this->Available;             this->lView->Columns->AddRange(__mcTemp__1);             this->lView->FullRowSelect = true;             this->lView->GridLines = true;             this->lView->Location = System::Drawing::Point(0, 0);             this->lView->MultiSelect = false;             this->lView->Name = S"lView";             this->lView->Size = System::Drawing::Size(424, 248);             this->lView->TabIndex = 0;             this->lView->View = System::Windows::Forms::View::Details;             this->lView->SelectedIndexChanged +=                    new System::EventHandler(this, lView_SelectedIndexChanged);             //             // Fruit             //             this->Fruit->Text = S"Fruit";             //             // Price             //             this->Price->Text = S"Price";             //             // Available             //             this->Available->Text = S"Available";             this->Available->Width = 100;             //             // label             //             this->label->BorderStyle =                    System::Windows::Forms::BorderStyle::FixedSingle;             this->label->Location = System::Drawing::Point(170, 260);             this->label->Name = S"label";             this->label->Size = System::Drawing::Size(60, 24);             this->label->TabIndex = 1;             this->label->TextAlign =                    System::Drawing::ContentAlignment::MiddleCenter;             //             // Form1             //             this->AutoScaleBaseSize = System::Drawing::Size(6, 15);             this->ClientSize = System::Drawing::Size(400, 300);             this->Controls->Add(this->label);             this->Controls->Add(this->lView);             this->Name = S"Form1";             this->Text = S"The List View Control";             this->ResumeLayout(false);         }     private:         void FillListView()         {             String *itemRec1[]  = { S"Apple", S"1.50", S"September" };             lView->Items->Add(new ListViewItem(itemRec1));             String *itemRec2[]  = { S"Orange", S"2.50", S"March" };             lView->Items->Add(new ListViewItem(itemRec2));             String *itemRec3[]  = { S"Grape", S"1.95", S"November" };             lView->Items->Add(new ListViewItem(itemRec3));         }     private:         System::Void lView_SelectedIndexChanged(System::Object * sender,                                                     System::EventArgs *  e)         {             if (lView->FocusedItem != 0)                 label->Text = lView->FocusedItem->SubItems->Item[1]->Text;         }     }; } 
end example

Working with the ListView is a little tricky because the GUI designer doesn't place things in the code where you expect them (or at least I don't think so). So I'll group the code together so that you can see what's happening more clearly.

First, like any control, you create the ListView and then configure it using its properties. The example ListView is anchored and uses full row selection, display gridlines, no multiple selections, and the detailed view.

 private: System::Windows::Forms::ListView * lView; //... this->lView = new System::Windows::Forms::ListView(); this->lView->Anchor = (System::Windows::Forms::AnchorStyles)                        ((System::Windows::Forms::AnchorStyles::Top |                          System::Windows::Forms::AnchorStyles::Left) |                          System::Windows::Forms::AnchorStyles::Right); this->lView->FullRowSelect = true; this->lView->GridLines = true; this->lView->MultiSelect = false; this->lView->Size = System::Drawing::Size(424, 248); this->lView->View = System::Windows::Forms::View::Details; this->lView->SelectedIndexChanged +=                     new System::EventHandler(this, lView_SelectedIndexChanged); this->Controls->Add(this->lView); 

Next, because the detailed view is used, you need to create headers for the ListView's items. Notice that you add the headers to the ListView control's Column property.

 // Create and configure Header this->Available = new System::Windows::Forms::ColumnHeader(); this->Available->Text = S"Available"; this->Available->Width = 100; // Add header to ListView System::Windows::Forms::ColumnHeader* __mcTemp__1[] =     new System::Windows::Forms::ColumnHeader*[3];         __mcTemp_1[0] = this->Fruit;         __mcTemp__1[1] = this->Price;         __mcTemp__1[2] = this->Available; this->lView->Columns->AddRange(__mcTemp__1); 

Finally, once the ListView is ready for the world to see, you add the list items to the view. I showed this being done manually, but you could also use the designer to add list items.

 // Add an Apple to the listview String *itemRec1[]  = { S"Apple",   S"1.50", S"September" }; lView->Items->Add(new ListViewItem(itemRec1)); 

Figure 10-1 shows what ListView.exe looks like when you execute it.

click to expand
Figure 10-1: A ListView of fruit

TreeView

If you have worked with Visual Studio .NET, then you should be familiar with the TreeView control. It is used in numerous places—Solution Explorer, Server Explorer, and Class View, just to name a few. It is a control that displays a hierarchy of items in a tree format.

The TreeView, like the ListView just covered, can be a little complicated when you first try to develop code for it. Once you get the hang of it, though, you will realize that it is worth the effort of learning. The TreeView is a powerful tool that you will probably use several times in your coding career.

Configuring the TreeView control requires setting properties, just as with every other control. Here are the common properties you will likely use:

  • CheckBoxes is a Boolean that represents whether check boxes are displayed next to each node in the tree. The default is false.

  • ImageIndex is a zero-based Int32 index to the ImageList that represents the position of the default image used by all nodes of the tree. The default is 0. A value of -1 specifies that no image will be used.

  • ImageList is a collection of bitmaps, icons, and metafiles that will be used to display the images on the tree control. If the Image list is null, which is the default, no images are displayed on the tree.

  • Indent is an Int32 that represents the distance in pixels to indent for each tree hierarchy level. The default is 19.

  • LabelEdit is a Boolean that represents whether the label is editable. The default is false.

  • Nodes is a TreeNodeCollection that represents all the TreeNodes that make up the tree. You will always have to populate this property and there is no default.

  • SelectedImageIndex is a zero-based Int32 index to the ImageList that represents the position of the default selected image used by the tree. The default is 0. A value of -1 specifies that no image will be used.

  • SelectedNode is a TreeNode that represents the currently selected node. The default is null, which means no node has been selected.

  • ShowLines is a Boolean that represents whether lines will be displayed between nodes. The default is true, which means that lines will be displayed.

  • ShowPlusMinus is a Boolean that represents whether the expand (+) and contract (-) buttons are displayed for nodes that have child nodes. The default is true, which means that they will be displayed.

  • ShowRootLines is a Boolean that represents whether lines will be displayed between nodes that are at the root of the tree. The default is true, which means that lines will be displayed.

The key to working with the TreeView, like any other control, is to know which event to handle (see Table 10-1). All the events of the TreeView have default handlers, but if you want the control to do anything other than expand and contract, you need to handle the events yourself.

Table 10-1: Common TreeView Events

EVENT

DESCRIPTION

AfterCheck

Occurs after a check box is checked

AfterCollapse

Occurs after a node is collapsed

AfterExpand

Occurs after a node is expanded

AfterLabelEdit

Occurs after a label is edited

AfterSelect

Occurs after a node is selected

BeforeCheck

Occurs before a check box is checked

BeforeCollapse

Occurs before a node is collapsed

BeforeExpand

Occurs before a node is expanded

BeforeLabelEdit

Occurs before a label is edited

BeforeSelect

Occurs before a node is selected

The basic building block of a tree hierarchy is the TreeNode. There is always at least one root node and from it sprouts (possibly many) subnodes. A subnode in turn is also a TreeNode, which can spout its own TreeNodes.

There are several constructors for the TreeNode, but you'll probably deal mostly with two of them, unless you create the tree at design time (then you won't have to deal with them at all). The first constructor takes as a parameter a String as the label for the TreeNode and the second constructor also takes a String label but also an array of child TreeNodes. The second constructor allows for a node to have one or more child nodes. To make a node with only one child, you need to assign to the second parameter an array of child TreeNodes containing only one node.

 // Constructor for a node with no children TreeNode *rtnA = new TreeNode(S"Root Node A"); // Constructor for a node with children TreeNode *tnodes[] = { new TreeNode(S"Node A"), new TreeNode(S"Node B") }; TreeNode *rtnB = new TreeNode(S"Root Node A", tnodes); 

The TreeNode has a number of properties to handle its functionality. Many of the properties are used in navigating the tree. Here are some of the more common TreeNode properties:

  • Checked is a Boolean that represents whether the current node is checked. The default is false.

  • FirstNode is the first TreeNode in the Nodes collection of the current node in the TreeView. If the current node has no child nodes, then the property returns a null value.

  • FullPath is a String containing the entire path from the root to the current node delimited by backslashes (\). The path is all the nodes that need to be navigated to get to the current node.

  • ImageIndex is a zero-based Int32 index to the TreeView::ImageList associated with the current node that represents the position of the unselected image for the node. The default is the same value as is specified in the TreeView::ImageIndex associated with the current node.

  • Index is a zero-based Int32 index that represents the index of the current node within the TreeView's Nodes collection.

  • LastNode is the last TreeNode in the Nodes collection of the current node in the TreeView. If the current node has no child nodes, then the property returns a null value.

  • NextNode is the next sibling TreeNode in the Nodes collection of the current node in the TreeView. If the current node has no next sibling node, then the property returns a null value.

  • Nodes is a TreeNodeCollection that represents all the children nodes that make up the current tree node.

  • Parent is a TreeNode that represents the parent node of the current tree node.

  • PrevNode is the previous sibling TreeNode in the Nodes collection of the current node in the TreeView. If the current node has no previous sibling node, then the property returns a null value.

  • SelectedImageIndex is a zero-based Int32 index to the TreeView::ImageList associated with the current node that represents the position of the selected image for the node. The default is the same value as is specified in the TreeView::ImageIndex associated with the current node.

  • Text is a String that represents the text label of the current tree node.

  • TreeView is a TreeView that represents the TreeView that the current node in a member of.

Listing 10-2 shows how to build a tree hierarchy at runtime as opposed to prebuilding it statically. This example builds a new tree hierarchy every time it runs as it generates its node information randomly.

Listing 10-2: Random Tree Builder

start example
 namespace TreeView1 {     using namespace System;     using namespace System::ComponentModel;     using namespace System::Collections;     using namespace System::Windows::Forms;     using namespace System::Data;     using namespace System::Drawing;     public __gc class Form1 : public System::Windows::Forms::Form     {     public:         Form1(void)         //...     protected:         void Dispose(Boolean disposing)         //...     private: System::Windows::Forms::TreeView *  tView;     private: System::ComponentModel::Container * components;         void InitializeComponent(void)         {             this->tView = new TreeView();             this->SuspendLayout();             //             // tView             //             this->tView->Dock = System::Windows::Forms::DockStyle::Fill;             this->tView->LabelEdit = true;             this->tView->Name = S"tView";             TreeNode* __mcTemp__1[] = new TreeNode*[2];             TreeNode* __mcTemp__2[] = new TreeNode*[1];             __mcTemp__2[0] = new TreeNode(S"<Holder>");             __mcTemp__1[0] = new TreeNode(S"Root Node A", __mcTemp__2);             TreeNode* _mcTemp_3[] = TreeNode*[1];             __mcTemp__3[0] = new TreeNode(S"<Holder>");             __mcTemp__1[1] = new TreeNode(S"Root Node B", __mcTemp__3);             this->tView->Nodes->AddRange(__mcTemp__1);             this->tView->Size = System::Drawing::Size(200, 450);             this->tView->BeforeExpand +=                    new TreeViewCancelEventHandler(this, tView_BeforeExpand);             //             // Forml             //             this->AutoScaleBaseSize = System::Drawing::Size(6, 15);             this->ClientSize = System::Drawing::Size(200, 450);             this->Controls->Add(this->tView);             this->Name = S"Form1";             this->Text = S"The Tree View";             this->ResumeLayout(false);         }     private:         System::Void tView_BeforeExpand(System::Object *  sender,                       System::Windows::Forms::TreeViewCancelEventArgs *  e)         {             // Already expanded before?             if (e->Node->Nodes->Count > 1)                 return;  // Already expanded             else if (e->Node->Nodes->Count == 1)             {                 if (e->Node->Nodes->Item[0]->Text->Equals(S"<Holder>"))                     e->Node->Nodes->RemoveAt(0); // node ready for expanding                 else                     return; // Already expanded but only one subnode             }             // Randomly expand the Node             Random *rand = new Random();             Int32 rnd = rand->Next(1,5);             for (Int32 i = 0; i < rnd; i++) // Random number of subnodes             {                 TreeNode *stn =                     new TreeNode(String::Format(S"Sub Node {0}", _box(i+1)));                 e->Node->Nodes->Add(stn);                 if (rand->Next(2) == 1) // Has sub subnodes                     stn->Nodes->Add(new TreeNode(S"<Holder>"));             }         }     }; } 
end example

The first steps, as with every other control, are to create the TreeView, configure it using properties, and then add it to the Form.

 this->tView = new TreeView(); this->tView->Dock = System::Windows::Forms::DockStyle::Fill; this->tView->LabelEdit = true; this->tView->Size = System::Drawing::Size(200, 450); this->tView->BeforeExpand +=       new TreeViewCancelEventHandler(this, tView_BeforeExpand); this->Controls->Add(this->tView); 

Because in this example you're building a tree hierarchy on the fly, you need to handle an event that occurs just before the tree node is expanded. The BeforeExpand event meets the bill. The BeforeExpand event takes as a handler TreeViewCancelEventHandler. You might note that the handler has the word "Cancel" in it, which means that it's triggered before the expansion of the node and it's possible to have the code cancel the expansion.

Now that you have a tree you need to add one or more root TreeNodes. You also have to add a holder sub-TreeNode or the expansion box will not be generated. The following code was autogenerated and is hardly pretty:

 TreeNode* __mcTemp_1[] = new TreeNode*[2]; TreeNode* __mcTemp_2[] = new TreeNode*[1]; __mcTemp__2[0] = new TreeNode(S"<Holder>"); __mcTemp__1[0] = new TreeNode(S"Root Node A", __mcTemp__2); this->tView->Nodes->AddRange(__mcTemp__1); 

If I were to code this by hand, the code would look more like this:

 TreeNode *rtnA = new TreeNode(S"Root Node A"); tView->Nodes->Add(rtnA); rtnA->Nodes->Add(new TreeNode(S"<Holder>")); 

At this point, if you were to execute the program (assuming you created a stub for the BeforeExpand event handler) you would get a TreeView with a root TreeNode and a sub-TreeNode. The sub-TreeNode would have the label <Holder>.

The last thing you need to do is replace the holder TreeNode when the expansion box is clicked with its own, randomly generated TreeNode hierarchy. Before you replace the holder TreeNode, you need to make sure that this is the first time the node has been expanded. You do this by looking for the holder TreeNode in the first child (and it should be the only child) of the selected expanded TreeNode. You can find all child nodes in the Nodes property in the Node property. (Look at the code—this is easier to code than explain.)

 if (e->Node->Nodes->Count > 1)     return; // Already expanded else if (e->Node->Nodes->Count == 1) {     if (e->Node->Nodes->Item[0]->Text->Equals(S"<Holder>"))         e->Node->Nodes->RemoveAt(0); // Holder node ready for expanding     else         return; // Already expanded but only one subnode } 

If the node has been expanded previously, just jump out of the handler and let the TreeView re-expand the node with its original tree. If this is the first time the node has been expanded, then remove the holder and randomly create a new sub-TreeNode. The code to create the sub-TreeNode is virtually the same as that of the root TreeNode, except now you add it to the selected to-be-expanded TreeNode.

 Random *rand = new Random(); Int32 rnd = rand->Next(1,5); for (Int32 i = 0; i < rnd; i++) // Random number of subnodes {     TreeNode *stn = new TreeNode(String::Format(S"Sub Node {0}", __box(i+1)));     e->Node->Nodes->Add(stn);     if (rand->Next(2) == 1) // Has sub subnodes         stn->Nodes->Add(new TreeNode(S"<Holder>")); } 

Figure 10-2 shows a sample of what TreeView.exe looks like when you execute it.

click to expand
Figure 10-2: Randomly generated and editable TreeView




Managed C++ and. NET Development
Managed C++ and .NET Development: Visual Studio .NET 2003 Edition
ISBN: 1590590333
EAN: 2147483647
Year: 2005
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net