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

Visual CSharp .NET Developer's Handbook (2002) [eng]

.pdf
Скачиваний:
35
Добавлен:
16.08.2013
Размер:
4.94 Mб
Скачать

{

// Display a helpful message. MessageBox.Show("Clicked File | New");

}

private void toolBar1_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)

{

// Display a message for the button. MessageBox.Show("Clicked the " +

e.Button.Tag.ToString() +

"button.");

//Do something special for the New button. if (e.Button.Tag.ToString() == "New")

mnuFileNew_Click(this, e);

}

Writing Windows Applications

Most of the applications you'll create with C# will likely have a GUI. Windows applications use a GUI to make the user experience better, reduce training time, and enable the user to work more efficiently. As previously mentioned, the standard Windows application types include dialog-based, SDI, and MDI. Within those three categories are an infinite array of subtypes. For example, a dialog-based application can act as a training aid or as a utility application.

The following sections examine several types of Windows applications. These examples are unique in some way—they don't necessarily follow the business-focused applications we'll discuss as the book progresses. For example, the dialog-based example also shows you how to create a utility that appears within the Taskbar Tray (Notification Area) instead of the Taskbar. This example also serves to demonstrate some of the ways in which Microsoft has improved Visual Studio .NET. Creating such an application in the previous version of Visual Studio would have required substantially more work.

Dialog-Based Example

Although we've already viewed several in the book, one type of dialog-based example that we haven't discussed is the use of the Taskbar Tray for keeping utility applications active and out of the way. Previous versions of Visual Studio tended to make it difficult to create such an application. Visual Studio .NET provides several new components and controls, plus some additional form settings, that make the task almost trivial. This example is a basic timer application. It displays a message at specific intervals that remind you to take a break from typing. The source code for this example is in the \Chapter 05\TypingBuddy folder on the CD.

This example name is Typing Buddy. It appears in the Taskbar Tray until the predefined interval elapses. At that moment, the program displays a message box advising the user it's time for a break. You'll need to use three special controls to make this application work properly:

NotifyIcon

ContextMenu

Timer

In addition to adding new components and controls to the application, you'll also need to configure the main form differently from other projects. Make sure you set the ShowInTaskbar property to false so the application moves to the Taskbar Tray when minimized. You'll also want to start the application minimized using the WindowState property so it waits in the background without bothering the user. Figure 5.7 shows the form configuration for this example.

Figure 5.7: The Typing Buddy example includes some basic configuration controls.

This type of application relies on a certain level of automation. You want the application to start automatically when the machine starts and continue operation until the user shuts the machine down. Of course, you also need to be sure the user configures the application properly for use. The solution is to check for an existing set of configuration options, and then create them if needed. Listing 5.3 shows the frmMain_Load(), which is responsible for getting the application running.

Listing 5.3: Creating the Initial Application Configuration

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

{

// See if the user has worked with this program before. if (!InRegistry())

{

//If not, display a welcome message and the configuration

//screen.

MessageBox.Show("Welcome to Typing Buddy!\r\n" + "You need to set an initial typing value by " + "entering a time interval and clicking Set.", "Welcome Message",

MessageBoxButtons.OK,

MessageBoxIcon.Information); WindowState = FormWindowState.Normal;

}

else

// Start the timer. Timer.Start();

}

private bool InRegistry()

 

{

// Typing Buddy storage key.

RegistryKey oSub;

//Check for the application key; null if not available. oSub = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(

"Software\\TypingBuddy");

//Check to see if the key exists.

if (oSub != null)

{

// Set the timer value.

TimeLeft = (int)oSub.GetValue("TimeSet");

//Close the registry keys. oSub.Close();

//Return a success value. return true;

}

// Return a failure value. return false;

}

The main job of frmMain_Load() is to ensure the application has settings to use when it begins running. The choice is one of displaying the configuration screen if this is the first use of the application or starting the timer so the application begins running in the background without user interaction. The means for making this determination is the InRegistry() method.

The InRegistry() uses the simple technique of opening the application's registry entry. If oSub is null, then the registry key doesn't exist and the application isn't configured for use. On the other hand, if the registry key does exist, InRegistry() reads the contents of the TimeSet value and places them in TimeLeft.

You might wonder why InRegistry() doesn't start the timer as well. The current coding enables the application to use InRegistry() from various locations to check the configuration state of the application. We'll also use this method when configuring the application.

It's time to look at the code for the three buttons shown in Figure 5.7. The Pause context menu option also relies on this code. All three buttons and the context menu control that state of the application. Listing 5.4 shows you how.

Listing 5.4: The Set Time, Pause, and Quit Buttons Control Application State

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

{

RegistryKey oReg; // Hive Registry Key RegistryKey oSub; // Company storage key.

// Set the controls.

TimeLeft = (Int32)TimerValue.Value * 60; Timer.Start();

// Add the information to the registry.

if (!InRegistry())

{

//Open the HKEY_LOCAL_MACHINE\Software key for writing. oReg = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(

"Software",

true);

//Write the company subkey. oReg.CreateSubKey("TypingBuddy");

//Close the registry key.

oReg.Close();

}

// Write the default value.

oSub = Microsoft.Win32.Registry.LocalMachine.OpenSubKey( "Software\\TypingBuddy",

true);

oSub.SetValue("TimeSet", TimeLeft);

// Close the registry keys. oSub.Close();

}

bool IsPaused; // Is the timer paused?

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

{

if (IsPaused)

{

//Start the timer. IsPaused = false; Timer.Start();

//Display status information. btnPause.Text = "Pause"; mnuPause.Text = "Pause";

}

else

{

//Stop the timer. IsPaused = true; Timer.Stop();

//Display status information. btnPause.Text = "Restart"; mnuPause.Text = "Restart"; notifyIcon1.Text = "Timer is Paused";

}

}

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

{

DialogResult RetValue; // Users selection.

// Display an exit message.

RetValue = MessageBox.Show("Are you sure you want to exit?" + "\r\n(Program will minimize if you select No.)",

"Exit Application",

MessageBoxButtons.YesNo,

MessageBoxIcon.Question,

MessageBoxDefaultButton.Button2);

if (RetValue == DialogResult.Yes) // Exit the application. Close();

else

// Minimize the form.

WindowState = FormWindowState.Minimized;

}

The btnSet_Click() begins by setting the control values for the application and starting the timer. This ensures the user will see changes immediately and won't need to wait for registry operations to complete.

The next task is to determine if the registry already contains settings for the control. If the registry doesn't contain the required entries, the application makes them, and then closes the registry key. The final step is to write the new timer value into the registry so that it's ready for the next time the user starts the application.

The btnPause_Click() method toggles the application between two operational states. The paused state sets the timer off and changes the buttons and notification icon balloon help to reflect the change. Likewise, when the application starts again, the timer state is changed and so are the buttons. The notification icon balloon help changes automatically as part of the timer functionality, so there's no need to change it here. The IsPaused field keeps track of the current state.

Normally, when the user selects Quit, the application exits. However, some users don't realize that minimizing the application removes the dialog display from view. Because of this problem, you'll want to write a special routine for the btnQuit_Click() method. Notice that this is one of the first times we've used the return value from the MessageBox.Show() method to control application flow. In addition, the example shows how to create a default button for the dialog box associated with the MessageBox.Show() call.

The return value determines if the application ends or merely minimizes. Notice the use of various built-in values for this example. You'll use the special DialogResult type to hold the user selection and compare it to values in the DialogResult class. The FormWindowState enumeration also enables you to set the WindowState property of the form with ease. Note that we use the FormWindowState enumeration for the notifyIcon1_DoubleClick() method as well (see the source code on the CD for details).

The "heartbeat" of this application is the system clock referenced by the Timer control. This control has a single event associated with it, Tick. Listing 5.5 shows the code for the Timer_Tick() method.

Listing 5.5: The Timer_Tick() Method is Central to Typing Buddy Operation

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

{

DialogResult RetVal; // Return value from dialog.

//Set the timer value. TimeLeft—;

//See if the timer has expired. if (TimeLeft == 0)

{

//Reset the controls.

TimeLeft = (Int32)TimerValue.Value * 60;

Timer.Stop();

// Display the stop typing message.

RetVal = MessageBox.Show("Time to stop typing! Relax!" + "\r\nPress OK to Restart" +

"\r\nor Cancel to exit program.", "TypingBuddy Alert", MessageBoxButtons.OKCancel, MessageBoxIcon.Hand);

if (RetVal == DialogResult.OK) // Restart the timer. Timer.Start();

else

// Exit the application. Close();

}

else

{

// Update the time left indicator. TypingTime.Text = TimeLeft.ToString(); notifyIcon1.Text = "Time Left in Seconds: " +

TimeLeft.ToString();

}

}

As you can see, the operation isn't complex. During a normal update cycle, the Timer_Tick() method decrements the TimeLeft field and performs a comparison to see if TimeLeft is at 0. Normally, the comparison will fail, so the Timer_Tick() method will update the text values for the TypingTime text box (shown in Figure 5.7) and notifyIcon1. Updating the notifyIcon1.Text property changes the value of the balloon help, so the user can hover their mouse over the icon to see how much time they have until their next break.

When TimeLeft does make it to 0, Timer_Tick() resets the TimeLeft field and turns off the timer. It displays a "break" message to the user. When the user clicks OK, the whole timing process begins again. Of course, the user can always select Cancel to end the application.

SDI Example

At one time, it was sufficient for an application to provide an appealing display of data located on either the user's hard drive or the company network. In fact, many users were happy if they could understand the display and use it to do something useful. Today, a user wants more in the way of presentation and feedback. Many applications today use simple animation to get a point across to the user.

Microsoft is at the forefront of the changes to the user interface—at least in some areas. For example, Windows Explorer uses animations to show a file moving from one location to another. The presence of action gives the user a feeling of comfort about the application, even when it doesn't do anything other than display the animated sequence.

Unfortunately, creating animations is difficult in Windows if you use the traditional methods. Fortunately, some solutions make it easy to add animation to websites and now those animations can appear on the desktop as well. One of many ways to create animations it to use an animated GIF file. Animated GIFs have been around for quite some time. You see them all the time on the Internet. All of those little animations you see on websites are very likely animated GIFs. An animated GIF file works by placing multiple images in a single file. Timing and presentation commands separate the images. Each command tells the displaying application how to present the next frame of the animation and how long to present it.

Tip You can see animated GIFs in action on many Internet sites. One of the more interesting places to look is http://www.wanderers2.com/rose/animate.html. The site offers an index of sites you can visit to see various kinds of animated GIFs. Looking at a variety of sites will help you understand what works and what doesn't. You can also download an animated GIF Wizard, make your own animated GIF online, and learn all about how to make animated GIFs.

Most developers know that Visual Studio doesn't support the GIF file format as a standard graphic—at least, Visual Studio didn't provide GIF support in the past. If you tried loading an animated GIF to your project, you'd receive an error message saying the GIF was damaged or simply incorrect. The Visual Studio .NET IDE still doesn't support GIF files directly. Even if you can view the GIF inside Internet Explorer, Visual Studio .NET will steadfastly refuse to load it.

Tip You'll find many applications you can use to create an animated GIF for your application. I designed the animated GIF for the example using the GIF Construction Set from Alchemy Mind Works. You can download this product from several places. The best place is from the vendor at http://www.mindworkshop.com/alchemy/gifcon.html. Another good alternative is Paint Shop Pro 7 from JASC (http://www.jasc.com). Note that earlier versions of the product don't include this feature and that the current product isn't quite as capable as the GIF Construction Set.

The good news is that you can combine the new Bitmap object with the Animator object to load and support animated GIFs within your application—even desktop applications. The Bitmap object acts as a container for the graphic, while the Animator provides the means to page through each image and presents it in turn. When you combine the two objects, you see the animation sequence on screen without resorting to the odd programming techniques of the past. Listing 5.6 shows the code you'll need to make this application work. (The full source code for this example appears in the \Chapter 05\AniDisplay folder on the CD.)

Listing 5.6: Adding Animation to the Desktop Isn't Hard in Visual Studio .NET

 

 

 

 

private

String

File2Open;

// Selected Filename

private

Bitmap

AniGIF;

// Animated GIF to Display

private

int

DrawSelect;

// Drawing mode.

private

bool

Animated;

// Animation active.

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

{

OpenFileDialog Dlg = new OpenFileDialog(); // File Open Dialog

// Set up the File Open Dialog

Dlg.Filter = "Graphics Interchange Format File (*.gif)|*.gif"; Dlg.DefaultExt = ".gif";

Dlg.Title = "Open File Dialog";

//Display the File Open Dialog and obtain the name of a file and

//the file information.

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

{

File2Open = Dlg.FileName;

}

else

{

// If the user didn't select anything, return. return;

}

// Open the document.

AniGIF = new Bitmap(File2Open);

//Set the drawing mode. DrawSelect = 2; Animated = false;

//Change the title bar.

Form1.ActiveForm.Text = "GIF Animation Example - " + File2Open;

// Force a redraw of the display area. DisplayArea.Invalidate();

}

private void DisplayArea_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

{

// Can't draw the image if the bitmap is null. if (AniGIF != null)

{

switch (DrawSelect)

{

case 1:

// Animate the GIF file. ImageAnimator.UpdateFrames(); e.Graphics.DrawImage(AniGIF,

4,

4,

AniGIF.Width,

AniGIF.Height);

break;

case 2:

// Draw a graphic normally. e.Graphics.DrawImage(AniGIF,

4,

4,

AniGIF.Width,

AniGIF.Height);

break;

}

}

}

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

{

// Initialize the animating the first time the user selects it. if (!Animated)

{

ImageAnimator.Animate(AniGIF,

new EventHandler(NextFrame));

Animated = true;

}

// Select a drawing mode. DrawSelect = 1;

}

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

{

// Select a drawing mode that stops the animation. DrawSelect = 2;

}

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

{

// Force OnPaint() to redraw the animation. DisplayArea.Invalidate();

}

The application requires the use of four fields, as shown in the listing. It's important to make the bitmap resources universally available within the class to ensure you can perform all of the required timing for the animation. However, nothing outside the class should require access to the bitmap. If so, you should use properties to ensure you can track everything that affects the bitmap and animation.

When the user opens the application, they'll see a typical SDI display. The first task is to open a file. (See the CommandLine2 example in Chapter 4 for code that grabs the filename from the command line.) The code shown in mnuFileOpen_Click() looks much the same as the code for the CommandLine2 example.

Near the end of the mnuFileOpen_Click() method, you'll see five lines of code that load the bitmap into the drawing area. The four fields now contain the values needed to present the bitmap on screen. The DisplayArea.Invalidate() call forces a redraw of the DisplayArea panel so that you can see the bitmap loaded.

The DisplayArea_Paint() method can present the bitmap in two ways. The method of choice when the bitmap first loads is as a static image. As you can see from the case 2 code, System.Windows.Forms.PaintEventArgs provides the application with a drawing area that it uses to display the bitmap on screen. All the code has to specify is the Bitmap object (AniGIF) and the drawing rectangle. (We'll discuss the case 1 code in a moment.)

The mnuAnimationStart_Click() method starts the animation process. Notice that we haven't created anything to animate the image yet and we won't—it comes as part of your application. The code verified the animation status of the bitmap before it adds the NextFrame event handler to the existing ImageAnimator object.

Remember from the Delegate example in Chapter 4 that you can continue to add new event handlers to a delegate. The delegate simply calls them each in turn. If you continue adding the same event handler to the ImageAnimator, the animation will continue to speed up until you can no longer see the animation—each event handler will advance the frame by one. Instead of seeing one frame per interval, you might begin seeing three or four. That's why the animated state check is so important. The ImageAnimator will call NextFrame() each time the command inside the animated GIF states it's time to display another image.

The interesting thing about the NextFrame() method is that the only task it performs is invalidating the display area. This action invokes the OnPaint() method. However, in this case, the OnPaint() method will use the case 1 code. The ImageAnimator appears in this code as well. In this case, you're asking the ImageAnimator to access the next frame in the animation. When OnPaint() draws the image, it will actually draw the current image in the animated series, rather than the first image contained in the file as normal.

Stopping the animation is relatively easy. All you need to do is select the case 2 image drawing routine in OnPaint(). The animation will stop at the current point in its execution. Theoretically, you could work with this example to slow the animation down, so you could view it one frame at a time. Figure 5.8 shows the AniDisplay application with the AnimatedTime.GIF file loaded. You'll find AnimatedTime.GIF and its components in the \Chapter 05\Animated Graphic folder of the CD.

Figure 5.8: Animated GIFs can provide extra pizzazz in a desktop application.

Application Debugging

The debugger is the most important tool in your arsenal for ensuring the code works as you intended after the application is released. The problem for some developers is that the