Exploring the GrabAPicture Application (Part 12)

We’re finally down to the last baseline post for the GrabAPicture application, frmAddEdit, which is the chameleon form. The Exploring the GrabAPicture Application (Part 11) post describes how the frmConfigure code modifies the appearance of the code for this form so it can perform several tasks. At the center of this capability are several versions of the constructor that allow for different configuration techniques as shown in the following code.

Public Sub New()
   MyBase.New()
 
   'This call is required by the Windows Form Designer.
   InitializeComponent()
 
   'Add any initialization after the InitializeComponent() call
 
End Sub
 
Public Sub New(ByVal Title As String)
   MyBase.New()
 
   'This call is required by the Windows Form Designer.
   InitializeComponent()
 
   'Change the title.
   Me.Text = Title
End Sub
 
Public Sub New(ByVal Title As String, _
               ByVal AddButtonTitle As String)
   MyBase.New()
 
   'This call is required by the Windows Form Designer.
   InitializeComponent()
 
   'Change the title.
   Me.Text = Title
 
   ' Change the add/edit button title.
   btnAddEdit.Text = AddButtonTitle
End Sub
 
Public Sub New(ByVal Title As String, _
               ByVal AddButtonTitle As String, _
               ByVal RemoteEdit As Boolean)
   MyBase.New()
 
   ' This call is required by the Windows Form Designer.
   InitializeComponent()
 
   ' Change the title.
   Me.Text = Title
 
   ' Change the add/edit button title.
   btnAddEdit.Text = AddButtonTitle
 
   ' Remove the directory button and resize the
   ' location field as needed.
   If RemoteEdit Then
      btnGetResource.Visible = False
      btnGetResource.Enabled = False
      txtLocation.Size = txtName.Size
      _IsRemote = True
   End If
End Sub

As you can see, each of the constructors provides one more level of configuration. You might wonder why the application doesn’t simply use the most configurable of the four constructors, but this approach has some disadvantages.

 

  • Using just a single, complex constructor makes the application inflexible.
  • The developer would also need to provide the default values for title, button, and remote editing capability, even if these values didn’t change from the default dialog box.
  • Adding this requirement would make the application more susceptible to errors.
  • Using four different constructors also forces the developer to consider which entries actually do need to change. Giving someone the opportunity to think application design through is never a bad idea.

Notice that the fourth constructor contains special code for removing the browse button from view when working with a remote resource. The browse button wouldn’t do the user much good in this case and clicking it can only cause problems.

Using the custom constructors is really important in this case and makes the application much stronger. You can divide the rest of the code for this form into three areas:

 


  • Property creation and maintenance

  • Form loading

  • User controls

Most of the code for this form is straightforward, albeit a bit convoluted at time due to the form’s multipurpose nature. The following sections describe each of the coding areas.

Property Creation and Maintenance

This form is used in a number of ways, so it’s important that the application be able to track the form’s current state. Otherwise, the application could make an assumption about the form content that isn’t correct. The best way to provide state information outside of the form is to use properties. Relying on properties makes it possible to monitor and control interaction with form state information in a controlled way. The application uses the properties shown here.

' Private variables used for editing purposes.
Private _Resources As LocalUri()
Private _RemoteSources As RemoteUri()
Private _IsRemote As Boolean = False
Private _IsEdit As Boolean = False
Private _RecordNumber As Int32 = 0
 
' Tracks the current list of local resources.
Public Property Resources() As LocalUri()
   Get
      Return _Resources
   End Get
   Set(ByVal Value As LocalUri())
      _Resources = Value
   End Set
End Property
 
' Tracks the current list of remote resources
Public Property RemoteSources() As RemoteUri()
   Get
      Return _RemoteSources
   End Get
   Set(ByVal Value As RemoteUri())
      _RemoteSources = Value
   End Set
End Property
 
' Shows whether edit is for a local or remote resource.
Public ReadOnly Property IsRemote() As Boolean
   Get
      Return _IsRemote
   End Get
End Property
 
' Determines whether this is an add or edit action.
Public Property IsEdit() As Boolean
   Get
      Return _IsEdit
   End Get
   Set(ByVal Value As Boolean)
      _IsEdit = Value
   End Set
End Property
 
' Defines the edited record.
Public Property RecordNumber() As Int32
   Get
      Return _RecordNumber
   End Get
   Set(ByVal Value As Int32)
      ' Verify the input is positive.
      If Value >= 0 Then
 
         ' Perform separate checks for local and remote.
         If _IsRemote Then
 
            ' Verify the input is less than the number of
            ' records when using a remote resource. This
            ' check will also throw a null reference
            ' exception if the record source isn't
            ' properly initialized.
            If Value >= _RemoteSources.Length Then
 
               Throw New ArgumentOutOfRangeException( _
                  "Record Number", _
                  "Record number is too high.")
            End If
         Else
 
            ' Verify the input is less than the number of
            ' records when using a local resource. This
            ' check will also throw a null reference
            ' exception if the record source isn't
            ' properly initialized.
            If Value >= _Resources.Length Then
 
               Throw New ArgumentOutOfRangeException( _
                  "Record Number", _
                  "Local record number is too high.")
            End If
         End If
 
         ' Set the record number.
         _RecordNumber = Value
      Else
         Throw New ArgumentOutOfRangeException( _
            "Record Number", _
            "Provide a positive record number.")
      End If
   End Set
End Property

The properties are pretty straightforward. They serve the following purposes:

 


  • Resources: Defines a local URI.

  • RemoteSources: Defines a remote URI.

  • IsRemote: A read-only property that the application can use to determine whether to access the Resources or RemoteSources property for the current URI.
  • IsEdit: Controls whether the dialog box provides an adding or editing appearance. The adding dialog box is blank, while the editing dialog box contains the current local or remote record information.
  • RecordNumber: Contains the record number of the current local or remote source.

Form Loading

Form loading is important because the code in this area controls the initial appearance of the dialog box. A form used for adding a new record, whether local or remote, is always blank, so it doesn’t require any custom configuration. However, if the form is used to edit a current record, the form loading code must detect whether the form is editing a local or remote resource. In addition to URI differences, local resources have an additional button available for browsing the local hard drive. The following code shows how form loading works.

Private Sub frmAddEdit_Load(ByVal sender As Object, _
                            ByVal e As System.EventArgs) _
                         Handles MyBase.Load
   ' Set the form up for editing.
   If _IsEdit Then
      ' Make sure you use the correct database.
      If _IsRemote Then
         txtName.Text = _RemoteSources(_RecordNumber).Name
         txtLocation.Text = _RemoteSources(_RecordNumber).Location
         Select Case _RemoteSources(_RecordNumber).Style
            Case Styles.Stretched
               rbStretched.Checked = True
               rbCentered.Checked = False
               rbTiled.Checked = False
            Case Styles.Centered
               rbStretched.Checked = False
               rbCentered.Checked = True
               rbTiled.Checked = False
            Case Styles.Tiled
               rbStretched.Checked = False
               rbCentered.Checked = False
               rbTiled.Checked = True
         End Select
      Else
         txtName.Text = _Resources(_RecordNumber).Name
         txtLocation.Text = _Resources(_RecordNumber).Location
         Select Case _Resources(_RecordNumber).Style
            Case Styles.Stretched
               rbStretched.Checked = True
               rbCentered.Checked = False
               rbTiled.Checked = False
            Case Styles.Centered
               rbStretched.Checked = False
               rbCentered.Checked = True
               rbTiled.Checked = False
            Case Styles.Tiled
               rbStretched.Checked = False
               rbCentered.Checked = False
               rbTiled.Checked = True
         End Select
      End If
   End If
End Sub

As you can see, the first thing the code detect is whether the form is using for adding. If it is, then the application doesn’t do any configuration.

When the dialog box is used for editing, the application checks the status of _IsRemote to determine how to configure the dialog box. The code obtains the correct record and then displays the information on screen.

User Controls

There are three user controls, but you only need to provide code for two of them (btnCancel provides the default action when clicked). The Add/Edit button (btnAddEdit) is always visible. The relies on it to make any changes permanent. The browse button (btnGetResource) is only available when working with a local resource. The user relies on this button to locate resources on the local hard drive. The following code shows how these buttons work.

Private Sub btnAddEdit_Click(ByVal sender As System.Object, _
                             ByVal e As System.EventArgs) _
                          Handles btnAddEdit.Click
   If IsEdit Then
      If _IsRemote Then
         ' Change the array data.
         _RemoteSources(_RecordNumber).Name = txtName.Text
         _RemoteSources(_RecordNumber).Location = txtLocation.Text
         If rbStretched.Checked Then
            _RemoteSources(_RecordNumber).Style = Styles.Stretched
         End If
         If rbCentered.Checked Then
            _RemoteSources(_RecordNumber).Style = Styles.Centered
         End If
         If rbTiled.Checked Then
            _RemoteSources(_RecordNumber).Style = Styles.Tiled
         End If
      Else
         ' Change the array data.
         _Resources(_RecordNumber).Name = txtName.Text
         _Resources(_RecordNumber).Location = txtLocation.Text
         If rbStretched.Checked Then
            _Resources(_RecordNumber).Style = Styles.Stretched
         End If
         If rbCentered.Checked Then
            _Resources(_RecordNumber).Style = Styles.Centered
         End If
         If rbTiled.Checked Then
            _Resources(_RecordNumber).Style = Styles.Tiled
         End If
      End If
   Else
      If _IsRemote Then
         ' Add a new array element.
         If _RemoteSources Is Nothing Then
            ReDim _RemoteSources(0)
         Else
            ReDim Preserve _RemoteSources(_RemoteSources.Length)
         End If
 
         ' Instantiate a new array item.
         _RemoteSources(_RemoteSources.Length - 1) = New RemoteUri
 
         ' Add the data to the array.
         _RemoteSources(_RemoteSources.Length - 1).Name = txtName.Text
         _RemoteSources(_RemoteSources.Length - 1).Location = txtLocation.Text
         If rbStretched.Checked Then
            _RemoteSources(_RemoteSources.Length - 1).Style = Styles.Stretched
         End If
         If rbCentered.Checked Then
            _RemoteSources(_RemoteSources.Length - 1).Style = Styles.Centered
         End If
         If rbTiled.Checked Then
            _RemoteSources(_RemoteSources.Length - 1).Style = Styles.Tiled
         End If
      Else
         ' Add a new array element.
         If _Resources Is Nothing Then
            ReDim _Resources(0)
         Else
            ReDim Preserve _Resources(_Resources.Length)
         End If
 
         ' Instantiate a new array item.
         _Resources(_Resources.Length - 1) = New LocalUri
 
         ' Add the data to the array.
         _Resources(_Resources.Length - 1).Name = txtName.Text
         _Resources(_Resources.Length - 1).Location = txtLocation.Text
         If rbStretched.Checked Then
            _Resources(_Resources.Length - 1).Style = Styles.Stretched
         End If
         If rbCentered.Checked Then
            _Resources(_Resources.Length - 1).Style = Styles.Centered
         End If
         If rbTiled.Checked Then
            _Resources(_Resources.Length - 1).Style = Styles.Tiled
         End If
      End If
   End If
End Sub
 
Private Sub btnGetResource_Click(ByVal sender As System.Object, _
                                 ByVal e As System.EventArgs) _
                              Handles btnGetResource.Click
   If OFD.ShowDialog() = DialogResult.OK Then
      txtLocation.Text = OFD.FileName
   End If
End Sub

Let’s discuss the simple button first. When the user clicks the browse button, the application displays an OpenFileDialog control, OFD. If the user selects a file and clicks OK, the code places the name of the file into txtLocation.Text. Theoretically, a little customization would make this dialog box friendlier, but it works well as presented.

The btnAddEdit_Click() event handler is a little more complex. It actually performs one of four actions depending on the dialog box options.

 

  • Edit/Remote: The code changes the record information found in _RemoteSources for the current record pointed at by _RecordNumber.
  • Edit/Local: The code changes the record information found in _Resources for the current record pointed at by _RecordNumber.
  • Add/Remote: The code either creates a new _RemoteSources array or adds an element to the existing _RemoteSources array. In both cases, the code adds the new record to the end of the array. Note that because the array is zero-based, you must subtract 1 from the array length in order to find the last record of the new array.
  • Add/Local: The
    code either creates a new _Resources array or adds an element to
    the existing _Resources array. In both cases, the code adds the new
    record to the end of the array.

Well, now you have the full story on the base application—the application I started with. The next post will discuss some of the additions we’ll pursue in the new year. For now, please keep those comments coming. Let me know if you have any questions at all about this form at John@JohnMuellerBooks.com.

 

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.