Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Manning - Windows Forms Programming With CSharp.pdf
Скачиваний:
72
Добавлен:
24.05.2014
Размер:
14.98 Mб
Скачать

This code defines a comparison class for our ListView control. The next step is to hook this into our actual form. This requires that we create an instance of the MyListViewComparer class and assign it as the comparer for our list. Let’s do this first, and then we can handle the ColumnClick event to adjust the comparison settings. This continues our previous steps.

ASSIGN COMPARER TO THE LIST VIEW CONTROL

 

Action

Result

 

 

 

15

Define a private variable to hold

private MyListViewComparer _comparer;

 

the comparer class for the form.

 

 

 

 

16

Create and assign this comparer

protected override void OnLoad(EventArgs e)

 

to the view in the OnLoad

{

 

method.

// Define the list view comparer

 

_comparer = new

 

 

 

Note: This is done at the begin-

MyListViewComparer(listViewMain);

 

ning of this method to ensure

listViewMain.ListViewItemSorter = _comparer;

 

listViewMain.Sorting = SortOrder.Ascending;

 

the comparer exists during con-

 

. . .

 

trol initialization.

}

 

 

 

We now have a comparison class assigned to our view. The ListView control will automatically call this class’s Compare method whenever it must sort the contents of the view. This occurs each time the Sorting property is set to a new value other than None.

We can take advantage of this in our ColumnClick handler to ensure that the list is updated whenever a column is clicked. As we indicated earlier, ColumnClick event handlers receive a ColumnClickEventArgs class as the event parameter. This class defines a Column property containing the index of the selected column in the corresponding list view.

Let’s define this handler to complete our implementation of column sorting.

HANDLE THE COLUMNCLICK EVENT

 

Action

Result

 

 

 

17

Handle the ColumnClick

private void listViewMain_ColumnClick

 

event for the listViewMain

(object sender, System.Windows.

 

control.

Forms.ColumnClickEventArgs e)

 

{

 

 

 

 

 

18

Reset the sorting order for

SortOrder prevOrder = listViewMain.Sorting;

 

the control.

listViewMain.Sorting = SortOrder.None;

 

 

 

19

If the current column was

if (e.Column == _comparer.SortColumn)

 

clicked, then invert the

{

 

existing sort order.

// Switch the sorting order

 

if (prevOrder == SortOrder.Ascending)

 

 

 

 

listViewMain.Sorting = SortOrder.Descending;

 

 

else

 

 

listViewMain.Sorting = SortOrder.Ascending;

 

 

}

 

 

 

462

CHAPTER 14 LIST VIEWS

HANDLE THE COLUMNCLICK EVENT (continued)

 

Action

Result

 

 

 

20

Otherwise, sort the control

else

 

based on the newly selected

{

 

column.

// Define new sort column and reset order

 

_comparer.SortColumn = e.Column;

 

 

 

 

listViewMain.Sorting = SortOrder.Ascending;

 

 

}

 

 

}

 

 

 

Twenty steps in one section. That might be a record. Note how we reset the sort order to None at the beginning of the handler. This ensures that the framework will re-sort the contents when we set the actual sort order a few lines later. Without this reset, the control will not invoke the comparer if the sort order is not a new value, such as when two different columns are clicked one after another.

Compile and run to verify that this sorting works as advertised. Make sure you click the Size column to perform integer-based sorting. An example of such a sort in descending order is shown in figure 14.4.

Figure 14.4

In this graphic, the ListView control in the application is sorted by the Size column.

Alternate sorting mechanisms are possible here as well. Later in the chapter, we will see how to sort date and time values in the control.

TRY IT! The AllowColumnReorder property indicates that the user may rearrange the columns by clicking and dragging them with the mouse. Set this property to true to see how this works. What happens when you sort a column after reordering the columns? Note that the ColumnClick event does not occur during a drag, even though the user must click and then release the column header.

This completes our initial discussion of items and subitems in the list view control. The remaining sections examine some of the more common events normally used with this control.

LISTVIEW COLUMNS

463

14.4SELECTION AND EDITING

So far we have created a ListView control in our application and supported column sorting. In this section we’ll look at some of the events used to interact with specific items in the list. Specifically, we will look at adding the following features:

Viewing the properties associated with a selected item in the list.

Editing the label of an item in our list.

Displaying the Photograph objects in the album when the user double-clicks on an item.

The first two topics are covered in this section. The last topic is related to item activation, which is the subject of section 14.5.

14.4.1SUPPORTING ITEM SELECTION

Like the ListBox control, a list view can support single item or multiple item selection. The MultiSelect property is a boolean property that indicates which type of selection to support. We looked at multiple selection within list boxes in chapter 10, so we will stick with single selection in this chapter.

The SelectedIndices property holds the collection of indices corresponding to the selected items in the list. These index values refer to the position of the items within the collection represented by the Items property. The SelectedItems property holds the collection of selected items directly.

Figure 14.5

The properties window displayed for the leeds.abm album.

To make use of the selected item, we will create a menu item to display the properties associated with the selected album, as shown in figure 14.5. The Click event handler for this menu will open the selected album, display the properties dialog for this

464

CHAPTER 14 LIST VIEWS

ListView control with any changes

album, and update both the album and the made by the user.

The following steps explain how to add both the menu and the Click handler.

Set the version number of the MyAlbumExplorer application to 14.4.

 

 

 

 

ADD A MENU TO DISPLAY ALBUM PROPERTIES

 

 

 

 

 

 

 

 

 

 

Action

Result

 

 

 

 

 

 

1

In the MainForm.cs [Design]

 

 

window, add a Properties menu

 

 

under the Edit menu.

 

 

 

Settings

 

 

 

 

 

 

 

 

 

 

Property

 

Value

 

 

 

(Name)

 

menuProperties

 

 

 

Text

 

&Properties…

 

 

 

 

 

 

 

 

2

Add a Click event handler for

private void menuProperties_Click

 

this menu to display the property

(object sender, System.EventArgs e)

 

dialog for the selected album, if

{

 

if (listViewMain.SelectedItems.Count <= 0)

 

any.

 

 

 

 

 

 

 

return;

 

Note: We separate the display

ListViewItem item

 

logic into a separate DisplayAl-

 

= listViewMain.SelectedItems[0];

 

bumProperties method in case

DisplayAlbumProperties(item);

 

we ever want to call it from other

}

 

portions of our application.

 

 

 

 

 

 

 

3

In order to locate the album file

private void LoadAlbumData(string dir)

 

name in our new method, record

{

 

the album file name in the Tag

. . .

 

foreach (string s in albumFiles)

 

property associated with each list

 

{

 

item in the LoadAlbumData

. . .

 

method.

 

 

 

if (album != null)

 

 

 

 

 

 

{

 

 

 

 

 

 

item.Tag = album.FileName;

 

 

 

 

 

 

item.ImageIndex = MainForm.AlbumIndex;

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

4

Add the

 

 

 

private void DisplayAlbumProperties

 

DisplayAlbumProperties

(ListViewItem item)

 

{

 

method to display the album

 

 

 

dialog.

 

 

 

 

 

 

 

 

 

 

5

Implement this method by

string fileName = item.Tag as string;

 

obtaining the file name for the

 

 

selected album.

 

 

 

 

 

 

 

6

Try to open the album

PhotoAlbum album = null;

 

corresponding to this file name.

if (fileName != null)

 

 

 

 

 

 

album = this.OpenAlbum(fileName);

 

 

 

 

 

 

 

SELECTION AND EDITING

465

ADD A MENU TO DISPLAY ALBUM PROPERTIES (continued)

 

Action

Result

 

 

 

7

Display an error message if the

if (album == null)

 

album could not be opened.

{

 

Note: Here and throughout the

MessageBox.Show("The properties for "

 

+ "this album cannot be displayed.");

 

remainder of the book, we use

return;

 

the simplest form of the Mes-

}

 

 

 

sageBox dialog. Feel free to use

 

 

an alternate form if you prefer.

 

 

See chapter 8 for detailed infor-

 

 

mation on the MessageBox

 

 

class.

 

 

 

 

8

Display the AlbumEditDlg if the

using (AlbumEditDlg dlg

 

album is opened successfully.

= new AlbumEditDlg(album))

 

 

{

 

 

 

9

If any changes are made by the

if (dlg.ShowDialog() == DialogResult.OK)

 

user, save these changes into the

{

 

album file. Catch any errors that

// Save changes made by the user

 

try

 

occur.

 

{

 

 

album.Save();

 

 

}

 

 

catch (Exception)

 

 

{

 

 

MessageBox.Show("Unable to save "

 

 

+ "changes to album.");

 

 

return;

 

 

}

 

 

 

10

Also update any subitem text that

// Update subitem settings

 

might be affected by the user’s

item.SubItems[MainForm.

 

changes.

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

album.Dispose();

 

of the method.

}

 

 

 

We employ the using statement to ensure that our dialog is properly disposed of at the 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 operations, especially if you are familiar with exception-handling mechanisms in languages like C and C++ where it indeed can be an expensive proposition to call try multiple times. In C#, the exception handling is built into the language and the compiler, so checking for exceptions as we do here is not much more expensive than an if statement. The expense comes if an exception actually occurs, since the compiler must

466

CHAPTER 14 LIST VIEWS

then 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 more reason 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 here should not affect our performance very much since we do not normally expect an exception to occur other than when opening an invalid album. We could improve the performance if we kept track of the invalid albums during the OnLoad method, since then we would not need to re-open these albums again here. We will not actually do this here, but it was worth a mention.

The remainder of the previous code is fairly self-explanatory. One other point worth mentioning is our use of the Tag property. This works well in our DisplayAlbumProperties method since all we need to keep track of is the album’s file name. It is also possible here to assign a PhotoAlbum instance to the Tag property rather than a string instance, although this requires extra memory and other resources to maintain the album for each item in memory.

An alternative approach often used to track more complex relationships is to derive a new class from the ListViewItem class. For our application, an excerpt of such 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 within the ListView control. Whenever the PhotoAlbum object for an album is required, a list view item can be downcast to the PhotoAlbumListItem class, where the Album property and other members may be used to manipulate the album.

Listing 14.1 Example deriving a new class from ListViewItem (not our approach)

public class PhotoAlbumListItem : ListViewItem, IDisposable

{

private string _fileName; private PhotoAlbum _album;

PhotoAlbumListItem(string file)

{

_fileName = file; _album = null;

}

public void Dispose()

{

// Dispose implementation

. . .

}

public PhotoAlbum Album

SELECTION AND EDITING

467

{

get

{

if (_album == null)

{

_album = new PhotoAlbum(); _album.Open(_fileName);

}

return _album;

}

}

// Other methods as required

. . .

}

For our purposes the use of a simple string value in the Tag property was sufficient to display the album’s properties dialog. Another feature worth supporting here is the ability to edit item labels.

14.4.2SUPPORTING LABEL EDITS

Editing an item label in place is one of the advantages the ListView class has over ListBox objects. In our application it would be nice if the user could edit the album name in order to rename an album file. This section will show how to support this feature.

Label editing is disabled by default, and turned on by setting the LabelEdit property to true. An actual edit of an item is initiated by the BeginEdit method of the ListViewItem class. The corresponding ListView control receives two events during the editing process. The BeforeLabelEdit event occurs before the edit process begins, while the AfterLabelEdit event occurs when the user completes the edit by pressing the Enter key or clicking outside of the edit area. Event handlers 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 Name menu under the top-level Edit menu, and the second way is by selecting an item and pressing the F2 key. This matches the keyboard shortcut supported by Windows Explorer, so it seems appropriate here.

In a production environment, we would probably handle both events in our application. 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 handler would update the album with a new title and store the album to disk. It would also update the album file on disk with the change.

468

CHAPTER 14 LIST VIEWS