Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning Visual Basic 2005 (2006)

.pdf
Скачиваний:
220
Добавлен:
17.08.2013
Размер:
14.97 Mб
Скачать

Chapter 19

Figure 19-3

Note that this will work with a normal Button control, too.

2.Double-click the LinkLabel control. This will create an event handler for the LinkClicked event. Add this code:

Private Sub lnkSendEmail_LinkClicked(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) _ Handles lnkSendEmail.LinkClicked

‘ start the e-mail client...

System.Diagnostics.Process.Start(“mailto:” & txtEmail.Text)

End Sub

3.Run the project and click the Load button. Ensure you have an e-mail address entered in the Email field and then click the Send Email link. Your e-mail client should display a new mail message with the To: field filled in with your e-mail address.

How It Works

Windows has a built-in capability to decode Internet addresses and fire up the programs that are associated with them.

When an e-mail client such as Outlook or Outlook Express is installed, it registers a protocol called mailto with Windows, just as, when a Web browser such as Internet Explorer is installed, it registers the protocol HTTP, familiar to anyone who browses the Web.

If you were to close the mail message, click the Start button from the Windows task bar, select Run, enter mailto: followed by the e-mail address from your program, and then click OK, the same mail message would appear.

In your code, you take the current value of the txtEmail field and put mailto: at the beginning. This turns the e-mail address into a URL. You then call the shared Start method on the System. Diagnostics.Process class, passing it this URL:

626

Visual Basic 2005 and XML

Private Sub lnkSendEmail_LinkClicked(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) _ Handles lnkSendEmail.LinkClicked

‘ start the e-mail client...

System.Diagnostics.Process.Start(“mailto:” & txtEmail.Text) End Sub

The Start method behaves in exactly the same way as the Run dialog box does. Both tap into Windows’ built-in URL-decoding functionality. In this case, you’ve used this functionality to integrate your application with Outlook. However, if you’d specified a protocol of http: rather than mailto:, your application could have opened a Web page. Likewise, if you had supplied a path to a Word document, or Excel spreadsheet, the application could open those too. Note that when you’re working with a file, you don’t need to supply a protocol — for example, you only need to do this:

c:\My Files\My Budget.xls

Creating a List of Addresses

The purpose of this Try It Out is to build an application that allows you to store a list of addresses in XML. At the moment you can successfully load just one address, so now you have to turn your attention to managing a list of addresses.

The class you’re going to build to do this is called AddressBook. This class will inherit from SerializableData because ultimately you want to get to a point where you can tell the AddressBook object to load and save itself to the XML file without you having to do anything.

Try It Out

Creating AddressBook

1.

2.

Using Solution Explorer, create a new class called AddressBook.

First, add this namespace declaration:

Imports System.Xml.Serialization

Public Class AddressBook

End Class

3.Second, set the class to inherit from SerializableData:

Imports System.Xml.Serialization

Public Class AddressBook

Inherits SerializableData

End Class

4.To store the addresses, you’re going to use a System.Collections.ArrayList object. You also need a method that you can use to create new addresses in the list. Add the following highlighted member and method to the class:

Imports System.Xml.Serialization Public Class AddressBook

Inherits SerializableData ‘ members...

Public Items As New ArrayList()

‘ AddAddress - add a new address to the book...

627

Chapter 19

Public Function AddAddress() As Address ‘ create one...

Dim newAddress As New Address()

add it to the list...

Items.Add(newAddress)

return the address...

Return newAddress

End Function

End Class

5.Open the code editor for Form1. Add these members to the top of the class:

Public Class Form1

‘ members...

Public AddressBook As AddressBook

Private _currentAddressIndex As Integer

6.Next, add this property to Form1:

CurrentAddress - property for the current address...

ReadOnly Property CurrentAddress() As Address Get

Return AddressBook.Items(CurrentAddressIndex - 1) End Get

End Property

7.Then add this property to Form1:

CurrentAddressIndex - property for the current address...

Property CurrentAddressIndex() As Integer Get

Return _currentAddressIndex End Get

Set(ByVal Value As Integer)

set the address...

_currentAddressIndex = Value

update the display...

PopulateFormFromAddress(CurrentAddress)

set the label...

lblAddressNumber.Text = _

_currentAddressIndex & “ of “ & AddressBook.Items.Count

End Set

End Property

8.Double-click the form to create the Load event for Form1 and add this highlighted code to the handler:

Private Sub Form1_Load(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles MyBase.Load

load the address book...

AddressBook = _

SerializableData.Load(DataFilename, GetType(AddressBook))

if the address book only contains one item, add a new one...

If AddressBook.Items.Count = 0 Then AddressBook.AddAddress()

select the first item in the list...

CurrentAddressIndex = 1

End Sub

628

Visual Basic 2005 and XML

9.Now that you can load the address book, you need to be able to save the changes. From the left drop-down list, select (Form1 Events). From the right list, select FormClosed. Add the highlighted code to the event handler, and also add the SaveChanges and UpdateCurrentAddress methods:

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed

‘ save the changes...

UpdateCurrentAddress()

SaveChanges() End Sub

‘ SaveChanges - save the address book to an XML file...

Public Sub SaveChanges()

‘ tell the address book to save itself...

AddressBook.Save(DataFilename)

End Sub

UpdateCurrentAddress - make sure the book has the current

values currently entered into the form...

Private Sub UpdateCurrentAddress()

PopulateAddressFromForm(CurrentAddress)

End Sub

Before you run the project, it’s very important that you delete the existing AddressBook.xml file. If you don’t, XmlSerializer will try to load an AddressBook object from a file containing an Address object, and an exception will be thrown.

10.Run the project. Don’t bother entering any information into the form, because the save routine won’t work — we’ve deliberately introduced a bug to illustrate an issue with XmlSerializer. Close the form, and you should see the exception thrown as shown in Figure 19-4.

Figure 19-4

How It Works (or Why It Doesn’t!)

When the form is loaded, the first thing you do is ask SerializableData to create a new AddressBook object from the AddressBook.xml file. Because you deleted this before you ran the project, this file won’t exist, and, as you recall, you rigged the Load method so that if the file didn’t exist it would just create an instance of whatever class you asked for. In this case, you get an AddressBook:

629

Chapter 19

Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load

‘ load the address book...

AddressBook = _

SerializableData.Load(DataFilename, GetType(AddressBook))

However, the new address book won’t have any addresses in it. You ask AddressBook to create a new address if the list is empty:

‘ if the address book only contains one item, add a new one...

If AddressBook.Items.Count = 0 Then AddressBook.AddAddress()

At this point, either you’ll have an AddressBook object that’s been loaded from the file and therefore contains a set of Address objects, or you’ll have a new AddressBook object that contains one, blank address. You set the CurrentAddressIndex property to 1, meaning the first item in the list:

‘ select the first item in the list...

CurrentAddressIndex = 1 End Sub

The setter for the CurrentAddressIndex property does a number of things. First, it updates the private

_currentAddressIndex member:

‘ CurrentAddressIndex - property for the current address...

Property CurrentAddressIndex() As Integer Get

Return _currentAddressIndex End Get

Set(ByVal Value As Integer) ‘ set the address...

_currentAddressIndex = Value

Then the setter uses the CurrentAddress property to get the Address object that corresponds to whatever _currentAddressIndex is set to. This Address object is passed to PopulateFormFromAddress, whose job it is to update the display:

‘ update the display...

PopulateFormFromAddress(CurrentAddress)

Finally, it changes the lblAddressNumber control so that it displays the current record number:

‘ set the label...

lblAddressNumber.Text = _

_currentAddressIndex & “ of “ & AddressBook.Items.Count End Set

End Property

You’ll just quickly look at CurrentAddress. This property’s job is to turn an integer index into the corresponding Address object stored in AddressBook. However, because AddressBook works on the basis of an ArrayList object that numbers items from 0, and your application starts numbering items at 1, you have to decrement your index value by 1 to get the matching value from AddressBook:

630

Visual Basic 2005 and XML

‘ CurrentAddress - property for the current address...

ReadOnly Property CurrentAddress() As Address Get

Return AddressBook.Items(CurrentAddressIndex - 1) End Get

End Property

All good so far, but why is XmlSerializer throwing an exception? Well the problems occur when you close the application. This fires the FormClosed method, which ultimately calls the Save method of

AddressBook.

As you know, to save an object to disk, XmlSerializer walks through each of the properties looking for ones that are readable and writable. So far, you’ve used XmlSerializer only with System.String, but when the object comes across a property that uses a complex type, such as Address, it uses the same principle — in other words, it looks through all of the properties that the complex type has. If properties on that that object return complex types, it will drill down again. What it’s doing is looking for simple types that it knows how to turn into text and write to the XML document.

However, some types cannot be turned into text, and at this point XmlSerializer chokes. The ArrayList object that you’re using to store a list of addresses had some properties that cannot be converted to text, which is the reason the exception is being thrown. What you need to do is provide an alternative property that XmlSerializer can hook into in order to get a list of addresses and tell it not to bother trying to serialize the ArrayList.

Ignoring Members

Although XmlSerializer cannot cope with certain data types, it has no problems with arrays. You’ve also seen that XmlSerializer has no problems with your Address class, simply because this object doesn’t have any properties of a type that XmlSerializer cannot support. In the next Try It Out, you’ll provide an alternative property that returns an array of Address objects and tells XmlSerializer to keep away from the Items property because XmlSerializer cannot deal with ArrayList objects.

Try It Out

Ignoring Members

1.Open the code editor for AddressBook. Find the Items property and prefix it with the

System.Xml.Serialization.XmlIgnore attribute:

Public Class AddressBook Inherits SerializableData ‘ members...

<XmlIgnore()>Public Items As New ArrayList

2.Now, add this new property to the AddressBook class:

Addresses - property that works with the items

collection as an array...

Public Property Addresses() As Address()

Get

‘ create a new array...

Dim addressArray(Items.Count - 1) As Address

Items.CopyTo(addressArray)

631

Chapter 19

Return addressArray

End Get

Set(ByVal Value As Address())

reset the arraylist...

Items.Clear()

did you get anything?

If Not Value Is Nothing Then

‘ go through the array and populate items...

Dim address As Address

For Each address In Value

Items.Add(address)

Next

End If

End Set

End Property

3.Run the project and then close the application; this time everything functions correctly. Run the project again, and this time around, enter some data into the address fields. Close the application and you should now find that AddressBook.xml does contain data. (We’ve removed the xmlns and ?xml values for clarity here.)

<AddressBook>

<Addresses>

<Address>

<FirstName>Bryan</FirstName>

<LastName>Newsome</LastName>

<CompanyName>Wiley</CompanyName> <Address1>123 Main St</Address1> <Address2 />

<City>Big City</City> <Region>SE</Region> <PostalCode>28222</PostalCode> <Country>USA</Country> <Email>Bryan@email.com</Email>

</Address>

</Addresses>

</AddressBook>

How It Works

The XML that got saved into your file proves that your approach works, but why?

At this point, your AddressBook object has two properties: Items and Addresses. Both are read/write properties, so both are going to be examined as candidates for serialization by XmlSerializer. As you know, Items returns an ArrayList object, and Addresses returns an array of Address objects.

However, you have now marked Items with the XmlIgnore attribute. This means, not surprisingly, that XmlSerializer will ignore the property, despite the fact that it is readable and writable. Instead, the serializer will move on to the Addresses property.

632

Visual Basic 2005 and XML

The Get portion of the Addresses property is what interests you. All you do is create a new array of Address objects and use the CopyTo method on the ArrayList to populate it:

Addresses - property that works with the items

collection as an array...

Public Property Addresses() As Address() Get

‘ create a new array...

Dim addressArray(Items.Count - 1) As Address Items.CopyTo(addressArray)

Return addressArray End Get

Set(ByVal Value As Address())

...

End Set End Property

When XmlSerializer gets an array of objects that it can deal with, all it does is iterate through the array, serializing each of these contained objects in turn. You can see this in the XML that you received: The structure of the XML contained within the Addresses element exactly matches the structure of the XML you saw when you tested the process and wrote a single Address object to the file:

<AddressBook>

<Addresses>

<Address>

<FirstName>Bryan</FirstName>

<LastName>Newsome</LastName>

<CompanyName>Wiley</CompanyName> <Address1>123 Main St</Address1> <Address2 />

<City>Big City</City> <Region>SE</Region> <PostalCode>28222</PostalCode> <Country>USA</Country> <Email>Bryan@email.com</Email>

</Address>

</Addresses>

</AddressBook>

Loading Addresses

If you’re lucky, loading addresses should just work! Close the program and run the project again. You will see a record as shown in Figure 19-5.

You already set up the project to load the address book the first time you ran the project after creating the AddressBook class itself. This time, however, AddressBook.Load can find a file on the disk, and so, rather than creating a blank object, it’s getting XmlSerializer to deserialize the lot. As XmlSerializer has no problems writing arrays, you can assume that it has no problem reading them.

633

Chapter 19

Figure 19-5

It’s the Set portion of the Addresses property that does the magic this time. One thing you have to be careful of with this property is that, if you are passed a blank array (in other words. Nothing), you want to prevent exceptions being thrown:

Addresses - property that works with the items

collection as an array...

Public Property Addresses() As Address()

Get

...

End Get

Set(ByVal Value As Address())

reset the arraylist...

Items.Clear()

did you get anything?

If Not Value Is Nothing Then

‘ go through the array and populate items...

Dim address As Address

For Each address In Value

Items.Add(address)

Next

End If

End Set

End Property

For each of the values in the array, all you have to do is take each one in turn and add it to the list.

Adding New Addresses

Next, you’ll look at how you can add new addresses to the list. In this Try It Out, you’ll be adding four new buttons to your form. Two buttons will allow you to navigate through the list of addresses, and two buttons will allow you to add and delete addresses.

634

Visual Basic 2005 and XML

Try It Out

Adding New Addresses

1.Open the Form Designer for Form1 and disable the Load and Save buttons before adding the four new buttons shown in Figure 19-6.

Figure 19-6

2.Name the buttons in turn btnPrevious, btnNext, btnNew, and btnDelete and set their Text properties to Previous, Next, New, and Delete, respectively.

3.Double-click the New button to create a Click handler. Add the highlighted line to the event handler, and also add the AddNewAddress method:

Private Sub btnNew_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles btnNew.Click

AddNewAddress()

End Sub

Public Function AddNewAddress() As Address

save the current address...

UpdateCurrentAddress()

create a new address...

Dim newAddress As Address = AddressBook.AddAddress ‘ update the display...

CurrentAddressIndex = AddressBook.Items.Count ‘ return the new address...

Return newAddress End Function

4.Run the project. Click New and a new address record will be created. Enter a new address:

5.Close the program and the changes will be saved. Open up AddressBook.xml, and you should see the new address.

635