Whenever the PhotoAlbum object for an album is required, a list view item can be downcast to the PhotoAlbumListItem class, where theAlbum property and other members may be used to manipu
Trang 1We employ the using statement to ensure that our dialog is properly disposed of atthe end of the handler Also note how multiple exceptional handling blocks are used
to catch errors that occur You may wonder if it is expensive to perform such tions, especially if you are familiar with exception-handling mechanisms in languageslike C and C++ where it indeed can be an expensive proposition to call try multipletimes In C#, the exception handling is built into the language and the compiler, sochecking for exceptions as we do here is not much more expensive than an if state-ment The expense comes if an exception actually occurs, since the compiler must
opera-7 Display an error message if the
album could not be opened.
Note: Here and throughout the
remainder of the book, we use the simplest form of the Mes- sageBox dialog Feel free to use
an alternate form if you prefer
See chapter 8 for detailed mation on the MessageBox
infor-class.
if (album == null) {
MessageBox.Show("The properties for " + "this album cannot be displayed."); return;
}
8 Display the AlbumEditDlg if the
album is opened successfully.
using (AlbumEditDlg dlg = new AlbumEditDlg(album)) {
9 If any changes are made by the
user, save these changes into the album file Catch any errors that occur.
if (dlg.ShowDialog() == DialogResult.OK) {
// Save changes made by the user try
{ album.Save();
} catch (Exception) {
MessageBox.Show("Unable to save " + "changes to album.");
return;
}
10 Also update any subitem text that
might be affected by the user’s changes.
// Update subitem settings item.SubItems[MainForm.
AlbumTitleColumn].Text = album.Title;
bool hasPwd = (album.Password != null) && (album.Password.Length > 0); item.SubItems[MainForm.
AlbumPwdColumn].Text = (hasPwd ? "y" : "n");
} }
11 Dispose of the album at the end
Trang 2then construct the Exception object, unravel the call stack and clean up any objects
as required, plus locate the appropriate catch block for the particular exception.The fact that exception clean up can impact a program’s performance is one morereason to ensure that you throw exceptions only for truly exceptional conditions.Common problems or situations should be handled through the use of an error code
As a case in point, this is one reason why file-related read and write methods in the.NET Framework do not raise an exception when the end of a file is reached.Back to our code, this discussion tells us that our use of try and catch hereshould not affect our performance very much since we do not normally expect anexception to occur other than when opening an invalid album We could improve theperformance if we kept track of the invalid albums during the OnLoad method, sincethen we would not need to re-open these albums again here We will not actually dothis here, but it was worth a mention
The remainder of the previous code is fairly self-explanatory One other pointworth mentioning is our use of the Tag property This works well in our Display- AlbumProperties method since all we need to keep track of is the album’s filename It is also possible here to assign a PhotoAlbum instance to the Tag propertyrather than a string instance, although this requires extra memory and otherresources to maintain the album for each item in memory
An alternative approach often used to track more complex relationships is toderive a new class from the ListViewItem class For our application, an excerpt ofsuch a class might look something like the code shown in listing 14.1 Since this class
is a ListViewItem object, instances of it can be assigned to and manipulated withinthe ListView control Whenever the PhotoAlbum object for an album is required,
a list view item can be downcast to the PhotoAlbumListItem class, where theAlbum property and other members may be used to manipulate the album
public class PhotoAlbumListItem : ListViewItem, IDisposable
{
private string _fileName;
private PhotoAlbum _album;
public PhotoAlbum Album
Listing 14.1 Example deriving a new class from ListViewItem (not our approach)
Trang 314.4.2 SUPPORTING LABEL EDITS
Editing an item label in place is one of the advantages the ListView class has overListBox objects In our application it would be nice if the user could edit the albumname in order to rename an album file This section will show how to support thisfeature
Label editing is disabled by default, and turned on by setting the LabelEditproperty to true An actual edit of an item is initiated by the BeginEdit method
of the ListViewItem class The corresponding ListView control receives twoevents during the editing process The BeforeLabelEdit event occurs before theedit process begins, while the AfterLabelEdit event occurs when the user com-pletes the edit by pressing the Enter key or clicking outside of the edit area Event han-dlers for both events receive the LabelEditEventArgs class as their event handler.See NET Table 14.7 for an overview of this class
We will allow an item to be edited in two ways The first way is through a Namemenu under the top-level Edit menu, and the second way is by selecting an item andpressing the F2 key This matches the keyboard shortcut supported by WindowsExplorer, so it seems appropriate here
In a production environment, we would probably handle both events in ourapplication In the BeginLabelEdit event handler we would make sure the album
is valid and can be successfully opened This provides some assurance that the edit will
be successful before the user begins typing The AfterLabelEdit event handlerwould update the album with a new title and store the album to disk It would alsoupdate the album file on disk with the change
Trang 4Since we are not in a production environment, we will take the easy way out and onlyhandle the AfterLabelEdit event This means a user may edit an album only tofind that he or she cannot save his changes, which is not the best interface from ausability perspective.
The code changes required are given in the following steps:
.NET Table 14.7 LabelEditEventArgs class
The LabelEditEventArgs class represents the event arguments received by belEdit and AfterLabelEdit event handlers for the ListView class This class is part of the System.Windows.Forms namespace, and inherits from the System.EventArgs class.
BeforeLa-Public Properties
CancelEdit Gets or sets whether the edit operation should be cancelled
This property can be set both before and after the item is edited.
Item Gets the zero-based index into the list view’s Items collection
of the ListViewItem to be edited.
Label Gets the new text to assign to the label of the indicated item.
I NITIATE LABEL EDITING
1 In the MainForm.cs [Design]
window, set the LabelEdit
property of the ListView control
to true
Item labels in the list view may now be edited.
2 Add a Name menu to the top of
the Edit menu.
3 Add a Click event handler for
this menu.
private void menuEditLabel_Click (object sender, System.EventArgs e) {
4 Within this handler, if an item is
selected, edit the item.
if (listViewMain.SelectedItems.Count == 1) listViewMain.SelectedItems[0].BeginEdit(); }
Note: This code only edits the label if a single item
is selected While we do not permit multiple items
to be selected in our ListView control, this code establishes an appropriate behavior in case such selection is ever permitted in the future.
5 Add a KeyDown event handler for
the ListView control.
private void listViewMain_KeyDown (object sender, System.Windows.
Forms.KeyEventArgs e) {
Settings Property Value
(Name) menuEditLabel Text &Name
Trang 5That’s all it takes to begin an edit The actual work of interacting with the user is dled by the framework When the user is finished, we can pick up the result in anAfterLabelEdit event handler There is also a BeforeLabelEdit event that isuseful for selectively permitting an edit or altering an item before the edit begins Forour purposes, the AfterLabelEdit event will suffice.
han-6 If the F2 key is pressed and an
item is selected, edit the item.
if (e.KeyCode == Keys.F2) {
if (listViewMain.SelectedItems.Count == 1) {
listViewMain.SelectedItems[0].
BeginEdit();
e.Handled = true;
} } }
8 If the user cancelled the edit,
then we are finished.
Note: For example, if the
user presses the Esc key ing editing, this handler is invoked with a null label.
if (e.Label == null) {
// Edit cancelled by the user e.CancelEdit = true;
return;
}
9 In this handler, locate the item
to be edited.
ListViewItem item = listViewMain.Items[e.Item];
10 Update the album name, and
cancel the edit if an error occurs.
Note: Once again we
sepa-rate the logic to opesepa-rate on our album into a separate method.
if (UpdateAlbumName(e.Label, item) == false) e.CancelEdit = true;
}
Trang 6This code uses some methods from the Path and File classes to manipulate the filename strings and rename the album file Our application now supports displayingalbum properties and editing of album labels The next topic of discussion is itemactivation.
11 Add the UpdateAlbumName
method to update the title of the album.
private bool UpdateAlbumName (string newName, ListViewItem item) {
string fileName = item.Tag as string;
string newFileName = RenameFile(fileName, newName, ".abm");
if (newFileName == null) {
12 Implement the RenameFile
method to construct the new name for the file.
private string RenameFile (string origFile, string newBase, string ext) {
string fileName = Path.
GetDirectoryName(origFile) + "\\" + newBase; string newFile = Path.ChangeExtension(fileName, ext);
13 Rename the file using the
Move method in the File
class.
try { File.Move(origFile, newFile);
// An error occurred return null;
} }
b Rename the file using a vate method that returns the new name.
pri-c Inform the user if the file could not be renamed.
d Otherwise, update the Tag
property with the new name.
How-to
a Use the ryName method to retrieve the directory for the file.
GetDirecto-b Use the ChangeExtension
method to ensure the file has the correct extension
Trang 714.5 I TEM ACTIVATION
As you might expect, item activation is the means by which an item is displayed orotherwise activated by the control Normally, activation is just a fancy way to saydouble-click In our ListBox class in chapter 10, we activated an item in the list byhandling the DoubleClick event and displaying the properties dialog associatedwith the item Such behavior is activation
The reason for the fancy term is that the ListView class allows activation otherthan a double-click to be supported The Activation property determines the type
of activation supported, based on the ItemActivation enumeration The possiblevalues for this enumeration are shown in NET Table 14.8 Note that the OneClickstyle is similar to an HTML link in a Web browser In our program, we will stick withthe standard activation
Regardless of how items are activated, an ItemActivate event occurs whenever an item
is activated The event handler for this event receives a standard System.EventArgsparameter, so the activated item is obtained from the SelectedItems collection.The activation behavior for our MyAlbumExplorer application will display thePhotographs in the selected album This is a rather complicated change, since thecolumns and list item behavior must now accommodate the display of both albumsand photos here The fact that we were careful to separate much of the album logicinto individual methods along the way will help us keep our code straight Figure 14.6shows our application with photographs displayed in the ListView control Thesephotographs are sorted by the date each photo was taken The icon used here mightnot be your first choice for a photograph icon, but it will suffice for our purposes Ifyou find another icon you prefer, or are feeling creative, you can use an alternate icon
in your application
.NET Table 14.8 ItemActivation enumeration
The ItemActivation enumeration specifies the type of activation supported by a control This enumeration is part of the System.Windows.Forms namespace.
Enumeration Values
OneClick A single click activates an item The cursor appears as a
hand pointer, and the item text changes color as the mouse pointer passes over the item.
Standard A double-click activates an item.
TwoClick A double-click activates an item, plus the item text changes
color as the mouse pointer passes over the item.
Trang 814.5.1 HANDLING ITEM ACTIVATION
The ultimate goal here is to display either the list of albums or the list of photos in analbum within our ListView control To do this, we must keep track of whetheralbums or photographs are currently shown in the view, and whether the PhotoAl- bum object corresponds to the view when photographs are displayed The followingsteps create private fields in our Form to track this information, and also implement
an event handler for the ItemActivate event Once these are available, we will look
at the additional steps required to fully support activation
Set the version number of the MyAlbumExplorer application to 14.5.
Figure 14.6
In this detailed view
of Photographs, note how three dots auto- matically appear when the text length exceeds the width of the column.
H ANDLE THE I TEM A CTIVATE EVENT FOR THE LIST VIEW
1 Add private fields to track the
current ListView control contents
in the MainForm.cs code window.
private bool _albumsShown = true;
private PhotoAlbum _album = null;
2 Add an ItemActivate event
handler to the ListView control.
private void listViewMain_ItemActivate (object sender, System.EventArgs e) {
3 If albums are currently shown and
an item is selected, then open the album corresponding to the selected item.
if (_albumsShown &&
listViewMain.SelectedItems.Count > 0) {
ListViewItem item = listViewMain.SelectedItems[0]; string fileName = item.Tag as string;
// Open the album for this item PhotoAlbum album = null;
if (fileName != null) album = OpenAlbum(fileName);
if (album == null) {
MessageBox.Show("The photographs for " + "this album cannot be displayed."); return;
}
4 If the album loads successfully,
load the album’s photographs into the list view.
// Switch to a photograph view LoadPhotoData(album);
} }
Trang 9Of course, we need to implement the LoadPhotoData method that appears in thiscode This method should set up the view to display photographs, including anappropriate set of columns, and reset the list of items to hold the set of photographs.Once this is done, there is also the support we created for our albums that must now
be implemented for photographs To help us keep our facts straight, let’s make a list
of the tasks we need to perform here
•Define new columns for displaying photographs
•Populate the ListView control with the photographs in the album
•Support column sorting
•Display the photo properties dialog
•Support item editing on photographs
•Allow the user to select the desired view, albums or photos
We will cover each of these topics in a separate section, in the same order as shown here
14.5.2 DEFINING NEW COLUMNS
As you’ll recall, we defined the list of columns for our control using the Header Collection Editor dialog in Visual Studio NET Now that we need to displaydifferent columns depending on what is displayed, this method no longer makessense Instead, we will create the columns programmatically in the LoadAlbumDatamethod Our new LoadPhotoData method we have yet to implement will definethe columns for displaying photographs
Column-The easiest way to add columns to a ListView control programmatically isthrough the Columns property The following steps remove the columns we created
in Visual Studio and will add them via the LoadAlbumData method
C REATE THE ALBUM COLUMNS PROGRAMMATICALLY
1 In the MainForm.cs [Design]
window, remove the four columns currently defined for the Columns property.
How-to
Use the ColumnHeader Collection Editor dialog.
Note: This is not strictly required since we clear the
contents of the list, including the column tions, as part of the next step Reducing unneces- sary clutter in your code is always a good idea, so performing this step makes sense.
defini-2 Modify the LoadAlbumData
method to initially clear the existing contents of the control.
private void LoadAlbumData(string dir) {
_album.Dispose();
Trang 10The Columns property refers to a ColumnHeaderCollection object This tion class includes an Add method that creates a new column for the control Oneversion of this method simply accepts a ColumnHeader class instance Our code uses
collec-a slightly more convenient form, with the following signcollec-ature:
void Add(string columnText, int width, HorizontalAlignment align);
We can use this same method to add columns when photographs are displayed Thefollowing table summarizes the columns we will use for this purpose
The following table defines constants for our new albums as well as the beginnings ofour LoadPhotoData implementation This table continues our previous steps
4 Define the columns for the
control before the album items are loaded.
How-to
Use the Add method available through the Columns property for the control.
// Define the columns listViewMain.Columns.Add("Name",
Columns for displaying photographs
0 Caption The caption for this photo.
1 Taken The date the photograph was taken.
2 Photographer The photographer for this photo.
3 File Name The fully qualified image file name.
C REATE THE PHOTO COLUMNS PROGRAMMATICALLY
5 In the MainForm.cs code
window, create constants to hold the positions of the columns when photographs are displayed.
private const int PhotoCaptionColumn = 0; private const int PhotoDateTakenColumn = 1; private const int PhotoPhotographerColumn = 2; private const int PhotoFileNameColumn = 3;
6 Add a private LoadPhotoData
method.
private void LoadPhotoData(PhotoAlbum album) {
Trang 11This code defines the four columns required to display photographs We are nowready to populate the list view with the photos from a selected album.
14.5.3 POPULATING THE LISTVIEW
This section completes the implementation of the LoadPhotoData method by ating the ListViewItem objects for the control The following steps add an item toour control for each Photograph in the album, and define the subitems associatedwith each item
cre-In the course of implementing support for photographs, we will need the tograph object itself We had a similar requirement for PhotoAlbum objects, andwere able to use the file name setting to load the album into memory While the filename is available for our photos as well, our PhotoAlbum class does not provide agood mechanism for locating a Photograph in an album based on the file name.The most convenient means for locating a specific photograph is based on theindex What we need, then, is a way to look up the index This value will be stored
Pho-in the Tag property for our list view item, in a manner similar to how we used thisproperty for photo albums
Of course, an alternate technique here would be to derive a new class from theListView class as we discussed at the end of section 14.4 The Tag property is finefor our purposes In your application, you can use whichever technique seems appro-priate for your current and expected requirements
7 To implement this method, clear
the list and set the album fields.
an album other than the default _album used in this chapter.
8 Define the columns required for
Trang 12This code initializes the control with the contents of the open album Note in ular how we define the Tag property to hold the integer index Since the Tag prop-erty holds an object instance, this line boxes the integer value in order to store it as
partic-a reference type Boxing wpartic-as mentioned in chpartic-apter 5, partic-and is discussed in detpartic-ail inappendix A
You can compile and run this code if you like Double-click on an album to vate it and display the contained photographs Most of the support for photographs
acti-is still macti-issing, so you’ll find it rather easy to cause an error
The remainder of this section implements the support required for both albumsand photographs to coexist in our ListView control We begin with column sorting
14.5.4 SORTING A COLUMN (AGAIN)
Our users will want to sort the columns for both the album and photograph display,
so we need to make some changes in our MyListViewComparer class to enable thissupport Of key importance is the ability to tell which type of object we are compar-ing When comparing photos, we also need to know the PhotoAlbum they come
A DD THE PHOTOS IN AN ALBUM TO THE LIST
1 Modify the LoadPhotoData
method to simply return if the given album is null or empty.
private void LoadPhotoData(PhotoAlbum album) {
// Handle null or empty album
if (album == null || album.Count == 0) return;
2 Iterate over the photographs in
the album.
How-to
Use a for loop to permit access
to the index values.
// Load the photo items for (int i = 0; i < album.Count; i++) {
3 Create a new ListViewItem for
each photo.
Photograph photo = album[i];
ListViewItem item = new ListViewItem();
4 Assign the caption as the item
label, and the image list index to our small photograph image.
6 Add the new item to the control listViewMain.Items.Add(item);
} }
Trang 13from We can handle both requirements through a private album field When thealbum is null, we are comparing PhotoAlbum objects When an album is assigned,
we are comparing Photograph instances
Let’s add this field and update our Compare method to make use of this value
Now all we have to do is implement the ComparePhotos method to compare twoPhotograph items Much of this will be similar to the CompareAlbums method.The one difference is when we need to compare items using the Taken column Thiscolumn holds a date value, so a string comparison is not appropriate It turns out theDateTime structure provides a Compare method for just this purpose
We can use this method in the ComparePhotos method to our comparer class
I DENTIFY THE TYPE OF OBJECT TO COMPARE
1 Locate the MyListViewComparer
class defined in the MainForm.cs source file.
private class MyListViewComparer : IComparer
{
2 Add a PhotoAlbum field and
corresponding property.
PhotoAlbum _album = null;
public PhotoAlbum CurrentAlbum {
get { return _album; } set { _album = value; } }
3 Use this property to identify which
object to compare in the Compare
method.
Note: Since the label for both types
of items is a string, the existing code for the non- Details case will work for both objects.
public int Compare(object a, object b) {
// Handle the nonDetails case
if (ListView.View != View.Details) {
return CaseInsensitiveComparer Default.Compare(
item1.Text, item2.Text); }
if (CurrentAlbum == null) return CompareAlbums(item1, item2); else
return ComparePhotos(item1, item2); }
}
Trang 14The last change required for column sorting is to update the CurrentAlbum erty for our comparer field whenever the contents of the ListView control arerefreshed This ensures that our Compare implementation performs the proper com-parison based on the contents of the control.
prop-I MPLEMENT METHOD TO COMPARE TWO PHOTO ITEMS
4 Add a new ComparePhotos
method to the Comparer class.
public int ComparePhotos (ListViewItem item1, ListViewItem item2) {
ListViewItem.ListViewSubItem sub1;
ListViewItem.ListViewSubItem sub2;
switch (SortColumn) {
5 For the columns containing text
strings, use the default comparer provided by the
CaseInsensitiveComparer
class.
case MainForm.PhotoCaptionColumn:
case MainForm.PhotoPhotographerColumn: case MainForm.PhotoFileNameColumn: sub1 = item1.SubItems[SortColumn]; sub2 = item2.SubItems[SortColumn]; return CaseInsensitiveComparer.
Default.Compare(sub1.Text, sub2.Text);
6 For the Taken column, determine
the index into the album for each photo.
case MainForm.PhotoDateTakenColumn: // Find the indices into the album int index1 = (int)item1.Tag;
int index2 = (int)item2.Tag;
7 Then determine the
corresponding DateTime value for each photo
// Look up the dates for each photo DateTime date1
= CurrentAlbum[index1].DateTaken; DateTime date2
= CurrentAlbum[index2].DateTaken;
8 Use the Compare method
provided by the DateTime
structure to calculate the result.
return DateTime.Compare(date1, date2);
9 Throw an exception if an
unrecognized column is provided.
default:
throw new IndexOutOfRangeException( "unrecognized column index"); }
}
U PDATE THE C URRENT A LBUM PROPERTY WHEN REQUIRED
10 Update the LoadPhotoData
method to assign the current album to the comparer.
private void LoadPhotoData(PhotoAlbum album) {
_albumsShown = false;
_album = album;
_comparer.CurrentAlbum = _album;
}
Trang 15Our application can now sort both photographs and albums Once again you cancompile and run the program if you are careful not to use any photograph functional-ity we have not yet implemented Our next task is the Properties dialog.
14.5.5 UPDATING THE PROPERTIES MENU
You may think we are moving through this code rather quickly, and you would beright While these changes are required as a result of our defined item activationbehavior, there are not a lot of new concepts to cover This is especially true here As aresult, we will simply run through the steps in the following table and then move on
to our final topic of editing the item label
11 Update the LoadAlbumData
method to assign a null album
U PDATE THE C LICK HANDLER FOR THE P ROPERTIES MENU
1 In the MainForm.cs code
window, update the Click event handler for the Properties menu
to call a new Properties method when photographs are shown in the control.
private void menuProperties_Click (object sender, System.EventArgs e) {
if (listViewMain.SelectedItems.Count <= 0) return;
ListViewItem item = listViewMain.SelectedItems[0];
if (this._albumsShown) DisplayAlbumProperties(item);
else DisplayPhotoProperties(item);
3 Determine the index of the
selected photo in the current album.
Note: While the is keyword works fine with integer types, the as keyword can only be used with reference types.
if (!(item.Tag is int)) return;
int index = (int)item.Tag;
4 Assign the current position in the
album to this index.
_album.CurrentPosition = index;
Trang 1614.5.6 UPDATING LABEL EDITING
Updating the label for our photographs again does not use any new constructs, so wewill hurry through this code as well As you’ll recall, the caption for each photograph
is displayed as the item label We should note that the menuEditLabel_Click dler does not require any changes, since this simply initiates the edit The After- LabelEdit event handler is where the new value is processed
han-5 Display the properties dialog for
the photo.
using (PhotoEditDlg dlg = new PhotoEditDlg(_album)) {
if (dlg.ShowDialog() == DialogResult.OK) {
6 If any changes were made in the
dialog, save the entire album to disk.
Note: As you’ll recall, we permit
multiple photographs to be ified in the dialog As a result, the entire album must be saved and reloaded into the control to pick up any changes.
// Save any changes made try
{ _album.Save(_album.FileName);
} catch (Exception) {
MessageBox.Show("Unable to save " + "changes to photos in album."); }
7 Reload the entire album into the
control to pick up the new changes.
// Update the list with any new settings LoadPhotoData(_album);
} } }
U PDATE THE A FTER L ABEL E DIT EVENT HANDLER
1 In the MainForm.cs code
window, modify the LabelEdit event handler to call
if (e.Label == null) {
// Edit cancelled by the user e.CancelEdit = true;
return;
}
ListViewItem item = listViewMain.Items[e.Item];
if (this._albumsShown) e.CancelEdit = !UpdateAlbumName(e.Label, item); else
e.CancelEdit = !UpdatePhotoCaption(e.Label, item); }
Trang 17One further change we can make here is to alter the text displayed in the ing menu item This will provide visual feedback to the user on which property theyare actually changing, especially when the Details view is not displayed.
correspond-2 Add the UpdatePhotoCaption
method to the MainForm class.
private bool UpdatePhotoCaption (string caption, ListViewItem item) {
3 Make sure the new caption is
int index = (int)item.Tag;
5 Set the photograph’s caption to
the new value.
_album[index].Caption = caption;
6 Save the album to store the new
value.
try { _album.Save(_album.FileName);
} catch (Exception) {
MessageBox.Show("Unable to save new "
+ "caption to album file.");
7 In the MainForm.cs [Design]
window, add a Popup event handler for the menuEditLabel
menu.
private void menuEdit_Popup (object sender, System.EventArgs e) {
8 Enable the contained menus
only if a single item is selected
in the view.
menuEditLabel.Enabled = (listViewMain.SelectedItems.Count == 1); menuProperties.Enabled
= (listViewMain.SelectedItems.Count == 1);
9 Set the menu’s text to “Name”
or “Caption” depending on which type of object is displayed
in the list.
if (this._albumsShown) menuEditLabel.Text = "&Name";
else menuEditLabel.Text = "&Caption";
}
Trang 1814.5.7 REDISPLAYING THE ALBUMS
As a final change, we need to give our user the opportunity to redisplay the albumview We may as well provide a menu to display the photo view as well, as an alterna-tive to double-clicking on the album
This completes our discussion of the ListView class In this chapter we discussedlist views in detail, and created a new MyAlbumExplorer interface to display the col-lection of albums available in our default album directory We supported all four pos-sible views available in a ListView control, and provided support for column
A LLOW USER SELECTION OF THE KIND OF OBJECT TO DISPLAY
1 In the MainForm.cs [Design]
window, add three menu items
to the bottom of the View menu.
2 Add a Click handler for the
Albums menu.
private void menuAlbums_Click (object sender, System.EventArgs e) {
// Display albums in the list
if (!_albumsShown) {
LoadAlbumData(PhotoAlbum.DefaultDir); }
}
3 Add a Click handler for the
Photos menu.
Note: This is the same as
acti-vating an album item.
private void menuPhotos_Click (object sender, System.EventArgs e) {
// Activate the selected album listViewMain_ItemActivate(sender, e);
}
4 Update the Popup handler for
the View menu to enable or disable the Photos menu as appropriate.
private void menuView_Popup (object sender, System.EventArgs e) {
View v = listViewMain.View;
.
if (_albumsShown && listViewMain.
SelectedItems.Count > 0) menuPhotos.Enabled = true;
else menuPhotos.Enabled = false;
}
Settings Menu Property Value
-Albums (Name) menuAlbums
Text &Albums Photos (Name) menuPhotos
Text &Photos
Trang 19sorting, item selection, and label editing We finished by implementing this samesupport for the photos in an album, so that our application can display albums orphotographs in the control.
Along the way we looked at a number of classes provided to support this control,most notably the ListViewItem, ListViewItem.ListViewSubItem, and Col- umnHeader classes We also examined the IComparer interface as a way to definehow two objects should be compared, and implemented a class supporting this inter-face in order to sort the columns in our detailed view of the list
The next chapter looks at a close cousin to the ListView class, namely theTreeView control.
Trang 20C H A P T E R 1 5
Tree views
15.1 Tree view basics 486
15.2 The TreeView class 486
15.3 Dynamic tree nodes 497
15.4 Node selection 505 15.5 Fun with tree views 513 15.6 Recap 524
In the previous chapter we created the MyAlbumExplorer application incorporating aListView control This program presents the default set of photo albums availableand the collection of photographs contained within these albums
In this chapter we extend this program to include a TreeView control in order
to present a more traditional explorer-style interface Specific topics we will cover inthis chapter include the following:
•Exploring the TreeView class
•Using the Splitter control to divide a container
•Populating a tree with the TreeNode class, both in Visual Studio and grammatically
pro-•Selecting nodes in a tree
•Editing the labels for a tree
•Integrating a ListView and TreeView control into an application
As we did for list views, we begin this chapter with a general discussion of tree viewsand a discussion of the terms and classes used for this control
Trang 2115.1 T REE VIEW BASICS
The TreeView class is a close cousin of the ListView class List views display a lection as a list, while tree views display collections as a tree Each item in a tree view
col-is called a tree node, or just a node Tree nodes can contain additional nodes, called
child nodes, to arbitrary levels in order to represent a hierarchy of objects in a single
control Various elements of a TreeView control are illustrated in figure 15.1
The explorer-style interface shown in the figure and used by other applications such
as Windows Explorer is a common use of the TreeView and ListView classes Inthis chapter we build such an interface by extending the MyAlbumExplorer projectconstructed in chapter 14
The TreeView class is summarized in.NET Table 15.1 Like the ListView class,this class inherits directly from the Control class, and provides an extensive list ofmembers for manipulating the objects displayed by the tree
b
c
d e
An icon taken from an
instance is associated with each node ImageList
b
An alternate icon from the
I can be displayed when a node is selected mageList
c
The class represents a single element, or node,
d
Figure 15.1 The TreeView control automatically shows the entire label in a tool tip style mat when the mouse hovers over a node, as was done for the “From the Walking Path” entry
for-in this figure.
Trang 22A TreeView object is created much like any other control in Visual Studio NET:you simply drag the control onto the form In our MyAlbumExplorer application, wealready have a ListView on our form, so it looks like all we need to add is a treeview in order to support the interface shown in figure 15.2.
.NET Table 15.1 TreeView class
The TreeView class represents a control that displays a collection of labeled items as a style hierarchy Typically an icon is displayed for each item in the collection to provide a graphi-
tree-cal indication of the nature or purpose of the item Items in the tree are referred to as nodes,
and each node is represented by a TreeNode class instance This class is part of the tem.Windows.Forms namespace, and inherits from the Control class See NET Table 4.1 on page 104 for a list of members inherited by this class.
Sys-Public Properties
CheckBoxes Gets or sets whether check boxes are displayed next to each
node in the tree The default is false HideSelection Gets or sets whether a selected node remains highlighted
even when the control does not have focus.
ImageIndex Gets or sets an index into the tree’s image list of the default
image to display by a tree node.
ImageList Gets or sets an ImageList to associate with this control LabelEdit Gets or sets whether node labels can be edited.
Nodes Gets the collection of TreeNode objects assigned to the
control.
PathSeparator Gets or sets the delimiter used for a tree node path, and in
particular the TreeNode.FullPath property.
SelectedNode Gets or sets the selected tree node.
ShowPlusMinus Gets or sets whether to indicate the expansion state of
parent tree nodes by drawing a plus ‘+’ or minus ‘-‘ sign next
to each node The default is true Sorted Gets or sets whether the tree nodes are sorted alphabetically
based on their label text.
TopNode Gets the tree node currently displayed at the top of the tree
view control.
Public Methods
CollapseAll Collapses all the tree nodes so that no child nodes are visible GetNodeAt Retrieves the tree node at the specified location in pixels
within the control.
GetNodeCount Returns the number of top-level nodes in the tree, or the total
number of nodes in the entire tree.
Public Events
AfterExpand Occurs after a tree node is expanded.
AfterLabelEdit Occurs after a tree node label is edited.
BeforeCollapse Occurs before a tree node is collapsed.
BeforeSelect Occurs before a tree node is selected.
ItemDrag Occurs when an item is dragged in the tree view.
Trang 2315.2.1 CREATING A TREE VIEW
There is, in fact, an issue here with how a tree view and list view are arranged on theform The gray vertical bar in the middle of our interface is a special control called a
splitter to separate the two controls We will talk about splitters in a moment First,
let’s add a TreeView to our form and see what happens
Set the version number for the MyAlbumExplorer application to 15.2.
1 In the MainForm.cs [Design]
window, drag a TreeView
control onto the form and set its properties.
2 Bring the list view to the top of
the z-order.
How-to: Right-click the View control and select the Bring to Front option.
List-3 Set the HideSelection
property in both the ListView
and the TreeView to false
Note: This will highlight the selected object in both
controls even when these controls do not have the focus.
Settings Property Value
(Name) treeViewMain
Trang 24So far, so good We have a TreeView on the left and a ListView on the right If yourun this program, you will see the interface shown in figure 15.3 The tree control is
on the left, and the list view on the right We have not added any nodes to our tree yet,but the photo albums from the default album directory appear in the list view as wasdiscussed in chapter 14 Note here that the ListView must be brought to the top ofthe z-order in step 2 to ensure it is not obscured by the TreeView control
TRY IT! Send the ListViewcontrol to the bottom of the z-order using the Send to
Back menu item Run the application to see what happens Because the
TreeViewis docked first, and then the ListViewfills the remaining area
You will note that if you resize the form in figure 15.3, the size of the tree view doesnot change In addition, the line between the two controls cannot be dragged as is thecase in other explorer-style programs such as Windows Explorer
We can enable this behavior by adding a Splitter control to our form We will
do this next, after which we will look at populating our tree with some items
15.2.2 USING THE SPLITTER CLASS
As a short aside to our discussion on tree views, the Splitter class is useful for ing all or part of a form or other container into two resizable sections While somereaders may not consider a splitter control to be an advanced concept, it fits nicely intoour discussion of the MyAlbumExplorer application, so this is where it goes
divid-Typically a splitter provides separate areas for two collection or container trols, normally one of the ListBox, ListView, TreeView, or Panel controls Anoverview of the Splitter class is given in NET Table 15.2
con-A splitter can appear horizontally or vertically When docked to the top or bottom
of a container, it is a horizontal splitter; when docked to the left or right, it is a vertical
Figure 15.3 The ListView control here works as before, just within a smaller area.
Trang 25splitter We will create a vertical splitter in our MyAlbumExplorer application, and
then discuss how to turn this into a horizontal splitter
The steps to create a vertical splitter are detailed in the following table
Compile the application to see the splitter in action Figure 15.4 shows our windowwith the splitter dragged far to the right The MinExtra property setting ensures thatthe items in the ListView cannot be obscured by dragging the splitter all the way tothe right side of the window The ListView can still disappear when the form is
.NET Table 15.2 Splitter class
The Splitter class represents a control that divides a container into two sections Each tion contains a docked control, and the splitter permits the user to resize each section at run- time This class is part of the System.Windows.Forms namespace, and inherits from the
sec-Control class See NET Table 4.1 on page 104 for a list of members inherited by this class.
Public Properties
BorderStyle Gets or sets the border style for the control.
Cursor (overridden from
Control )
Gets or sets the cursor for the control A horizontal splitter uses the HSplit cursor by default, while a vertical splitter uses the VSplit cursor by default.
Dock (overridden from
Control )
Gets or sets the docking style A splitter must be docked
to one side of its container This setting determines the orientation, either vertical or horizontal, of the splitter The
None and Fill values are not permitted The position of the splitter in the z-order determines the location of the splitter within its container.
MinExtra Gets or sets the minimum size for the remainder of the
container, which is occupied by the subsequent control in the docking order.
MinSize Gets or sets the minimum size for the target of the
splitter, which is the previous control in the docking order SplitPosition Gets or sets the position of the splitter, in pixels.
Public Events
SplitterMoved Occurs when the splitter has moved.
SplitterMoving Occurs when the splitter is moving.
A DD A SPLITTER CONTROL
1 In the MainForm.cs [Design]
window, drag a Splitter object onto the form.
2 Set the MinExtra property for
the splitter to 100.
Note: This ensures that the large icons in our View will always be visible.
List-3 Move the ListView control to
the front of the z-order.
The window looks much the same as before The difference occurs when the application is executed.