Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# ПІДРУЧНИКИ / c# / Manning - Windows.forms.programming.with.c#.pdf
Скачиваний:
108
Добавлен:
12.02.2016
Размер:
14.98 Mб
Скачать

CREATE A CLICK EVENT HANDLER FOR THE NEW MENU

 

Action

Result

 

 

 

1

In the MainForm.cs [Design] window,

private void menuNew_Click

 

add a Click event handler for the

(object sender, System.EventArgs e)

 

New menu.

{

 

 

 

 

 

2

In this handler, dispose of the existing

if (_album != null)

 

album and create a new one.

_album.Dispose();

 

Note: This really is poor design,

_album = new PhotoAlbum();

 

 

 

since we throw away any changes to

 

 

the existing album. We will fix this in

 

 

chapter 8 when we discuss the Mes-

 

 

sageBox class.

 

 

 

 

3

Initialize the application title bar.

// Set the application title bar

 

 

SetTitleBar();

 

 

 

4

Invalidate the current window.

this.Invalidate();

 

 

}

 

 

 

5

Add a call to this method in the

menuNew_Click(this, EventArgs.Empty);

 

MainForm constructor.

Note: The static EventArgs.Empty property

 

 

 

 

provides an empty EventArgs instance for

 

 

use when calling event handlers from your

 

 

code.

 

 

 

6

Remove the code to set the title bar

The initial title bar is now set as part of the

 

from the MainForm constructor.

menuNew_Click method.

 

 

 

We have made a few changes to our MainForm constructor here. To make sure we are all on the same page (so to speak), your constructor in Visual Studio should now look something like the following:

public MainForm()

{

//

//Required for Windows Form Designer support

InitializeComponent();

//Additional Form initialization DefineContextMenu(); menuNew_Click(this, EventArgs.Empty);

}

With this infrastructure in place, we can turn our attention to the methods required in the PhotoAlbum class.

6.6SAVE FILE DIALOGS

So far we have used the MyPhotoAlbum library to support the creation and manipulation of an album in memory. At this point, we would like to preserve this album by storing it on disk. In this section we will handle the Save menu item to do just this. In

SAVE FILE DIALOGS

181

the next section we will implement an Open menu handler to allow such an album to be reloaded and used at a later time.

We have already seen how the OpenFileDialog class is used to locate image files. As you might expect, .NET provides a SaveFileDialog class to store information to a file. A summary of this class is shown in .NET Table 6.5.

.NET Table 6.5 SaveFileDialog class

The SaveFileDialog class represents a common file dialog box for saving a file to disk, and is part of the System.Windows.Forms namespace. This class inherits from the FileDialog class. See the FileDialog class description in .NET Table 1.2 on page 24 for a list of inherited members.

 

CreatePrompt

Gets or sets whether the dialog should prompt

 

 

the user for permission to create a specified file

 

 

that does not exist. The default is false (do not

Public Properties

 

prompt).

OverwritePrompt

Gets or sets whether the dialog should prompt

 

 

 

the user for permission to overwrite a specified

 

 

file that already exists. The default is true

 

 

(always prompt).

 

 

 

Public Methods

OpenFile

Returns a Stream object with read/write

 

 

permission of the file selected by the user.

 

 

 

To save an album to disk, we need to implement two types of methods. The first is a Click event handler for both the Save and Save As menus. These handlers will use the SaveFileDialog class to allow a file to be selected. Second is a PhotoAlbum.Save method to write the album information into the selected file. Separating the user interface portion, in this case the file selection, from the data manipulation portion, here the actual file writes, is a common design technique that allows us to change either aspect of the task without overly affecting the other. As we shall see in future chapters, changes to how the data is stored by the PhotoAlbum.Save method will not affect the menu handlers implemented here.

6.6.1WRITING ALBUM DATA

The Click handlers for our Save and Save As menus will rely on a Save method in the PhotoAlbum class to actually save the data, so let’s implement this first. This method will accept the name of a file in which to store the data. We rely on the user interface in MainForm to provide a file name approved by the user, so if the file already exists we will simply overwrite it.

182

CHAPTER 6 COMMON FILE DIALOGS

Set the version number of the MyPhotoAlbum library to 6.6.

ADD PHOTOALBUM.SAVE METHOD

 

Action

Result

 

 

 

1

Display the PhotoAlbum.cs file.

 

 

 

 

2

At the end of the file, add the

public void Save(string fileName)

 

new Save method.

{

 

 

}

 

 

Note: This method is void since an error is not

 

 

expected. If something goes wrong, an Exception

 

 

will be thrown.

 

 

 

The format to use when creating such a file is always a question. One possibility would be to write an XML file to hold this album information. This is a good idea, but beyond the scope of this chapter, so we will stick with a simple text format. Since the file format will likely change, especially in this book, we will allow for possible future changes.

With these issues in mind, we will store each photograph in the album on a separate line, with a version number at the beginning of the file. This section will use 66 as the version number, since we are in section 6.6 of the book. The resulting file looks like this:

66

<path to photograph 0>

<path to photograph 1>

<path to photograph 2>

. . .

Our version number is likely to change in future chapters, so we will provide a constant to hold the current version.

ADD A CURRENT VERSION CONSTANT

 

Action

Result

 

 

 

3

Add a static constant integer called

private const int _CurrentVersion = 66;

 

_CurrentVersion to hold the version

 

 

number.

 

 

 

 

The Save method will store the version number followed by the file name of each Photograph written as a simple string.

SAVE FILE DIALOGS

183

IMPLEMENT PHOTOALBUM.SAVE METHOD

 

Action

Result

 

 

 

4

Implement the Save method to

public void Save(string fileName)

 

store the album in the given file

{

 

using the agreed-upon format.

FileStream fs = new FileStream(fileName,

 

FileMode.Create,

 

 

 

 

FileAccess.ReadWrite);

 

 

StreamWriter sw = new StreamWriter(fs);

 

 

try

 

 

{

 

 

sw.WriteLine(_CurrentVersion.ToString());

 

 

// Store each file on a separate line.

 

 

foreach (Photograph photo in this)

 

 

{

 

 

sw.WriteLine(photo.FileName);

 

 

}

 

 

}

 

 

finally

 

 

{

 

 

sw.Close();

 

 

fs.Close();

 

 

}

 

 

}

 

 

 

5

Implement an alternate Save

public void Save()

 

method that uses the default file

{

 

name.

// Assumes FileName is not null

 

Save(this.FileName);

 

 

 

 

}

 

 

 

This code uses some classes we have not seen before, so let’s break our main Save method down piece by piece. Our first line opens or creates the given file name as a FileStream object. This class provides file I/O using simple byte arrays, and supports the well-known standard in, standard out, and standard error streams familiar to C and C++ programmers. Files can be open in various modes (via the FileMode enumeration), with various access levels (via the FileAccess enumeration). Different sharing options can be specified as well (not shown here) via the FileShare enumeration.

public void Save(string fileName)

{

FileStream fs = new FileStream(fileName,

FileMode.Create,

FileAccess.ReadWrite);

Next, we create a StreamWriter instance using the new FileStream object. Since we are using strings and not byte arrays, we need a class that provides simple string operations. The StreamWriter class does just this, and includes a constructor that accepts a FileStream instance.

StreamWriter sw = new StreamWriter(fs);

184

CHAPTER 6 COMMON FILE DIALOGS

Dis-
_CurrentVersion

The new StreamWriter instance is used to write our data into the file. We encapsulate the code to write the actual data in a try block to catch any exception that might occur.

try

{

First we write the version number as a string on the first line of the file. This line is a bit more magical than it looks. We are using a constant integer as an object here. While permitted, it requires the conversion of the value type

into a reference type that can be treated as an object instance on the heap. This conversion is called boxing, since the value is “boxed” into a reference type on the heap. More information on boxing is provided in appendix A.

sw.WriteLine(_CurrentVersion.ToString());

The Photograph objects in the album are written using a foreach loop to iterate through the array. This code relies on the fact that our album contains Photograph objects and implements the IEnumerable interface. The WriteLine method from the StreamWriter class (actually, this method is inherited from the base TextWriter class) writes a given string onto a single line of the file and adds the appropriate line termination characters.

// Store each file on a separate line. foreach (Photograph photo in this)

{

sw.WriteLine(photo.FileName);

}

You may think the magic of garbage collection obviates the need to explicitly clean up system resources such as files. As we have seen, this just isn’t so. Normally the pose method is used to clean up nonmemory resources. For file objects such as FileStream and StreamWriter, the more traditional Close method is used. By definition, Close is equivalent to Dispose in the .NET Framework. Classes that provide a Close method are automatically disposed of when the Close method is called. We will discuss this notion in more detail in chapter 8.

Since the files must be closed even when an exception occurs, we encapsulate these lines in a finally block. As you may know, while a finally block does not catch any exceptions, any code in the block is executed regardless of whether an exception occurs or not.

finally

{

sw.Close();

fs.Close();

}

}

Note that closing the objects in the reverse order of which they were opened is critical. Once the FileWriter is closed, the StreamWriter is not able to write any

SAVE FILE DIALOGS

185

remaining data into the file. Calling the Close methods in the proper order ensures all data is properly written to the file and avoids this potential error.

More .NET In this book we take a rather straightforward approach to reading and writing files, and will stick with a simple text file to represent our album throughout the book. There are some summaries of using the System.IO namespace in the .NET documentation if you are interested in more details. Search for the “Working with I/O” section in the .NET Framework Developer’s Guide.

We could also have stored our file in XML using classes from the System.XML namespace. The use of XML, for eXtensible Markup Language, is a great way to organize data, and is particularly useful when interacting with database systems or interfacing with remote computer systems. We opted for a simple text file in our application since many readers may not be familiar with XML. You can read up on XML in general at www.xml.org, or look up the XmlReader class and other members of the System.XML namespace in the .NET documentation.

Our new Save method can now be used in our MyPhotos application to save an album via our Save and Save As menus.

6.6.2SAVING AN ALBUM AS A NEW FILE

Let’s implement a handler for the Save As menu first. This handler should prompt the user to select a file where the album should be stored (using the SaveFileDialog class) and then use this file name to save the actual data. There are some questions to answer here about how photo albums should be saved. These questions apply more generally to any file, so are presented generically to apply to any file and not just our albums.

SaveFileDialog: questions to answer

• Where are photo albums stored?

Even though you may allow the user to select any location on disk, it is a good idea to encourage a standard location for the files in your application. In our case, this location is specified by the static DefaultDir property in the PhotoAlbum class.

• What is the file extension?

The selection of extension is a bit subjective. On Windows platforms, the following conventions normally apply:

Use three-letter extensions. The one exception is .html files for HTML files, but even here the .htm extension is preferred.

Keep the first letter. Typically, the first letter of the type of file should be the first letter of your extension. In our case, the extension for album file should begin with the letter ‘a’.

186

CHAPTER 6 COMMON FILE DIALOGS

Avoid numbers. At a minimum, start the extension with a letter. Use a number only if it is a critical aspect the type file you are creating.

Avoid well-known extensions. You will avoid confusion by using a somewhat unique combination of letters. You would not want to use extensions such as

.txt (already used for Text files) or .jpg (for JPEG files). To see the list of file types currently registered on your computer, open Windows Explorer and select the Folder Options… item under the Tools menu. Click on the File Types tab to see the extensions currently in use.

Use an acronym. It helps if your extension has some meaning to your users. If it makes sense, use an acronym of the full name. For example, the .gif extension is used for Graphics Interchange Format files.

Leave out the vowels. Another common tactic is to leave out any vowels in the name. Examples of this include the .txt (Text) and .jpg (JPEG) extensions.

Based on these conventions, we could use alb or abm here, which both derive from Album without the vowel “u’). We will use the extension .abm.

• What is the right title bar text?

Don’t forget to set a custom title bar that makes sense for your dialog. The default title bar is “Save,” which is not very descriptive. We will use “Save Album” for our title.

• How should existing or nonexistent files be handled?

By default, the user will be prompted if they select a file that already exists (the OverwritePrompt property) and will not be told if the file is new (the CreatePrompt property). Often the default behavior is fine, but it is worth making a conscious decision for your application. We will (consciously!) use the defaults in our code.

Now that we understand the right behavior to provide, we can implement the Save As menu handler.

SAVE FILE DIALOGS

187

Соседние файлы в папке c#