Exploring the GrabAPicture Application (Part 11)

The previous post in this series, Exploring the GrabAPicture Application (Part 10), discussed frmMain, which allows the user to perform application configuration and manually configure the Desktop wallpaper. The focus of this application, however, is automated configuration, which is the purview of frmConfigure—the topic of discussion today. The user relies on frmConfigure to add, edit, delete, and select either local or remote graphic sources for the Desktop wallpaper as shown here.

GrabAPicture1101

There are really two sets of four buttons here: the local set and the remote set. Each set performs the same sets of tasks with a different part of the same XML database. The information about this database appears in previous posts, but the main post of concern is Exploring the GrabAPicture Application (Part 8). When adding or editing a database element, the user sees a form similar to the one shown here:

GrabAPicture1102

Even though frmConfigure looks a little complex, there are really only three groups of tasks to perform:

 

  • Load the current database
  • Manage local database resources
  • Manage remote database resources

Load the Current Database

The first task is to load the current database. In this case, the application creates and configures a global variable that provides access to the preconfigured wallpaper choices as shown here.

' Current application settings.
Dim CurrentSettings As GrabAPictureSettings
 
Private Sub frmConfigure_Load(ByVal sender As Object, _
                              ByVal e As System.EventArgs) _
                              Handles MyBase.Load
   ' Initialize the stored settings.
   CurrentSettings = GrabAPictureSettings.LoadSettings()
 
   ' Configure the controls.
   If Not CurrentSettings Is Nothing Then
      If Not CurrentSettings.LocalUris Is Nothing Then
         For Each Item As LocalUri In CurrentSettings.LocalUris
            lstLocal.Items.Add(Item.Name)
         Next
      End If
      If Not CurrentSettings.RemoteUris Is Nothing Then
         For Each Item As RemoteUri In CurrentSettings.RemoteUris
            lstRemote.Items.Add(Item.Name)
         Next
      End If
   Else
      ' Define default settings.
      CurrentSettings = New GrabAPictureSettings
   End If
End Sub

The global variable, CurrentSettings, is of type GrabAPictureSettings. The application begins trying to fill CurrentSettings by calling GrabAPictureSettings.LoadSettings(). When this is the first application use and the user hasn’t added any wallpaper yet, the result of this call is that CurrentSettings equals Nothing, so the application creates a new database by creating a new GrabAPictureSettings database.

When there are settings to use, the application must determine which settings are available. The database may only contain local or remote settings, but not both. The next step determines whether CurrentSettings.LocalUris contains Nothing. If there are local settings, the application places the name of each of these entries in the lstLocal list box. The application performs a similar task for the remote settings. As a result, the user sees the names of all of the available local and remote wallpaper sources when the application dialog first appears on screen.

Manage Local Database Resources

After the form appears on screen, the user sees the local resources at the top of the form in the Local Sources list. The user can add, edit, delete, or select local resources using the associated controls as shown here:

Private Sub btnLAdd_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
                       Handles btnLAdd.Click
   ' Create a form to add the record.
   Dim AddItem As New frmAddEdit
 
   ' Set the resources when available.
   If Not CurrentSettings.LocalUris Is Nothing Then
      AddItem.Resources = CurrentSettings.LocalUris
   End If
 
   ' If the user makes changes, add the information to
   ' the settings database and display it on screen.
   If AddItem.ShowDialog(Me) = DialogResult.OK Then
      CurrentSettings.LocalUris = AddItem.Resources
      GrabAPictureSettings.SaveSettings(CurrentSettings)
      lstLocal.Items.Add(AddItem.Resources(AddItem.Resources.Length - 1).Name)
   End If
End Sub
 
Private Sub btnLEdit_Click(ByVal sender As System.Object, _
                           ByVal e As System.EventArgs) _
                        Handles btnLEdit.Click
   ' Make sure the user has selected an entry.
   If lstLocal.SelectedIndex = -1 Then
      MessageBox.Show("Select an entry to edit.")
      Return
   End If
 
   ' Create a new form for editing the data.
   Dim EditItem As New frmAddEdit("Edit Local Resource", "&Edit")
   EditItem.IsEdit = True
 
   ' Make sure there is data to edit.
   If CurrentSettings.LocalUris Is Nothing Then
      MessageBox.Show("No Records to Edit")
      Return
   Else
      ' Provide the records and choose the one to edit.
      EditItem.Resources = CurrentSettings.LocalUris
      EditItem.RecordNumber = lstLocal.SelectedIndex
   End If
 
   ' If the user changes the data, update the settings
   ' database and show changes on screen.
   If EditItem.ShowDialog(Me) = DialogResult.OK Then
      CurrentSettings.LocalUris = EditItem.Resources
      GrabAPictureSettings.SaveSettings(CurrentSettings)
      lstLocal.Items(lstLocal.SelectedIndex) = _
         EditItem.Resources(lstLocal.SelectedIndex).Name
   End If
End Sub
 
Private Sub btnLDelete_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                             Handles btnLDelete.Click
   ' Make sure the user has selected an entry.
   If lstLocal.SelectedIndex = -1 Then
      MessageBox.Show("Select an entry to remove.")
      Return
   End If
 
   ' Create a temporary record array.
   Dim Temp(CurrentSettings.LocalUris.Length - 2) As LocalUri
 
   ' Remove the requested record.
   Dim Count As Int32 = 0
   For Each Item As LocalUri In CurrentSettings.LocalUris
 
      ' Add the record if it doesn't match the target.
      If Not Item.Name = lstLocal.Items(lstLocal.SelectedIndex) Then
         Temp(Count) = Item
         Count += 1
      End If
   Next
 
   ' Update the settings.
   CurrentSettings.LocalUris = Temp
   GrabAPictureSettings.SaveSettings(CurrentSettings)
 
   ' Update the display.
   lstLocal.Items.RemoveAt(lstLocal.SelectedIndex)
End Sub
 
Private Sub btnLSelect_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                          Handles btnLSelect.Click
   ' Wallpaper manipulation class.
   Dim Wallpaper As New WinWallpaper
 
   ' Choose the wallpaper style.
   Select Case CurrentSettings.LocalUris(lstLocal.SelectedIndex).Style
      Case WinWallpaper.Styles.Stretched
         Wallpaper.Style = WinWallpaper.Styles.Stretched
      Case WinWallpaper.Styles.Centered
         Wallpaper.Style = WinWallpaper.Styles.Centered
      Case WinWallpaper.Styles.Tiled
         Wallpaper.Style = WinWallpaper.Styles.Tiled
   End Select
 
   ' Create a wallpaper URI.
   Dim NewUri As New Uri( _
      CurrentSettings.LocalUris(lstLocal.SelectedIndex).Location)
 
   ' Set the wallpaper location.
   Wallpaper.WallpaperURI = NewUri
End Sub

Let’s look at each of these event handlers in turn. The btnLAdd_Click() event handler is the simplest of the group. It begins by creating a new frmAddEdit using the default constructor. When there are local resources to provide in CurrentSettings.LocalUris, the program supplies them to the AddItem.Resources property. At this point, the application displays the dialog box. If the user clicks Add, rather than Cancel, in the Add Local Resource dialog box (which produces a return value of DialogResult.OK), the application obtains the updated list of resources from AddItem.Resources and places them in CurrentSettings.LocalUris. The application then saves the database by calling GrabAPictureSettings.SaveSettings() with CurrentSettings. Finally, the application displays the newly added item in the Local Sources list.

The btnLEdit_Click() event handler begins by checking whether the user has actually selected an entry to edit by checking lstLocal.SelectedIndex (a value of -1 indicates no selection). If not, the application displays an error message and exits. It then creates a new frmAddEdit() using a special constructor that allows modification of the title bar text and the btnAddEdit.Text property, so that the resulting dialog box is completely customized. You’ll find that there are actually four constructors for frmAddEdit:

 

  • No modification
  • Modify the title
  • Modify both title and btnAddEdit.Text property
  • Modify both title and btnAddEdit.Text property in a remote source setting

Providing this sort of customization reduces the work you need to do and yet provides a better interface for the user. The next step is to set the EditItem.IsEdit property to True, which tells frmAddEdit to fill the fields with data to edit, rather than present a blank form. Again, this is the type of customization that will actually save you time later. (A second property, EditItem.IsRemote, determines whether the data should come from the local or remote wallpaper database—the default is to use the local database.)

The application has to make two decisions when editing a record. First, it has to check whether there is a record to edit. If there aren’t any records, the application displays a dialog box stating as much and exits. Second, it must determine which record to edit. The code passes the value in lstLocal.SelectedIndex onto EditItem.RecordNumber. As the application is currently configured, the contents of lstLocal must precisely match the contents of CurrentRecords, which means that lstLocal remains unsorted. A future update will make it possible to sort the list, if desired, but for now, the application is designed for simplicity of understanding, rather than aesthetic appeal. The remainder of the btnLEdit_Click() code works just like the btnLAdd_Click() event handler, with the result that any changes are immediately saved to the database.

Deleting a record means checking for the required record in the database by name and then removing that particular record. The btnLDelete_Click() event handler begins by checking whether the user has actually selected a record (lstLocal.SelectedIndex must equal a value other than -1). It then creates a temporary array, Temp, of type LocalUri, that will hold one record less than the current number of records. The next bit of code may look confusing at first, but all it really does is move all of the records in CurrentSettings.LocalUris that don’t have the name of the selected record as defined by lstLocal.Items(lstLocal.SelectedIndex) to Temp. The result is Temp has one less record than CurrentSettings.LocalUris when the process is finished. The code then copies the new list of records from Temp to CurrentSettings.LocalUris and saves the result to disk. The final step is to remove the name from the Local Sources list.

Selecting a specific local source is relatively straightforward. The btnLSelect_Click() event handler begins by creating Wallpaper, which is type WinWallpaper. It’s important to remember that modifying the Wallpaper object automatically changes the Desktop wallpaper. You saw how this works in the previous post and it works the same here. The application relies on the index provided by the user’s selection, lstLocal.SelectedIndex, to select the correct wallpaper in CurrentSettings.LocalUris. The application then sets the wallpaper style and provides it with a location in the form of a Uri object, NewUri. The result is that the user sees the selected wallpaper on screen.

Manage Remote Database Resources

The code for managing the remote database resources is almost precisely the same as the code for managing local database resources. In fact, the modifications are mainly cosmetic, such as changes to the frmAddEdit title and button text. However, there are a few distinct differences to note as shown here.

Private Sub btnRAdd_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
                       Handles btnRAdd.Click
   ' Create a form to add the record.
   Dim AddItem As New frmAddEdit("Add Remote Resource", "&Add", True)
 
   ' Set the resources when available.
   If Not CurrentSettings.RemoteUris Is Nothing Then
      AddItem.RemoteSources = CurrentSettings.RemoteUris
   End If
 
   ' If the user makes changes, add the information to
   ' the settings database and display it on screen.
   If AddItem.ShowDialog(Me) = DialogResult.OK Then
      CurrentSettings.RemoteUris = AddItem.RemoteSources
      GrabAPictureSettings.SaveSettings(CurrentSettings)
      lstRemote.Items.Add( _
         AddItem.RemoteSources(AddItem.RemoteSources.Length - 1).Name)
   End If
End Sub
 
Private Sub btnEdit_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
                       Handles btnEdit.Click
   ' Make sure the user has selected an entry.
   If lstRemote.SelectedIndex = -1 Then
      MessageBox.Show("Select an entry to edit.")
      Return
   End If
 
   ' Create a new form for editing the data.
   Dim EditItem As New frmAddEdit("Edit Remote Resource", "&Edit", True)
   EditItem.IsEdit = True
 
   ' Make sure there is data to edit.
   If CurrentSettings.RemoteUris Is Nothing Then
      MessageBox.Show("No Records to Edit")
      Return
   Else
      ' Provide the records and choose the one to edit.
      EditItem.RemoteSources = CurrentSettings.RemoteUris
      EditItem.RecordNumber = lstRemote.SelectedIndex
   End If
 
   ' If the user changes the data, update the settings
   ' database and show changes on screen.
   If EditItem.ShowDialog(Me) = DialogResult.OK Then
      CurrentSettings.RemoteUris = EditItem.RemoteSources
      GrabAPictureSettings.SaveSettings(CurrentSettings)
      lstRemote.Items(lstRemote.SelectedIndex) = _
         EditItem.RemoteSources(lstRemote.SelectedIndex).Name
   End If
End Sub
 
Private Sub btnRDelete_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                          Handles btnRDelete.Click
   ' Make sure the user has selected an entry.
   If lstRemote.SelectedIndex = -1 Then
      MessageBox.Show("Select an entry to remove.")
      Return
   End If
 
   ' Create a temporary record array.
   Dim Temp(CurrentSettings.RemoteUris.Length - 2) As RemoteUri
 
   ' Remove the requested record.
   Dim Count As Int32 = 0
   For Each Item As RemoteUri In CurrentSettings.RemoteUris
 
      ' Add the record if it doesn't match the target.
      If Not Item.Name = lstRemote.Items(lstRemote.SelectedIndex) Then
         Temp(Count) = Item
         Count += 1
      End If
   Next
 
   ' Update the settings.
   CurrentSettings.RemoteUris = Temp
   GrabAPictureSettings.SaveSettings(CurrentSettings)
 
   ' Update the display.
   lstRemote.Items.RemoveAt(lstRemote.SelectedIndex)
End Sub
 
Private Sub btnSelect_Click(ByVal sender As System.Object, _
                            ByVal e As System.EventArgs) _
                         Handles btnSelect.Click
   ' Wallpaper manipulation class.
   Dim Wallpaper As New WinWallpaper
 
   ' Choose the wallpaper style.
   Select Case CurrentSettings.RemoteUris(lstRemote.SelectedIndex).Style
      Case WinWallpaper.Styles.Stretched
         Wallpaper.Style = WinWallpaper.Styles.Stretched
      Case WinWallpaper.Styles.Centered
         Wallpaper.Style = WinWallpaper.Styles.Centered
      Case WinWallpaper.Styles.Tiled
         Wallpaper.Style = WinWallpaper.Styles.Tiled
   End Select
 
   ' Create a wallpaper URI.
   Dim NewUri As New Uri( _
      CurrentSettings.RemoteUris(lstRemote.SelectedIndex).Location)
 
   ' Set the wallpaper location.
   Wallpaper.WallpaperURI = NewUri
End Sub

Of course, everything is done using CurrentSettings.RemoteUris in this case because you’re working with the remote settings. In addition, every one of the dialog boxes uses the most complex frmAddEdit constructor, which configures the application for a remote source. I’ll leave it to you to explore this code based on the conversation of the local source workings.

Well, that’s it for frmConfigure. The next post will complete the basic application. We’ll look at how frmAddEdit works. In the meantime, feel free to contact me about any questions at John@JohnMuellerBooks.com. I’ll also start entertaining some additions to this basic application. I have a few in mind, but I’d love to hear your thoughts on the subject. You can find the last post in this series at: Exploring the GrabAPicture Application (Part 12).