Exploring the GrabAPicture Application (Part 10)

In the previous post, Exploring the GrabAPicture Application (Part 9), I discussed the command line interface for the GrabAPicture application. This week we begin looking at the GUI controls for frmMain, which is the first form the user sees. Here’s how this form appears when you’re using it.

GrabAPicture1001

It’s important to analyze the functionality of this interface. You can divide the tasks that it performs into the following areas:

 

  • Dialog Box Management (dialog box configuration and Close button)
  • Direct Wallpaper Access (Wallpaper Location text box, radio buttons in the Style Selection group, and Set Value button)
  • Wallpaper Database Access (Random Sources button)
  • Command Line Interface Configuration (Use Random Local Sources and Use Random Remote Sources check boxes)

It’s helpful to perform such an analysis of any interface you design. Doing so makes it possible to look for ways to make the application more efficient (by reducing the number of controls when possible) and easier to use (by placing some controls on another form). Theoretically, you could make this application more efficient by making the Command Line Interface Configuration part of the command line interface. Likewise, you could make it easier to use by placing the Direct Wallpaper Access controls on a separate form. However, the form is relatively simple as is and most people won’t have trouble using it. This post discusses all four areas in the order shown.

Dialog Box Management

Part of the dialog box management task is to configure the form when you first start the application. This application, like most applications out there, uses the Load event to perform the task. In this case, it means using the frmMain_Load() handler shown here.

' Current application settings.
Dim CurrentSettings As GrabAPictureSettings
 
Private Sub frmMain_Load(ByVal sender As Object, _
                         ByVal e As System.EventArgs) _
                         Handles MyBase.Load
 
   ' Create an instance of the WinWallpaper class to gain
   ' access to the current Windows settings.
   Dim Wallpaper As New WinWallpaper
 
   ' Get the current wallpaper URI and display it.
   txtWallpaperURI.Text = _
      Wallpaper.WallpaperURI.ToString()
 
   ' Get the current wallpaper style.
   Select Case Wallpaper.Style
      Case WinWallpaper.Styles.Stretched
         rbStretched.Checked = True
         rbCentered.Checked = False
         rbTiled.Checked = False
      Case WinWallpaper.Styles.Centered
         rbStretched.Checked = False
         rbCentered.Checked = True
         rbTiled.Checked = False
      Case WinWallpaper.Styles.Tiled
         rbStretched.Checked = False
         rbCentered.Checked = False
         rbTiled.Checked = True
   End Select
 
   ' Initialize the stored settings.
   CurrentSettings = GrabAPictureSettings.LoadSettings()
 
   ' Configure the controls.
   If Not CurrentSettings Is Nothing Then
      cbLocal.Checked = CurrentSettings.LocalChecked
      cbRemote.Checked = CurrentSettings.RemoteChecked
   Else
      ' Define default settings.
      CurrentSettings = New GrabAPictureSettings
   End If
End Sub

The code begins by creating a global GrabAPictureSettings object, CurrentSettings, that contains the content of the user’s wallpaper database. Normally, I prefer not to use global variables because they’re prone to all sorts of problems, especially when the code your working with could be multithreaded (reentrancy problems are extremely tough to debug). However, using the global variable in this case shouldn’t be a problem.

The frmMain_Load() method begins by creating creating a WinWallpaper object, Wallpaper. The reason you must create this variable every time you need it is because external applications can indeed change the wallpaper. If you didn’t create these variable every time, the user could end up using old information. Admittedly, this wouldn’t be much of a problem when it comes to wallpaper, but it’s good practice to follow in every application where an external application could affect your application’s data. The code then uses Wallpaper to set the Wallpaper Location and Style Selection settings of the application.

At this point, the code instantiates CurrentSettings. You know from the Exploring the GrabAPicture Application (Part 8) post that the LoadSettings() method returns Nothing when the user hasn’t saved any settings before, which is always the case when the user starts the application for the first time. This code checks whether CurrentSettings is Nothing after the LoadSettings() call. If so, the application creates an entirely new GrabAPictureSettings object. Otherwise, it uses the existing settings to configure the Use Random Local Sources and Use Random Remote Sources check boxes.

The configuration process is complete. The only remaining task is to provide a means of ending the application. Clicking Close, which is btnCancel, performs this task. Here is the code used to accomplish this task.

Private Sub btnCancel_Click(ByVal sender As System.Object, _
                            ByVal e As System.EventArgs) _
                         Handles btnCancel.Click
   ' Exit with the Cancel code.
   Environment.Exit(DialogResult.Cancel)
End Sub

Notice that this application doesn’t simply close the form. Instead, it calls Environment.Exit() with an ending value of DialogResult.Cancel. The purpose for this approach is that GrabAPicture is designed for use in a batch file. Any batch file you create can rely on the ErrorLevel value to detect how the application exited (see my Understanding the Connection Between Application Output and ErrorLevel post for details), so you must provide a value to detect. It’s good practice not to simply close the form anyway—you should always provide some sort of exit value.

Direct Wallpaper Access

This application provides the means for the user to simply type the full path to a piece of wallpaper, set the style, and then click Set Value to use it. In short, the user could decide not to use any of the random option at all. There is a command line interface feature to allow the user to work this way as well. Here is the code used to provide direct wallpaper access.

Private Sub rbStretched_CheckedChanged(ByVal sender As System.Object, _
                                       ByVal e As System.EventArgs) _
                                    Handles rbStretched.CheckedChanged
   Dim Wallpaper As New WinWallpaper
 
   ' Set the new style.
   Wallpaper.Style = WinWallpaper.Styles.Stretched
End Sub
 
Private Sub rbCentered_CheckedChanged(ByVal sender As System.Object, _
                                      ByVal e As System.EventArgs) _
                                   Handles rbCentered.CheckedChanged
   Dim Wallpaper As New WinWallpaper
 
   ' Set the new style.
   Wallpaper.Style = WinWallpaper.Styles.Centered
End Sub
 
Private Sub rbTiled_CheckedChanged(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) _
                                Handles rbTiled.CheckedChanged
   Dim Wallpaper As New WinWallpaper
 
   ' Set the new style.
   Wallpaper.Style = WinWallpaper.Styles.Tiled
End Sub
 
Private Sub btnOK_Click(ByVal sender As System.Object, _
                        ByVal e As System.EventArgs) _
                        Handles btnOK.Click
   Dim Wallpaper As New WinWallpaper
 
   ' Set the wallpaper URI.
   Try
      Wallpaper.WallpaperURI = New Uri(txtWallpaperURI.Text)
   Catch UFE As UriFormatException
      MessageBox.Show("Provide a valid URI!" & vbCrLf & _
                      UFE.Message)
   Catch ex As Exception
      MessageBox.Show(ex.Message)
   End Try
 
   ' Exit with the OK code.
   'Environment.Exit(DialogResult.OK)
End Sub

Each of the check box event handlers performs essentially the same task. They set the Wallpaper.Style property to the appropriate Wallpaper.Styles value. A possible enhancement to these event handlers would be to force them to make the change automatically. The user still has to click Set Value to make the change. After testing this example out with a few willing volunteers, I found it was less confusing if the application waited for the user to click Set Value, rather than have the change occur automatically. The strategy you pursue will likely depend on your users and the complexity of the application you create.

Clicking Set Value calls the btnOK_Click() event handler. In this case, the code performs a conversion of the text the user types into Wallpaper Location to a Uri object. This is the first potential source of an exception. If the text doesn’t form a useful URI, then the application generates a UriFormatException. The assignment could possibly generate other exceptions, so the code adds a general exception handler as well. The act of making the assignment changes the Desktop wallpaper.

This example used to close the dialog box automatically after the user clicked Set Value. A few testers complained about this practice—preferring to experiment with the wallpaper. Consequently, the current version of the example has this piece of code commented out. If you choose to enable the old method of doing things, you’ll want to note that the application will exit with a value of DialogResult.OK in this case, which you can easily trap using the ErrorLevel value in a batch file.

Wallpaper Database Access

Wallpaper database configuration is performed using a combination of frmConfigure (to display the database content) and frmAddEdit (to make changes). When the user clicks Random Sources, the application calls btnConfigure_Click(). This event handler displays frmConfigure so that the user can see the list of configured wallpapers and make changes to the list. The following code shows how to perform this task.

Private Sub btnConfigure_Click(ByVal sender As System.Object, _
                               ByVal e As System.EventArgs) _
                               Handles btnConfigure.Click
   ' Create a new configuration dialog box.
   Dim Config As New frmConfigure
 
   ' Display the dialog box on screen.
   Config.ShowDialog(Me)
 
   ' Update the current configuration settings.
   CurrentSettings = GrabAPictureSettings.LoadSettings()
 
   ' Get the current wallpaper URI and display it.
   Dim Wallpaper As New WinWallpaper
   txtWallpaperURI.Text = _
      Wallpaper.WallpaperURI.ToString()
 
   ' Define default settings if necessary.
   If CurrentSettings Is Nothing Then
      CurrentSettings = New GrabAPictureSettings
   End If
End Sub

The first part of this code looks like any dialog box display code you’ve used in the past. The example creates a frmConfigure object, Config. It then calls the Config.ShowDialog() method to display the dialog box with the current dialog box as the parent. A lot of developers create subordinate dialog boxes without the proper parent, which makes the application behave incorrectly. Always assign a parent to a subordinate dialog box when the subordinate should be dismissed before going back to the parent.

On return, the user has supposedly made changes to the wallpaper database. With this in mind, the application updates CurrentSettings to match the new wallpaper list. In addition, the wallpaper itself may have changed, so the application updates the wallpaper settings as well. The last bit of code may seem confusing at first, but think about it for a minute. The user might have deleted the last wallpaper entry from the list. If this is the case, the application needs to set CurrentSettings to a new instance of GrabAPictureSettings.

Command Line Interface Configuration

The final task for frmMain is at hand. The application needs some method of configuring the command line interface to use the correct random settings. The Use Random Local Settings and Use Random Remote Settings check boxes perform the task. Here is the code for the associated event handlers.

Private Sub cbLocal_CheckStateChanged(ByVal sender As Object, _
                                      ByVal e As System.EventArgs) _
                                   Handles cbLocal.CheckStateChanged
   ' Save the current checked state.
   CurrentSettings.LocalChecked = cbLocal.Checked
   GrabAPictureSettings.SaveSettings(CurrentSettings)
End Sub
 
Private Sub cbRemote_CheckStateChanged(ByVal sender As Object, _
                                       ByVal e As System.EventArgs) _
                                    Handles cbRemote.CheckStateChanged
   ' Save the current checked state.
   CurrentSettings.RemoteChecked = cbRemote.Checked
   GrabAPictureSettings.SaveSettings(CurrentSettings)
End Sub

As you can see, the application sets the correct CurrentSettings property, and then calls GrabAPictureSettings.SaveSettings() with the CurrentSettings object as an argument to save the settings to disk. The result is a change to the user’s disk-based XML settings. A side effect of this process is that the user’s wallpaper list is also saved to disk. The SaveSettings() method saves all of the settings at one time.

Well, that’s it for frmMain. The next post will look at frmConfigure. Until that time, please let me know if you have any questions at John@JohnMuellerBooks.com. You can find the next post in this series at Exploring the GrabAPicture Application (Part 11).

Author: John

John Mueller is a freelance author and technical editor. He has writing in his blood, having produced 99 books and over 600 articles to date. The topics range from networking to artificial intelligence and from database management to heads-down programming. Some of his current books include a Web security book, discussions of how to manage big data using data science, a Windows command -line reference, and a book that shows how to build your own custom PC. His technical editing skills have helped over more than 67 authors refine the content of their manuscripts. John has provided technical editing services to both Data Based Advisor and Coast Compute magazines. He has also contributed articles to magazines such as Software Quality Connection, DevSource, InformIT, SQL Server Professional, Visual C++ Developer, Hard Core Visual Basic, asp.netPRO, Software Test and Performance, and Visual Basic Developer. Be sure to read John’s blog at http://blog.johnmuellerbooks.com/. When John isn’t working at the computer, you can find him outside in the garden, cutting wood, or generally enjoying nature. John also likes making wine and knitting. When not occupied with anything else, he makes glycerin soap and candles, which comes in handy for gift baskets. You can reach John on the Internet at John@JohnMuellerBooks.com. John is also setting up a website at http://www.johnmuellerbooks.com/. Feel free to take a look and make suggestions on how he can improve it.