1. Trang chủ
  2. » Công Nghệ Thông Tin

The book of visual basic 2005 net insight for classic vb developers 2006 - phần 7 potx

51 303 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 51
Dung lượng 0,97 MB

Nội dung

Dealing with Data: Files, Printing, and XML 289 ' Add an event handler dynamically. AddHandler Watch.Created, AddressOf NewFile The subroutine below reacts when a new file is added to the monitored directory, retrieving its name using the supplied parameters. Figure 9-5 shows this code in action. Public Sub NewFile(ByVal sender As Object, _ ByVal e As System.IO.FileSystemEventArgs) lstNewFiles.Items.Add("New file: " & e.Name) End Sub Figure 9-5: Monitoring the file system NOTE To make this example more robust, you need a dash of multithreading smarts. That’s because the FileWatcher fires events on another thread, not the thread that’s controlling your application. To prevent a conflict, consider the code shown in the FileWatcher project (included with the sample code), which uses the Control.Invoke() method. This technique is explained in detail in Chapter 11. File System Change Events It’s easy to handle the Created, Deleted, and Renamed events of the FileSystemWatcher. However, the Changed event is a little trickier, because there are a huge number of types of possible changes that can be detected as Windows performs its ordinary housekeeping. In order to handle the Change event properly, you need to set the types of changes you want to monitor in the NotifyFilter property (specifying each one using a value from the NotifyFilters enumeration). You can combine several different types of monitored actions using the bitwise Or keyword. bvb_02.book Page 289 Thursday, March 30, 2006 12:39 PM 290 Chapter 9 ' Watch for changes to file size or file name. Watch.NotifyFilter = NotifyFilters.FileName Or NotifyFilters.Size Object Serialization The Bob program we looked at earlier used a relatively crude (but effective) mode of handmade serialization. The .NET platform also introduces an auto- matic form of serialization that you can use to store the information in an object (see Figure 9-6). This technique can be used in your own applications, and the .NET Framework also relies on it to transmit a class to a remote com- ponent on another computer if you are designing a distributed application. Figure 9-6: .NET serialization Implementing this type of serialization in one of your classes is extremely easy. All you need to do is flag your class as serializable with a special attribute. The client code (the code using your class) can then decide what type of serialization it wants to use, and store the object as a binary file, as an XML file, or as something else. Here is the Person class, simplified and remade to support automatic serialization. <Serializable> Public Class SerializablePerson Public Name As String Public Age As Integer Public Height As Integer Public Sub New() End Sub Public Sub New(ByVal Name As String, ByVal Age As String, _ ByVal Height As String) Me.Name = Name Me.Age = Age Me.Height = Height End Sub End Class BinaryFormatter or SoapFormatter Object Your Serializable Object Backing Store (a file, memory block, etc.) Deserialize() Serialize() Stream Stream bvb_02.book Page 290 Thursday, March 30, 2006 12:39 PM Dealing with Data: Files, Printing, and XML 291 Did you notice the differences? There are exactly two modifications: The SaveToFile() and LoadFromFile() methods were removed. This time, .NET will do the serialization for us automatically. The Class now has a <Serializable> attribute in the first line, where it is declared. This tells .NET that it is allowed to persist and restore instances of this class to and from any type of stream. Storing and Retrieving a Serializable Object To serialize the class, you need to use a serializer from the System.Runtime .Serialization branch of the class library. The best choice is the BinaryFormatter class, which is found in the System.Runtime.Serialization.Formatters.Binary namespace. To get off to a good start, we’ll import the namespace: Imports System.Runtime.Serialization.Formatters.Binary The BinaryFormatter class has two important, straightforward methods: Serialize() and Deserialize(). Serialize() takes an object, converts it to a compact binary format, and sends it to the specified stream. Dim Bob As New SerializablePerson("Bob", 34, 5.25) Dim fs As New FileStream("c:\bob.dat", FileMode.Create) Dim bf As New BinaryFormatter() ' Store Bob with the help of the BinaryFormatter. bf.Serialize(fs, Bob) fs.Close() Deserialize() retrieves the information from the stream and reconstructs the object. The object is returned to life as the generic System.Object type, so you need to use the CType() function to give it back its proper identity. Dim fs As New FileStream("c:\bob.dat", FileMode.Open) Dim bf As New BinaryFormatter() ' Retrieve Bob with the help of the BinaryFormatter. Dim Bob As SerializablePerson Bob = CType(bf.Deserialize(fs), SerializablePerson) ' Verify Bob's information. MessageBox.Show(Bob.Name) fs.Close() bvb_02.book Page 291 Thursday, March 30, 2006 12:39 PM 292 Chapter 9 That’s really all you need. It’s very simple (although some error-handling code would be nice to guard against any possible file access errors). You can try out this code with the SerializablePerson2 project. Fine-Tuned Serialization In some cases, you might have a class that can be partly but not entirely serialized. For example, you might have certain member variables that correspond to information that won’t have any meaning on another computer or at another time, such as a low-level handle to a Window. (A handle is a number that the operating system uses to uniquely identify a currently running window. It’s abstracted away by .NET, but it’s heavily used by the Windows API.) To deal with this case, just mark the nonserializable information with a <NonSerialized> attribute. This indicates to .NET that it should ignore this value when persisting instances of the class. When the serialized object is reconstructed, this variable will return to its default uninitialized value. In the PartlySerializablePerson class shown below, any information about Height will not be retained. When a PartlySerializablePerson is deserialized, its Height will return to zero. <Serializable> Public Class PartlySerializablePerson Public Name As String Public Age As Integer <NonSerialized> Public Height As Integer End Class A more interesting situation occurs with serializable objects that con- tain references to other objects. In this case, the referenced object must also support serialization, or the whole process will fail. The .NET Framework will store and restore all the subobjects automatically. This can end up persisting a large amount of information. For example, if you have a SerializablePerson object that contains a reference to another SerializablePerson object, which in turn references a third SerializablePerson object, you will end up with three times the data you expected. If you don’t want to serialize a linked object, you can always mark the as appropriate variables with <NonSerialized>. Cloning Objects with Serialization Serialization also provides us with an interesting way to clone an object. You may remember (from Chapter 6) that cloning an object is not always easy, particularly if the class contains multiple subobjects. Cloning an object with serialization basically consists of copying the object into a memory stream and then retrieving it from the stream as a new object. You can insert this cloning code directly into your object, as shown here. <Serializable> Public Class ClonableSerializablePerson Implements ICloneable bvb_02.book Page 292 Thursday, March 30, 2006 12:39 PM Dealing with Data: Files, Printing, and XML 293 Public Name As String Public Age As Integer Public Height As Integer Public Function Clone() As Object Implements ICloneable.Clone Dim ms As New MemoryStream() Dim bf As New BinaryFormatter() ' No more MemberwiseClone()! bf.Serialize(ms, Me) Clone = bf.Deserialize(ms) ms.Close() End Function End Class This code will duplicate every contained object. In some cases this will be too much, and you’ll need a more controlled approach that involves manually copying some objects, as shown in Chapter 6. Printing and Previewing Data Printing in Visual Basic 2005 is quite different than it was before .NET. The main difference is that the printing process is now asynchronous. In Visual Basic 6, the computer would be temporarily frozen while output commands were sent to the printer. While this worked fine for most applications, pro- grams that required lengthy print operations would be unresponsive while the information was being sent. Visual Basic 2005 introduces an event-based printing model. Here’s how it works. First, you create a PrintDocument object. You then start printing by calling the Print() method. At this point the PrintDocument begins firing the PrintPage event, once for each page that needs to be printed. The PrintPage event provides your code with a PrintPageEventArgs object which contains information about the printer, together with methods you can use to print text and pictures. Your code handles the PrintPage event in an event handler, outputs the next page, and then decides whether or not another page is required. If it is, the event handler sets PrintPageEventArgs.HasMorePages to True, and the PrintPage event is fired again a short time after. If not, it sets PrintPageEventArgs.HasMorePages to False, and the process ends. Because pages are printed one at a time, your program remains responsive. However, another consequence of the page-by-page printing model is that your code needs to keep track of where it currently is in the printout, so that the next time the PrintPage event is fired, the printing resumes at the proper position. How can you keep track of your position? This part is up to you, but a common way is to use a variable that stores the current page. Then, each time the PrintPage event fires you can check the variable in a conditional block (an If/End If statement or a Select Case statement) and print the bvb_02.book Page 293 Thursday, March 30, 2006 12:39 PM 294 Chapter 9 corresponding text. In many programs, a printout actually consists of a single large block of information that spans as many pages as needed, in which case it makes more sense to store an offset into that block. For example, if you are printing rows of report information from a database, you might store the current row number. If you are printing a text stream, you might keep track of the character position. Printing Data from an Array The following example uses a simple array. The reference to the PrintDocument object and the code for the PrintPage event are contained in a form class named PrintStatus. Imports System.Drawing.Printing Public Class PrintStatus Private WithEvents MyDoc As PrintDocument Private PageNumber As Integer Private Offset As Integer Private PrintData(100) As String Private PrintFont As New Font("Arial", 10) Private Sub PrintStatus_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Fill PrintData array with bogus information. Dim i As Integer For i = 0 To 100 PrintData(i) = "This is line number " & i + 1 & ". " PrintData(i) &= "It originates from the array element number " PrintData(i) &= i & "." Next MyDoc = New PrintDocument() End Sub Private Sub cmdPrint_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdPrint.Click PageNumber = 0 Offset = 0 MyDoc.Print() End Sub Private Sub MyDoc_PrintPage(ByVal sender As Object, _ ByVal e As PrintPageEventArgs) Handles MyDoc.PrintPage ' (Printing code left out.) End Sub End Class This form contains member variables that store the current page number and print offset, as well as the actual print data. In the Load event, the print data array is filled with sample information, and a bvb_02.book Page 294 Thursday, March 30, 2006 12:39 PM Dealing with Data: Files, Printing, and XML 295 printer is selected. Asynchronous printing is started when the user clicks the cmdPrint button. The actual printing takes place once the PrintPage event occurs. The first PrintPage event will occur almost instantaneously (as you can verify by insert- ing a breakpoint). Inside the event handler, you use the PrintPageEventArgs object to perform the actual printing: Private Sub MyDoc_PrintPage(ByVal sender As Object, _ ByVal e As PrintPageEventArgs) Handles MyDoc.PrintPage ' Determine the line height. Dim LineHeight As Single = PrintFont.GetHeight(e.Graphics) ' Create variables to hold position on page. Dim x As Single = e.MarginBounds.Left Dim y As Single = e.MarginBounds.Top ' Increment global page counter and refresh display. PageNumber += 1 lblStatus.Text = "Print Page " & PageNumber ' Print all the information that can fit on the page. Do e.Graphics.DrawString(PrintData(Offset), PrintFont, _ Brushes.Black, x, y) Offset += 1 y += LineHeight Loop Until (y + LineHeight) > e.MarginBounds.Bottom Or _ Offset > PrintData.GetUpperBound(0) ' Determine if another page is needed. If Offset < PrintData.GetUpperBound(0) Then e.HasMorePages = True End Sub In our example, the PrintPage event will occur twice—once for each of the two required pages. The event handler code begins by defining a font that will be used for printing and determining how large the line spacing should be to accommodate that font. The next two lines create variables to track the current position on the page. By default, printing begins at the page margin border. Remember, with printing routines, you need to take care of all the details; for example, you have to break up long strings into multiple lines of text, and explicitly set the position on the page every time you print a new line. The following two lines increment the page counter and display the page information in a label control in the window. This keeps the user informed about print progress. The Do/Loop block contains the actual print- ing code. This code uses the DrawString() method to print out a single line of text at the indicated coordinates. The code then increments our Offset value (which represents the line number) and moves the y coordinate down one bvb_02.book Page 295 Thursday, March 30, 2006 12:39 PM 296 Chapter 9 full line space. (Coordinates are measured from zero, starting at the upper left corner.) Before continuing to print the next line, the event handler checks for two possible conditions: Loop Until (y + LineHeight) > e.MarginBounds.Bottom Or _ Offset > PrintData.GetUpperBound(0) In order to continue, there must be space left on the current page for the next line, and there must be data left to print. (The value of PrintOffset can’t be larger than the upper boundary of our array, because then it would indicate a row that doesn’t exist.) The final line of our event handler determines whether there is still unprinted data left in the array. If there is, the e.HasMorePages property must be set to True. Otherwise, .NET will assume that our printing is completed and won’t bother to call the PrintPage event again. Printing Wrapped Text Some applications print out extremely long strings of text that break over more than one printed line. There are several different ways to handle this scenario. You can split the text into a series of separate lines before printing and then load the information into an array or collection. Alternatively, you can split the information into lines as you print it, depending on the width of the current page. This approach, known as “wrapping” the text, is often the required solution if you are printing mixed information that combines text, graphics, and other data, or if you allow the user to select the font size you use. .NET has a handy shortcut that handles this job. You simply need to use the Graphics.DrawString() method with x and y coordinates and a bounding rectangle. The rectangle represents the bounds inside of which you want the text to be printed. The x and y coordinates tell .NET where the top-left corner of the rectangle should be placed on the page. The text is automatically wrapped to fit the rectangle. The online samples for this chapter include a WrappedPrinting example that demonstrates the difference between wrapping and not wrapping (Figure 9-7). Figure 9-7: The WrappedPrinting project bvb_02.book Page 296 Thursday, March 30, 2006 12:39 PM Dealing with Data: Files, Printing, and XML 297 When printing text that doesn’t need to wrap, you simply specify the top-left coordinate where printing should start. The line extends to the right indefinitely and will continue off the edge of the page without causing an error. e.Graphics.DrawString(txtData.Text, MyFont, Brushes.Black, _ x, y, StringFormat.GenericDefault) To print wrapped text, you supply a Rectangle object instead of the coor- dinates. (The Rectangle object is defined in the System.Drawing namespace.) You can create a rectangle by hand, but this example simply uses the already available rectangle that represents the page margins, e.MarginBounds. The default page margins are set according to the printer settings. You’ll learn how to tweak these details in the next section. e.Graphics.DrawString(txtData.Text, MyFont, Brushes.Black, _ e.MarginBounds, StringFormat.GenericDefault) Figure 9-8 shows the result. Figure 9-8: The wrapped printout You can also modify the StringFormat parameter that you use with the DrawString() method to specify different options for the text alignment. For example, you can create a new StringFormat object and configure it to auto- matically center the printed text with this code: Dim CustomFormat As StringFormat = StringFormat.GenericDefault ' Center the block of text on the page (vertically). CustomFormat.LineAlignment = StringAlignment.Center ' Center the individual lines of text (horizontally). CustomFormat.Alignment = StringAlignment.Center e.Graphics.DrawString(txtData.Text, MyFont, Brushes.Black, _ e.MarginBounds, CustomFormat) bvb_02.book Page 297 Thursday, March 30, 2006 12:39 PM 298 Chapter 9 Printing Pictures The previous example demonstrates how to print the most common type of information: formatted text. You can also use other methods from the Graphics object with equal ease to print different types of information, including basic shapes (through methods like DrawRectangle), lines (DrawLine), and even pictures: e.Graphics.DrawImage(Image.FromFile("c:\MyFolder\MyFile.bmp"), x, y) When you use the Graphics object, you are actually making use of the GDI+ technology in the .NET Framework. The interesting part is that this standard Graphics object is reused in more than one place. For example, you use DrawImage() and DrawString() to place output on a printed page in exactly the same way that you use them to place output onto a Windows form. Even though you are less likely to use GDI+ to manually create window output, it’s good to know that the skills you use in printing can be reused with on-screen graphics if required. NOTE If you don’t want to store the graphic in a separate file, you can embed it directly into your application assembly using the resource techniques described in Chapter 7. Print Settings The previous printing examples use the default margin and page size settings. However, this approach is rarely flexible enough for a real application. Most users expect to have control of at least some basic printing options, including the ability to choose the specific printer they want to use. In Visual Basic 6, this was a fairly straightforward but manual task that involved presenting the user with a Print Options window, retrieving the settings they chose, and applying them to the Print object before beginning a print operation. In .NET, the process has been made much easier. All the tools you need for displaying standard Print Options and Page Settings windows are provided in convenient classes from .NET’s class library. Before you display a Printer Settings window, you associate it with the PrintDocument object that you are using. Then, any configuration that the user performs will be automatically incorporated in the appropriate object ( MyDoc in our example) and in the PrinterEventArgs object (called e) provided in the PrintPage event. Private Sub cmdConfigure_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdConfigure.Click Dim dlgSettings As New PrintDialog() dlgSettings.Document = MyDoc dlgSettings.ShowDialog() End Sub bvb_02.book Page 298 Thursday, March 30, 2006 12:39 PM [...]... retrieves another set of attributes and applies them instead (SetSize) The path name is hard-coded for this application as Software\AcmeInsurance\PolicyMaker A key is added to the path using the name of the form, to help group the settings for different forms (as in Software\AcmeInsurance\PolicyMaker\Main) Depending on how your application works, this may not be an appropriate way to store information For example,... ADO .NET is based on disconnected DataSets, with no support for server-side cursors While ADO was a “best of breed” standard Microsoft component built out of COM, ADO .NET is an inhabitant of the NET class library, designed for managed code, and integrated with XML As you’ll see in this chapter, ADO .NET is one more NET revolution New in NET The NET languages require a new database technology ADO, the. .. signature at the top of a file when creating the file, and then verifying the signature when reading the file to make sure that the file really does belong to your application Or, it might calculate a checksum based on the data in the file (A simple example would be adding together all the product numbers in a purchase order file and then storing this total at the end of the file When reading the file,... NOTE For more convenient access to the registry, you can use the My.Computer.Registry branch of the My object XML Files The real story with XML and Visual Basic 2005 is how the NET platform uses XML behind the scenes to accomplish a variety of tasks You’ll discover in Chapters 10 and 13 how NET uses XML to provide communication between web services and clients, and to store relational data in ADO .NET. .. add the contents of the OrderID field of each record processed into a list box named lstOrders, you would use this code in the body of the loop: ' Add the data from the OrderID field lstOrders.Items.Add(reader("OrderID")) Alternatively, you could perform the same task using the field index: lstOrders.Items.Add(reader(0)) Da t ab as es a nd A DO NET 323 bvb_02 .book Page 324 Thursday, March 30, 2006. .. combination of the XmlDocument and XmlNodeReader classes, which is what we will do here XmlNodeReader is designed to read nodes Each time you use the XmlNodeReader.Read() method, it loads the information for the next node in the XML file into the XmlNodeReader object (using the Name, Value, and NoteType properties) The Read() method returns True if the operation is successful, and False if the end of the file... if you create different forms dynamically, and are in the habit of giving them the same name, their settings will overwrite each other in the registry Inside each form-specific key, four values are specified: Height, Width, Top, and Left Imports Microsoft.Win32 Public Class RegistryReader Public Shared Sub SaveSize(ByVal frm As System.Windows.Forms.Form) ' The next line creates the key only if it doesn't... access the full registry, you had to resort to the Windows API Thankfully, VB 2005 has improved the picture once again To access the Windows registry in NET, you use two classes from the Microsoft.Win32 namespace: Registry and RegistryKey Registry provides your starting point into one of the main divisions of the registry Typically, you will use the Registry.CurrentUser property to work with the registry... project in Visual Studio Double-click the My Project node in the Solution Explorer 3 Click the Settings tab You’ll see a list of all the settings that are defined for this application (By default, this list is empty.) Add a new setting to the list Give it a descriptive name (for example, Northwind), use Application for the scope (so that the setting is common for all users), and supply the full connection... ap te r 9 bvb_02 .book Page 305 Thursday, March 30, 2006 12:39 PM Figure 9-1 1: XML in Internet Explorer XML tags indicate content, not format In fact, XML documents are used almost exclusively for storing data, because the data is described in a way that makes it easy for other programs (and even humans) to interpret it Part of the complexity of using XML is understanding the many standards for writing . Sub NOTE For more convenient access to the registry, you can use the My.Computer.Registry branch of the My object. XML Files The real story with XML and Visual Basic 2005 is how the .NET platform. rectangle. The rectangle represents the bounds inside of which you want the text to be printed. The x and y coordinates tell .NET where the top-left corner of the rectangle should be placed on the. Previewing Data Printing in Visual Basic 2005 is quite different than it was before .NET. The main difference is that the printing process is now asynchronous. In Visual Basic 6, the computer would

Ngày đăng: 13/08/2014, 08:21