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

.NET Table 17.8 CurrencyManager class

The CurrencyManager class represents a binding manager that is associated with a data source supporting the IList interface. This class is part of the System.Windows.Forms namespace, and inherits from the BindingManagerBase class. See .NET Table 17.7 for the members inherited from the base class.

 

Refresh

Forces a repopulation of all bound controls for a data

Public Methods

 

source that does not support notification when the

 

 

underlying data changes.

 

 

 

 

ItemChanged

Occurs when an item in the associated data source is

Public Events

 

altered. This event will only occur if the associated data

 

source supports two-way notification, such as the support

 

 

 

 

provided by the IBindingList interface.

 

 

 

From the table, it appears that the Refresh method is the solution we need. This method updates the bound controls with the underlying data. For classes that support the IBindingList interface, most notably the database-related objects, this method is not generally needed since the ItemChanged event will occur whenever the database object itself is modified. Do not confuse the ItemChanged event with the PositionChanged event, which occurs when a new item, or row, in the associated list is selected; or with the CurrentChanged event, which occurs when the control’s bound property is altered.

For our purposes, the default behavior that occurs when the Position or Current properties change will suffice. Since we do not support the IBindingList interface, we need to call the Refresh method directly when our PhotoAlbum is altered internally. In our current interface, this occurs each time a new album file is selected in the Album combo box control.

The following table continues our prior steps to alter our Changed event handler to invoke the Refresh method.

UPDATE THE SELECTEDINDEXCHANGED EVENT HANDLER

 

Action

Result

 

 

 

7

Move the

protected override void OnLoad(EventArgs e)

 

DataGrid.SetDataBinding call

{

 

from the

. . .

 

gridPhotoAlbum.SetDataBinding(_album,

 

SelectedIndexChanged event

 

null);

 

handler to the end of the OnLoad

}

 

method.

Note: At the start of the chapter, we had to clear

 

 

 

 

the binding and then rebind to the album each

 

 

time the album changed. This is no longer

 

 

required.

 

 

 

SIMPLE DATA BINDING

597

UPDATE THE SELECTEDINDEXCHANGED EVENT HANDLER (continued)

 

Action

Result

 

 

 

8

At the end of the Selected-

private void cmbxAlbum_SelectedIndexChanged

 

IndexChanged event handler, add

(object sender, System.EventArgs e)

 

a single empty Photograph

{

 

. . .

 

object to the cleared PhotoAlbum

 

// Required to prevent binding exception

 

instance when the album is

if (_album.Count == 0)

 

empty.

_album.Add(new Photograph(""));

 

 

Note: Our bound controls required that at least

 

 

one object be present in the collection. Otherwise,

 

 

the subsequent lines will throw an exception. A

 

 

more elegant solution might be to unbind the con-

 

 

trols and disable the tab control in this case.

 

 

 

9

Add code to the end of the

// Refresh the Photo tab controls

 

method to retrieve the

BindingManagerBase bm

 

CurrencyManager object used to

= this.BindingContext[_album];

 

CurrencyManager cm = bm as CurrencyManager;

 

manage the _album data source.

 

 

 

How-to

 

 

Retrieve the BindingManager-

 

 

Base for this data source from the

 

 

Form and convert it to a

 

 

CurrencyManager object.

 

 

 

 

10

If the CurrencyManager was

if (cm != null)

 

located, refresh the bound

cm.Refresh();

 

controls.

 

 

 

 

11

Also call EnablePhotoButtons to

EnablePhotoButtons(bm);

 

enable or display the Next and

}

 

Prev buttons as required.

 

 

 

 

The controls in the Photo tab now update properly in all cases. You can compile and run this to experience the magic. Note how the index into the album, based on the Position property, is preserved when you change albums. If the second item in an album is shown and a new album is selected, the second item in the new album is selected. What happens when the number of photos in the current album is more than the number in a newly selected album?

TRY IT! We have not discussed the data binding for list controls very much here. As a way to see this in action, replace the txtPhotographer control with a combo box called cmbxPhotographer. Use data binding to automatically fill this list with the Photographer entries from the current album. This should be done in the OnLoad method and should look like the following:

cmbxPhotographer.DataSource = _album;

cmbxPhotographer.DataMember = "Photographer";

598

CHAPTER 17 DATA BINDING

As you will see if you compile this change, all photographers assigned to the album are listed, even if they occur multiple times. Even so, this is a good example of how to populate a list quickly with values from a data source.

To provide a more robust implementation, implement a GetPhotographers method in the PhotoAlbum class that returns an array of unique photographer strings in the album. This array can then be set as the DataSource for the combo box. Be careful here, as you will need to update this setting in the SaveChanges method to accommodate any changes made to the list.

As already mentioned, when binding to a database, the IBindingList interface will ensure that the bound properties and the database object stay in sync. When either object is modified, the other is automatically updated.

One issue that remains in our current application is the display of our image in the PictureBox control. We will address this topic next.

17.4.4DISPLAYING THE IMAGE

While our application is working quite well, there is the small matter of our PictureBox control. As a simple solution, we could modify the SizeMode property of our PictureBox control to use the StretchImage value. This would stretch our image to fit the window, but would not preserve the aspect ratio of our images.

A better solution, as we know very well, is to scale the image to preserve the aspect ratio within the window. We can do this if we paint the image ourselves rather than provide a value for the Image property. The Paint event for the PictureBox control can be used to perform this painting, although we still need to know which image to paint. For this, we will use the Tag property to keep track of the current image.

Of course, as we mentioned earlier in the book, it would be nice to build a “PhotoBox” control that did this automatically. Such a control would extend the Windows Forms PictureBox control to scale an image as we do in the subsequent table. Coding this by hand is not much extra work, so we did this explicitly here. A short discussion on how to build such a “PhotoBox” control is given at the end of section 18.2.

The following steps are required to scale the image within the pboxPhoto control.

MODIFY THE PICTUREBOX CONTROL TO DISPLAY A SCALED IMAGE

 

Action

Result

 

 

 

1

In the OnLoad method of the

protected override void OnLoad(EventArgs e)

 

MainForm.cs code window,

{

 

replace the binding of the Image

. . .

 

txtNotes.DataBindings.Add(

 

property in the pboxPhoto

 

"Text", _album, "Notes");

 

control to use the Tag property

pboxPhoto.DataBindings.Add(

 

instead.

"Tag", _album, "Image");

 

 

gridPhotoAlbum.SetDataBinding(_album, null);

 

 

}

 

 

 

SIMPLE DATA BINDING

599

MODIFY THE PICTUREBOX CONTROL TO DISPLAY A SCALED IMAGE (continued)

 

Action

Result

 

 

 

2

Invalidate the PictureBox

private void EnablePhotoButtons

 

control in the

(BindingManagerBase bm)

 

EnablePhotoButtons method.

{

 

btnNext.Enabled

 

 

 

 

= (bm.Position < _album.Count - 1);

 

 

btnPrev.Enabled = (bm.Position > 0);

 

 

// Force image to repaint

 

 

pboxPhoto.Invalidate();

 

 

}

 

 

 

3

Add a handler for the Paint

private void pboxPhoto_Paint

 

event for the PictureBox

(object sender,

 

control.

System.Windows.Forms.PaintEventArgs e)

 

{

 

 

 

 

 

4

Convert the Tag property to a

Bitmap image = pboxPhoto.Tag as Bitmap;

 

Bitmap object.

 

 

 

 

5

If no image is present, clear the

if (image == null)

 

graphics area.

{

 

 

// No image, just clear the graphics

 

 

e.Graphics.Clear(SystemColors.Control);

 

 

return;

 

 

}

 

 

 

6

Retrieve the current

// Load the current photo

 

Photograph object from the

BindingManagerBase bm

 

binding manager.

= BindingContext[_album];

 

Photograph photo = bm.Current as Photograph;

 

 

 

How-to

 

 

Use the Current property of

 

 

the binding manager object.

 

 

 

 

7

If for some reason the current

Rectangle r = pboxPhoto.ClientRectangle;

 

Photograph is not found,

if (photo == null)

 

simply draw the image in the

{

 

// Something is wrong, just draw the image

 

client rectangle.

 

e.Graphics.DrawImage(image, r);

 

 

}

 

 

 

8

If the photo is found, draw the

else

 

image in the scaled rectangle.

{

 

 

// Paint the image with proper aspect ratio

 

 

e.Graphics.DrawImage(

 

 

image, photo.ScaleToFit(r));

 

 

}

 

 

}

 

 

 

The photograph is now scaled and displayed within our PictureBox control. Note how we used the entire client rectangle rather than the rectangle provided in the Graphics object to ensure that the entire control area is redrawn.

It is worth noting here that binding to the Tag property is not strictly required. Since we access the current Photograph object here, we could simply load the associated image correctly. There may be a slight performance advantage here in having the image already available in the Tag property, so we selected the approach shown in the table.

600

CHAPTER 17 DATA BINDING

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