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

INITIALIZE THE MONTH CALENDAR CONTROL

 

Action

Result

 

 

 

1

In the MainForm.cs source file,

private void UpdateCalendar()

 

add an UpdateCalendar method

{

 

to update the MonthCalendar

// Initialize MonthCalendar control

 

 

 

control in the Dates tab.

 

 

 

 

2

In this method, calculate the range

DateTime minDate = DateTime.MaxValue;

 

of dates used by photographs in

DateTime maxDate = DateTime.MinValue;

 

this album.

DateTime[] dates

 

 

 

 

= new DateTime[_album.Count];

 

 

 

3

For each Photograph in the

for (int i = 0; i < _album.Count; i++)

 

album, record its date and adjust

{

 

the minimum and maximum date

DateTime newDate

 

= _album[i].DateTaken;

 

as required.

 

dates[i] = newDate;

 

Note: We could use a foreach

if (newDate < minDate)

 

loop here, of course. A for loop

 

minDate = newDate;

 

works a little better since an index

if (newDate > maxDate)

 

for the dates array is required.

 

 

maxDate = newDate;

 

 

}

 

 

 

4

Assign the MonthCalendar

if (_album.Count > 0)

 

properties based on the calculated

{

 

date values.

monthCalDates.BoldedDates = dates;

 

monthCalDates.MinDate = minDate;

 

 

 

Note: The SelectionStart prop-

monthCalDates.MaxDate = maxDate;

 

erty ensures that the initial date

monthCalDates.SelectionStart = minDate;

 

}

 

for the album is displayed by the

 

}

 

calendar.

 

 

 

 

5

Add a new UpdatePhotographs

private void UpdatePhotographs()

 

method to update the appropriate

{

 

tab page.

if (tcPhotos.SelectedTab == tabPhotos)

 

UpdateList();

 

 

 

How-to

else if (tcPhotos.SelectedTab == tabDates)

 

Use the SelectedTab property of

UpdateCalendar();

 

}

 

the tcPhotos control.

 

 

 

 

 

6

Modify the OpenAlbum method to

private void OpenAlbum(string fileName)

 

update the appropriate tab page.

{

 

 

CloseAlbum();

 

 

_album.Open(fileName);

 

 

this.Text = _album.FileName;

 

 

UpdatePhotographs();

 

 

}

 

 

 

7

In the MainForm.cs [Design]

private void tcPhotos_SelectedIndexChanged

 

window, handle the

(object sender, System.EventArgs e)

 

SelectedIndexChanged event for

{

 

UpdatePhotographs();

 

our tab control.

 

}

 

Note: This is the default event for

 

 

tab controls, and occurs when-

 

 

ever a new tab is selected by the

 

 

user.

 

 

 

 

CALENDARS

375

Mouse-

INITIALIZE THE MONTH CALENDAR CONTROL (continued)

 

Action

Result

 

 

 

8

Update the

private void cmbxAlbums_SelectedIndexChanged

 

SelectedIndexChanged event

(object sender, System.EventArgs e)

 

handler for our combo box control

{

 

. . .

 

to enable or disable the controls as

 

try

 

required.

{

 

Note: We take a slightly different

CloseAlbum();

 

OpenAlbum(albumPath);

 

approach here than we used ear-

tcPhotos.Enabled = true;

 

lier in the chapter. The final effect

btnAlbumProp.Enabled = true;

 

is the same.

}

 

catch (Exception)

 

 

 

 

{

 

 

// Unable to open album

 

 

this.Text = "Unable to . . . album";

 

 

tcPhotos.Enabled = false;

 

 

lstPhotos.Items.Clear();

 

 

monthCalDates.RemoveAllBoldedDates();

 

 

btnAlbumProp.Enabled = false;

 

 

}

 

 

}

 

 

 

Our calendar, as well as our list box, is updated whenever an album is opened and whenever the user displays an alternate tab page. Compile and run the application if you would like to see this in action. The next section processes the user’s mouse clicks in the control to provide access to the PhotoEditDlg form associated with a selected date.

11.4.3HANDLING MOUSE CLICKS IN A CALENDAR CONTROL

Our MonthCalendar control is on the form and displays the dates assigned to an album’s photographs in bold. The next step is to handle clicks by the user and link them up with associated photographs.

We will handle the MouseDown event for this purpose, and create a ContextMenu object on the fly to display any photos associated with the selection point. The MonthCalendar class also provides the DateChanged event that occurs whenever

a valid date is clicked. We could use this event instead, although the current mouse position would still be required to display the context menu. Since the MouseDown event provides the mouse location directly, this event seems a more logical choice.

We will discuss mouse events in more detail in the next chapter. Like the

Move event used in chapter 8, a MouseDown event handler receives a MouseEventArgs that includes the current position of the mouse. We will use this position both to determine which aspect of the calendar the user clicked on and to display the context menu at the appropriate location.

Before we see how to add this handler, one other item is needed. When we create MenuItem objects for the context menu, we will need a way to retrieve the associated Photograph object if the user later selects the menu. While the Control class provides a Tag property that associates an object instance with a control, the MenuItem

376

CHAPTER 11 MORE CONTROLS

class has no such property. As a result, we have to deal with this unfortunate omission ourselves. In chapter 3, we created an array indexed by the menu location that linked a display mode to the menu. Now that we are more experienced, we will simply create a new class derived from MenuItem for a similar purpose.

CREATE A CUSTOM MENUITEM CLASS TO HOLD THE ALBUM INDEX

 

Action

Result

 

 

 

1

Within the MainForm class definition,

private class PhotoMenuItem : MenuItem

 

define a new PhotoMenuItem class

{

 

based on the MenuItem class within

 

 

the MainForm class.

 

 

 

 

2

Add a public field in this class to hold

public int tag;

 

the integer album index associated

}

 

with the menu.

 

 

 

 

As you will see, this very simple class will make our click handling much more efficient. Let’s take a look at this code.

HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL

 

Action

Result

 

 

 

3

Add an event handler for the

private void monthCalDates_MouseDown

 

MouseDown event in the

(object sender,

 

MonthCalendar control.

System.Windows.Forms.MouseEventArgs e)

 

{

 

 

 

 

 

4

Determine if the user clicked on a

MonthCalendar.HitTestInfo info

 

date.

= monthCalDates.HitTest(e.X, e.Y);

 

How-to

if (info.HitArea

 

== MonthCalendar.HitArea.Date)

 

Use the HitTest method.

{

 

 

 

5

If so, create a new context menu

ContextMenu ctxtPhotoCal

 

to hold any photographs

= new ContextMenu();

 

associated with this date.

 

 

 

 

6

Iterate through the photos in the

for (int i = 0; i < _album.Count; i++)

 

album.

{

 

 

 

7

Look for any photographs taken

if (_album[i].DateTaken.Date

 

on the same date as the date

== info.Time.Date)

 

clicked by the user.

{

 

 

 

How to

 

 

Use the Date property to obtain

 

 

only the date portion of the

 

 

DateTime objects.

 

 

 

 

CALENDARS

377

HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL (continued)

 

Action

Result

 

 

 

8

If a matching photo is found,

PhotoMenuItem newItem

 

create a new PhotoMenuItem

= new PhotoMenuItem();

 

object for this photo.

newItem.tag = i;

 

 

 

How-to

newItem.Text = _album[i].FileName;

 

newItem.Click += new

 

a. Initialize the tag field to the

 

EventHandler(

 

photograph’s index.

ctxtPhotoCal_MenuClick);

 

b. Initialize the MenuItem.Text

 

 

property to the image file

 

 

name.

 

 

c. Set a Click handler for this

 

 

menu item.

 

 

 

 

9

Add this new item to the context

ctxtPhotoCal.MenuItems.Add(newItem);

 

menu.

}

 

 

}

 

 

 

10

If one or more matching

if (ctxtPhotoCal.MenuItems.Count >= 1)

 

photographs were found, display

{

 

the context menu.

ctxtPhotoCal.Show(monthCalDates,

 

new Point(e.X, e.Y));

 

 

 

How-to

}

 

Use the Show method at the

}

 

}

 

current mouse location.

 

 

 

 

 

11

Create a private DisplayPhoto-

private bool DisplayPhotoEditDlg(int index)

 

EditDlg method to accept an

{

 

album index and display the

_album.CurrentPosition = index;

 

 

 

associated dialog.

using (PhotoEditDlg dlg

 

Note: This method returns a

= new PhotoEditDlg(_album))

 

{

 

boolean value indicating whether

 

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

 

the user modified any settings.

{

 

 

_bAlbumChanged = true;

 

 

return true;

 

 

}

 

 

}

 

 

return false;

 

 

}

 

 

 

12

Implement a ctxtPhoto-

private void ctxtPhotoCal_MenuClick

 

Cal_MenuClick method to

(object sender, System.EventArgs e)

 

handle any context menu

{

 

PhotoMenuItem mi = sender as PhotoMenuItem;

 

selection and display the

 

 

 

associated Photo Properties

if ((mi != null)

 

dialog.

&& (DisplayPhotoEditDlg(mi.tag)))

 

 

{

 

 

UpdateCalendar();

 

 

}

 

 

}

 

 

 

378

CHAPTER 11 MORE CONTROLS

Hit-

HANDLE A MOUSE CLICK IN THE CALENDAR CONTROL (continued)

 

Action

Result

 

 

 

13

Update the Click handler for the

private void btnPhotoProp_Click

 

photo’s Properties button on the

(object sender, System.EventArgs e)

 

Photos tab page to use the new

{

 

if (_album.Count == 0)

 

DisplayPhotoEditDlg method.

 

return;

 

 

if (lstPhotos.SelectedIndex >= 0)

 

 

{

 

 

if (DisplayPhotoEditDlg(

 

 

lstPhotos.SelectedIndex))

 

 

{

 

 

UpdateList();

 

 

}

 

 

}

 

 

}

 

 

 

When the user clicks on the MonthCalendar control, this code will find and display any photographs associated with a selected date. Note how the HitTest method is used to retrieve information about the selected point. This method returns a TestInfo object. The HitTestInfo class is defined within the MonthCalendar class, and provides a HitArea property containing the type of area clicked by the user, and a Time property containing the DateTime value corresponding to the selected item. The possible values for the HitArea property are defined by the MonthCalendar.HitArea enumeration, as described in .NET Table 11.6.

private void monthCalDates_MouseDown

(object sender, System.Windows.Forms.MouseEventArgs e)

{

MonthCalendar.HitTestInfo info = monthCalDates.HitTest(e.X, e.Y); if (info.HitArea == MonthCalendar.HitArea.Date)

{

Another important part of this code is the definition and use of the PhotoMenuItem class. Without this class, we would be forced to search for a selected photograph based on the file name stored in the Text property of the menu. This rather simple extension to MenuItem provides an efficient method of communicating a photograph’s index from the context menu to a menu item’s Click handler.

private class PhotoMenuItem : MenuItem

{

// An integer field to store a photograph’s index public int tag;

}

Because this class is still a MenuItem instance, we can use it just like any other menu item object. We can set the Text property, establish a Click event handler, and add the menu to our context menu.

PhotoMenuItem newItem = new PhotoMenuItem();

newItem.tag = i;

CALENDARS

379

newItem.Text = _album[i].FileName;

newItem.Click += new EventHandler(ctxtPhotoCal_MenuClick);

// Add this item to the context menu ctxtPhotoCal.MenuItems.Add(newItem);

After the context menu has been displayed, the Click handler receives the menu item object selected by the user. We downcast the given object into a PhotoMenuItem instance in order to retrieve the index in the photo album and display the appropriate Photo Properties dialog.

private void ctxtPhotoCal_MenuClick(object sender, System.EventArgs e)

{

PhotoMenuItem mi = sender as PhotoMenuItem;

if ((mi != null) && (DisplayPhotoEditDlg(mi.tag)))

{

UpdateCalendar();

}

}

Compile and run the application to see how all this code works. Click on a date where one or more photographs were taken and be amazed as a context menu pops up with the corresponding photos. Also try clicking on other aspects of the control to see what happens. In particular, see what happens when you click on the month and year in the title of the control. Note that your ability to alter the month and year displayed is restricted by the range of dates represented in the photo album.

More .NET The PhotoMenuItem class developed in this section extends the MenuItem object provided by the Windows Forms namespace. This technique is useful when you need a class that is similar to an existing control, and the ability to downcast objects in C# ensures that you can access the additional members of your derived class in a type-safe manner.

You can also build custom controls by extending the Control class directly. Windows Forms also provides a UserControl class that is specifically intended for building customized container controls. The Project menu in Visual Studio .NET includes an Add User Control item for creating such a control. There is a walkthrough in the .NET documentation entitled “Authoring a User Control with Visual C#” that introduces this concept.

Custom controls can also be tightly integrated into the Toolbox and other parts of Visual Studio .NET. The System.Windows.Forms.Design namespace contains the classes and other types to support such integration. If you are interested in this topic, search for more information at any of the .NET web sites listed in appendix D. In particular, as of this writing there is an article by Shawn Burke entitled “Writing Custom Designers for .NET Components” at the Microsoft Developer Network at msdn.microsoft.com.

380

CHAPTER 11 MORE CONTROLS

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