Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
817,68 KB
Nội dung
166 CHAPTER 6 COMMON FILE DIALOGS These variables are required to implement all of our new menu items in this chapter. With these in place, it is time to do just that. 6.2 MULTIPLE FILE SELECTION Now that we have an album in our MainForm class, albeit an empty one, we can add photos to it. In previous chapters, we allowed the user to read in a single photo, first using a button and later with a menu item. In our new structure, this has been replaced by the ability to add multiple photos to the album or remove the current photo from the album. Since this code builds on our original Load handler, it is a good place to begin. As you would expect, we will provide Click handlers for both the Add and Remove items. The Add menu should allow one or more photos to be selected and added to the album, while the Remove menu should delete the currently displayed photo from the album. The Add menu will use the Multiselect property of the OpenFileDialog class, and is where our catchy section title comes from. 6.2.1 A DDING IMAGES TO AN ALBUM In chapter 3, the Click handler for the Load menu permitted a single file to be selected using the OpenFileDialog class. This made sense when only a single image was managed by the application. In this chapter, the idea of an album permits multiple images to be present at the same time. As a result, our user should also be able to load multiple images at once. This is again done using the OpenFileDialog class, so the code for this handler will be similar to the Click event handler for the Load menu from chapter 3. The Multiselect property is provided by the Open- FileDialog class to indicate whether multiple files can be selected in the dialog. This and other members specific to this class are summarized in .NET Table 6.1. 2 Within the MainForm class, add a protected album variable _album. protected PhotoAlbum _album; 3 Add a protected boolean called _bAlbumChanged to track when an album is modified. protected bool _bAlbumChanged = false; Note: This will be useful when deciding whether to save an existing album before loading a new one or closing the application. If no changes have occurred, then we will know to not save the album. 4 Create an empty album at the end of the MainForm constructor. public MainForm() { . . . _album = new PhotoAlbum(); } CREATE SOME CLASS VARIABLES (continued) Action Result MULTIPLE FILE SELECTION 167 The steps to implement a Click event handler for the Add menu are shown in the following table. Set the version number of the MyPhotos application to 6.2. .NET Table 6.1 OpenFileDialog class The OpenFileDialog class represents a common file dialog box for loading one or more files from disk, and is part of the System.Windows.Forms namespace. This class inherits from the FileDialog class, and is the standard class for opening existing files. See .NET Table 1.2 on page 24 for a list of members inherited from the FileDialog class. Public Properties Multiselect Gets or sets whether the user can select multiple files in the dialog. The FileNames property inherited from the FileDialog class should be used to retrieve the selected files. ShowReadOnly Gets or sets whether the dialog should contain a read-only check box. Defaults to false. ReadOnlyChecked Gets or sets whether the read only checkbox is checked. Defaults to false. Public Methods OpenFile Returns a Stream with read-only access for the file specified by the FileName property. IMPLEMENT ADD HANDLER Action Result 1 Open the Windows Forms Designer window for the MainForm.cs file. As we have seen before, a graphic of the current layout for this form is displayed. 2 Add a Click event handler for the Add item under the Edit menu. How-to Double-click on the menu item. A new menuAdd_Click method is added to and displayed in the MainForm.cs source file. The line to add the handler is created by Visual Studio in the InitializeComponent method automatically: menuAdd.Click += new EventHandler (this.menuAdd_Click); 3 Remove the menuLoad_Click handler and copy its code into the menuAdd_Click handler. Note: This code opens a single file and arranges to dis- play it in the window. Here, we just want to add the file to the album, so some changes are required. The code in the subsequent steps is based on the Load handler, although there are some differences. In particular, we do not handle any exceptions that might occur. This is done intentionally so that we can discuss the handling of such exceptions in chapter 7. 168 CHAPTER 6 COMMON FILE DIALOGS In the code, note how the Multiselect property is used to permit multiple file selections. This property is one of the few OpenFileDialog members not inherited from the FileDialog class. 4 Initialize an OpenFileDialog instance to allow multiple selections of various image file types. How-to Use the Multiselect property to allow multiple files to be selected. Note: The Filter setting here includes most of the common formats users are likely to see. All of these for- mats are supported by the Bitmap class. protected void menuAdd_Click (object sender, System.EventArgs e) { OpenFileDialog dlg = new OpenFileDialog(); dlg.Title = "Add Photos"; dlg.Multiselect = true; dlg.Filter = "Image Files (JPEG, GIF, BMP, etc.)|" + "*.jpg;*.jpeg;*.gif;*.bmp;" + "*.tif;*.tiff;*.png|" + "JPEG files (*.jpg;*.jpeg)|*.jpg;*.jpeg|" + "GIF files (*.gif)|*.gif|" + "BMP files (*.bmp)|*.bmp|" + "TIFF files (*.tif;*.tiff)|*.tif;*.tiff|" + "PNG files (*.png)|*.png|" + "All files (*.*)|*.*"; dlg.InitialDirectory = Environment.CurrentDirectory; 5 Invoke the dialog and process an OK response. if (dlg.ShowDialog() == DialogResult.OK) { 6 Extract the array of files selected by the user. string[] files = dlg.FileNames; 7 Turn off the status bar panels while the images are loading. statusBar1.ShowPanels = false; statusBar1.Text = String.Format("Loading {0} Files", files.Length); 8 Iterate through the array of selected files. int index = 0; foreach (string s in files) { 9 Add each image to the album if it is not already present. How-to Use the IndexOf method to see if the photo is already in the album. Photograph photo = new Photograph(s); // Add the file (if not already present) index = _album.IndexOf(photo); if (index < 0) { index = _album.Add(photo); _bAlbumChanged = true; } } Note: The IndexOf method relies on the Equals override we implemented in chapter 5. 10 Dispose of the nonmemory resources used by the dialog. dlg.Dispose(); 11 Invalidate the main window to display the new settings. this.Invalidate(); } } IMPLEMENT ADD HANDLER (continued) Action Result PAINT EVENTS 169 The code also sets the InitialDirectory property to the current directory using the Environment class. This ensures that the initial directory in the dialog is always the current directory for our application. While this may not seem so relevant right now, it will become important when we implement Click handlers for our Save and Save As menus. We will look at the Environment class in more detail later in the chapter. The menuAdd_Click method is similar to our original Load menu handler, but also very different. In particular, this method leaves unresolved the issue of what to dis- play in the form, and the exception handling has been removed. We will handle these issues subsequently. For now, let’s move on to the Remove menu handler. 6.2.2 R EMOVING IMAGES FROM AN ALBUM The event handler for the Remove menu uses the CurrentPosition property to locate the current photo and delete it from the album. The menuRemove_Click handler uses the RemoveAt method from our PhotoAl- bum class to remove the current photo. The issue of adjusting the current position in case we remove the last photo from the album is left to the PhotoAlbum class to han- dle. If you recall, the RemoveAt method we implemented in chapter 5 ensures that the current index remains valid after it is called through an override of the OnRe- moveComplete method, so the current position is properly updated here. Once again we have ignored the display issues. This is because our menu handlers will no longer interact with the Form window directly. Instead we will override the protected OnPaint method for this purpose, which is our next topic. 6.3 PAINT EVENTS Now that we can load multiple images into our album, we need a way to make them appear in the window. In previous chapters, we have simply assigned the selected photo to the Image property of our PictureBox control and relied on the .NET Framework to deal with the rest. The framework will still do most of the work, but now we need to identify which image from our album should be drawn. IMPLEMENT REMOVE HANDLER Action Result 1 Add a Click handler for the Remove menu. protected void menuRemove_Click (object sender, System.EventArgs e) { 2 Implement this handler to remove the current photo from the album. if (_album.Count > 0) { _album.RemoveAt(_album.CurrentPosition); _bAlbumChanged = true; } this.Invalidate(); } 170 CHAPTER 6 COMMON FILE DIALOGS As in previous Microsoft development environments, such drawing is called painting in .NET. You may have noticed in chapter 3 that the Control class provides a Paint event for custom painting of a control. The event name is one piece of the support provided for each event in the .NET Framework. While we have seen these pieces in our previous use of events, this is a good place to list them more formally. The following support is required in order to define and support an event. • A class that defines the event data. This is either the System.EventArgs class or a class derived from System.EventArgs. The event data for the Paint event is defined by the PaintEventArgs class. We will discuss the contents of the PaintEventArgs class in chapter 7. • A delegate for the event. This delegate is used by Visual Studio .NET to add the event handler in the InitializeComponent method. By convention, the name of this delegate is the event name followed by the string “EventHandler.” The Paint event is supported by the PaintEventHandler delegate. The creation of delegates is discussed in chapter 9. • A class that raises the event. This class must define the event and provide a method to raise the event. By convention the method to raise the event is the string “On” followed by the event name. The protected OnPaint method raises the Paint event. For painting of controls, the Control class defines the Paint event. Within the def- inition of this class, the event is defined using the event keyword in C#. public event PaintEventHandler Paint; 6.3.1 DRAWING THE CURRENT PHOTOGRAPH Returning to our code, we need a way to draw the appropriate photograph in our album. We could handle the Paint event directly in our Form or PictureBox control for this purpose. Instead, since the MainForm class derives from the Form class, we can override the method that raises the event directly. This technique is pre- ferred where possible to avoid the extra overhead of creating and invoking an event handler. In this case, we will override the protected OnPaint method to handle the Paint event. PAINT EVENTS 171 Set the version number of the MyPhotos application to 6.4. 6.3.2 DISPLAYING THE CURRENT POSITION Before we see our changes in action, it would be nice to have some indication of our current position within the album and the total album size in the window. We can do this by adding a new StatusBarPanel to hold this information, as detailed by the following steps. OVERRIDE THE ONPAINT METHOD Action Result 1 In the MainForm.cs file override the OnPaint method. protected override void OnPaint( PaintEventArgs e) { 2 Only paint an image if the album is not empty. Note: The three comments here are implemented in the subsequent steps. In all cases, the status bar is invalidated. if (_album.Count > 0) { // Paint the current image // Update the status bar } else { // Indicate the album is empty } statusBar1.Invalidate(); 3 Call OnPaint in the base class. base.OnPaint(e); } Note: This call is required to ensure that any Paint event handlers registered with the Form are called. As mentioned in chapter 5, the base keyword refers to the base class of the current object. 4 Paint the current image by setting the Image property of the pbxPhoto control. // Paint the current image Photograph photo = _album.CurrentPhoto; pbxPhoto.Image = photo.Image; 5 Update the status bar to hold the appropriate information about the image. Note: The code here is similar to what we used in our menuLoad_Click event handler in chapter 4. // Update the status bar. sbpnlFileName.Text = photo.FileName; sbpnlImageSize.Text = String.Format ("{0:#} x {1:#}", photo.Image.Width, photo.Image.Height ); statusBar1.ShowPanels = true; 6 When no images are present, clear the screen and display an appropriate status bar message. // Indicate the album is empty pbxPhoto.Image = null; statusBar1.Text = "No Photos in Album"; statusBar1.ShowPanels = false; 172 CHAPTER 6 COMMON FILE DIALOGS The preceding tables have made a number of changes to the OnPaint method. The following code pulls together all of the pieces presented in the preceding tables. We will not discuss these changes in additional detail. protected override void OnPaint(PaintEventArgs e) { if (_album.Count > 0) { // Paint the current image Photograph photo = _album.CurrentPhoto; pbxPhoto.Image = photo.Image; // Update the status bar. sbpnlFileName.Text = photo.FileName; sbpnlFileIndex.Text = String.Format("{0}/{1}", _album.CurrentPosition+1, _album.Count); sbpnlImageSize.Text = String.Format("{0} x {1}", photo.Image.Width, photo.Image.Height); ADD A NEW STATUS BAR PANEL Action Result 1 In the MainForm.cs Design window, display the StatusBarPanel Collection Editor for the statusBar1 control. The StatusBarPanel Collection Editor dialog appears as was shown in chapter 4. 2 Add a new StatusBarPanel in this dialog just before the existing sbpnlImagePercent panel. The new panel is added to the Panels collection. The source code in the InitializeComponent method is updated to define the new panel and add it to the status bar. 3 In the OnPaint method, set the text for this panel to contain the image index and album size. sbpnlFileIndex.Text = String.Format ("{0:#}/{1:#}", _album.CurrentPosition+1, _album.Count); How-to a. Display the properties for this control. b. Click on the Panels property item. c. Click the … button. How-to a. Click the Add button. b. Click the up arrow in the center of the dialog to move the panel just beforethe image percent panel. c. Assign the proper settings as shown. d. Click OK to add the panel. Settings Property Value (Name) sbpnlFileIndex AutoSize Contents ToolTipText Image Index CONTEXT MENUS REVISITED 173 statusBar1.ShowPanels = true; } else { // Indicate the album is empty pbxPhoto.Image = null; statusBar1.Text = "No Photos in Album"; statusBar1.ShowPanels = false; } statusBar1.Invalidate(); base.OnPaint(e); } Our code is coming along. We can add new photos to the album, and remove the photo currently displayed. TRY IT! Compile the code and verify that you can add and remove images to the album. Make sure you can add multiple images at once by selecting a range of images with the Shift key. This can be done by clicking the first file, holding down the Shift key, and then clicking the last file. You can also se- lect multiple single images with the Ctrl key by clicking the first, holding down the Ctrl key, clicking the second, and so on. Also see what happens when a nonimage file is specified. You should see our invalid image with the red X that we created in chapter 5. This indicates to the user that something is wrong, but maintains the image paradigm used by our application. The current code does not allow us to move to the next and previous images in the album, so only the first photo in the album is ever displayed. Navigating within the album using the Next and Previous menus is our next topic. 6.4 CONTEXT MENUS REVISITED In this section we implement the Next and Previous menu items for our application. These menus are part of the View menu on the main menu bar. If you recall, this menu was cloned and assigned to the context menu for use with the PictureBox control. Our careful implementation in chapter 3 ensured that the contents of the context menu always match the contents of the View menu. In fact, your application should include these menus now, as can be seen in figure 6.2. 174 CHAPTER 6 COMMON FILE DIALOGS The handlers for Next and Previous use concepts we have previously discussed, so let’s get to it. 6.4.1 D ISPLAYING THE NEXT PHOTOGRAPH The Next handler uses the CurrentNext method from our PhotoAlbum class, and is implemented using the following steps. Set the version number of the MyPhotos application to 6.4. You will note that we invalidate any image currently displayed only if a next photo- graph is available. It might be a good idea to beep or display a message when no next photo is available to inform the user they are at the end of the album. We will discuss how to do this in the next chapter. 6.4.2 D ISPLAYING THE PREVIOUS PHOTOGRAPH The Click event for the Previous menu is implemented in a similar manner. Figure 6.2 A context menu displays keyboard shortcuts just like the main menu. As a special treat, an image not yet seen in this book is shown here. IMPLEMENT HANDLER FOR THE NEXT MENU Action Result 1 Add a Click handler for the Next menu item. protected void menuNext_Click (object sender, System.EventArgs e) { 2 Implement this handler using the CurrentNext method. if (_album.CurrentNext()) { this.Invalidate(); } } FILES AND PATHS 175 Compile and run the application to verify that your code produces the screen shown in figure 6.2 earlier in this section. TRY IT! It would be useful to have First and Last menu items here. These would dis- play the first or last photo in the album, respectively. Add these two menus to the View menu and provide a Click event handler for each menu. 6.5 FILES AND PATHS Before we implement our save methods, a brief talk on the name of an album is in order. While we may store the album in a file such as “C:\Program Files\MyPho- tos\sample.abm,” such a name is a bit cumbersome for use in dialogs and on the title bar. The base file name, in this case “sample,” is more appropriate for this purpose. Another issue is where exactly should album files be stored? This section resolves these issues by defining a default directory where albums are stored and establishing a title bar based on the current album name. These features will then be used to implement a Click event handler for our New menu. 6.5.1 C REATING A DEFAULT ALBUM DIRECTORY While an album file can be placed in any directory, it is nice to provide a common place for such files. This location will be used by default for both opening and saving albums. Common directories for this and other standard information are available from the Environment class, as summarized in .NET Table 6.2. For our default directory, the GetFolderPath method provides convenient access to the special folders in the system, such as the user’s My Documents directory. There are a number of special folders available, with a few of them listed in .NET Table 6.3. We are interested in the location of the My Documents directory, which corresponds to the Personal enumeration value. IMPLEMENT PREVIOUS HANDLER Action Result 1 Add a Click handler for the Previous menu item. protected void menuPrevious_Click (object sender, System.EventArgs e) { 2 Implement this handler using the CurrentPrev method. if (_album.CurrentPrev()) { this.Invalidate(); } } [...]... functionality is useful, but not as glamorous as scroll bars or forms, so we skip it .NET Table 7.2 Form class The Form class represents any window that can be displayed by an application, including standard windows as well as modal or modeless dialog boxes and multiple document interface (MDI) windows This class is part of the System .Windows. Forms namespace and inherits from the ContainerControl class... although the actual windows are quite different Also of note are the PageSetupDialog and PaintDialog classes These common dialogs are used when printing from Windows Forms applications, and are discussed in chapter 18 Finally, we should also note that common dialogs, including the OpenFileDialog and SaveFileDialog classes used in this chapter can be configured directly in the Windows Forms Designer window... as forms and panels A summary of this class is provided in the NET Table 7.1 We will see the members of this class in action later in the chapter when we enable scrolling within our application .NET Table 7.1 ScrollableControl class The ScrollableControl class represents a control that supports automated scrolling This class is part of the System .Windows. Forms namespace and inherits from the System .Windows. Forms. Control... Earlier development environments from Microsoft distinguished among the different types of windows an application may display In MFC, for example, there is one hierarchy (CFrameWnd) for framed windows such as MDI windows and control bars, another (CDialog) for dialog boxes, and yet another (CView) for the various document view classes The NET Framework has taken a very different approach In NET, the Control... low-level classes used by Windows Forms, and chapter 4 extended this hierarchy to include the Control class The Form class is based on additional extensions to enable scrolling and containment The complete hierarchy is shown in figure 7.1 Figure 7.1 The Form class hierarchy extends the Control class discussed in chapter 4 with the functionality required for various kinds of application windows As you can... very different approach In NET, the Control class is the basis for all controls, including the various types of window objects The Form class, as we shall see, encompasses all types of windows be they MDI frames, MDI child windows, floating tool bars, or dialog boxes The next two chapters will take a closer look at the Form class to understand how to interact with this object in our applications In this... 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... property in our PhotoAlbum class We will allow a programmer to modify this value, but this provides a starting point for album storage To distinguish photo albums from other documents, we will create an Albums directory within the My Documents folder 176 CHA PTE R 6 COMMON FILE DIALOGS .NET Table 6.3 SpecialFolder enumeration The SpecialFolder enumeration specifies various types of predefined folders in... pixels AutoScrollPosition Gets or sets the Point within the virtual display area to appear in the upper left corner of the visible portion of the control DockPadding Gets or sets the extra padding for the inside border of this control when it is docked Public Methods SetAutoScrollMargin Sets the AutoScrollMargin property Protected Properties HScroll Gets or sets whether the horizontal scroll bar is... 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 . common file dialog box for loading one or more files from disk, and is part of the System .Windows. Forms namespace. This class inherits from the FileDialog class, and is the standard class. file specified by the FileName property. IMPLEMENT ADD HANDLER Action Result 1 Open the Windows Forms Designer window for the MainForm.cs file. As we have seen before, a graphic of the current. as the user’s My Documents directory. There are a number of special folders available, with a few of them listed in .NET Table 6.3. We are interested in the location of the My Documents directory,