October 31, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Create a GUI for an FTP Client with VB.NET

  • October 29, 2004
  • By Paul Kimmel
  • Send Email »
  • More Articles »

Attach the FTP Client to the GUI

Next, you need to attach the FtpClient class library to your GUI. You can accomplish this easily from an architectural perspective by using the Observer pattern. From a practical point of view, the Observer pattern is manifested as events.

To attach the FtpClient to the GUI, declare and create an instance of the FtpClient and then define and attach event handlers to the FtpClient using the AddHandler statement. The code that demonstrates how to manually add event handlers is shown in the Form1_Load event handler in Listing 3 and in context in the Form's code listing at the end of this article.

Listing 3: The Form1_Load Method Demonstrates How to Manually Attach Event Handlers.

    Private ftp As FtpClient = Nothing
    Private FileControl As FileListUserControl

    Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

        Broadcaster.Add(Me)

        ftp = New FtpClient("localhost", ".", "anonymous", _
            "pkimmel@softconcepts.com", 21)

        AddHandler ftp.OnTcpEvent, AddressOf OnTcpEvent

        FileControl = New FileListUserControl
        FileControl.Caption = "Remote Files"
        FileControl.Parent = Panel1
        Panel1.Controls.Add(FileControl)
        FileControl.Dock = DockStyle.Fill
    End Sub

(Rather than remove the code for the FileListUserControl, I show the code for dynamically creating the UserControl in Listing 3 too.) The AddHandler statement requires an object and its event field, and the second parameter is the AddressOf the event handler. Implicitly, AddressOf creates an instance of a delegate object, which is a class that encapsulates event handlers.

Update a StatusStrip

Whidbey has replaced the StatusBar with the StatusStrip. Although it is a little more clumsy to use than the StatusBar, it is more OOPy but will feel a tad foreign. Basically, the StatusStrip is a status bar that has more advanced designers similar to the MenuStrip's designers. Instead of repeating that information here, let's talk about a proper way to update status information on your presentation layer.

Status information could literally come from anywhere. The FtpClient's state will change, and you might elect to report that status information. The FileListUserControl's or Form1's status might change, and you might elect to report that status information. Obviously, you could define a method in the Form that permits updating status, but then every status reporting object would need to have an instance of the Form. That would be a spaghetti code nightmare. If you asked yourself why, consider an example using just one reporting object, the Form. Suppose the FtpClient is told about the main form in the Windows client. This means the Windows client must have a reference to the FtpClient to invoke behaviors and the FtpClient must have a reference to the Windows Form to update status. In addition to being a circular reference, the FtpClient class also breaks if Form1 goes away, and you can't reuse the FtpClient in some other application without dragging the Windows Forms client along with it. This is called tightly coupled code and it is a bad thing.

A better alternative would be to define an OnStatusChange method in the FtpClient, attach the Windows Form to that event, and raise the event when status changes. While this represents looser coupling and better cohesion, it still means that other status-reporting objects would need to follow suit with events and event handlers.

The best solution can be had by again using the Observer pattern. However, in this context, think of the Observer pattern by its alter ego publish-subscribe or what I call broadcaster and listeners. (Think radio station and radio listeners.) Defining a broadcaster and listener means that you can broadcast status messages (from anywhere) and any listener can tune it. By combining the Singleton (to ensure one instance of the broadcaster) and the Observer patterns, you can send status messages from anywhere without any knowledge of who or what is listening. (If you need some validation for this solution, keep in mind that it is precisely how Microsoft implemented the System.Diagnostics.Trace class and TraceListeners to send Trace information to the Output window in VS.NET itself.)

Implement the Listener

Listening is comprised of two parts: an interface named IListener and a typed collection of IListeners. Using an interface means that any class that implements IListener can tune in to broadcasts. You might think inheritance would work here, but remember an obvious listener is a Form and it already inherits from the System.Windows.Forms.Form and multiple inheritance is not allowed in VB.NET.

Next, your broadcaster may want to multicast. Thus, you need a means of knowing about multiple listeners; for example, you might want to log status messages in the EventLog. Such a scenario is ideally suited for a strongly typed collection of IListeners. Listing 4 shows both the IListener and ListenerCollection. (For more information about typed collections, read my book Visual Basic .NET Power Coding from Addison-Wesley, and for more information about the Observer pattern, refer to Design Patterns by Erich Gamma, et. al., also from Addison-Wesley.)

Listing 4: The IListener and ListenerCollection Classes

Public Interface IListener

    ReadOnly Property Listening() As Boolean
    Sub Listen(ByVal message As String)

End Interface


Imports System
Imports System.Collections


Public Class ListenerCollection
    Inherits CollectionBase

    Default Public Property Item(ByVal index As Integer) As IListener
        Get
            Return CType(List(index), IListener)
        End Get
        Set(ByVal value As IListener)
            List(index) = value
        End Set
    End Property

    Public Function Add(ByVal value As IListener) As Integer
        Return List.Add(value)
    End Function

    Public Sub Remove(ByVal value As IListener)
        Dim I As Integer
        For I = 0 To List.Count - 1
            If (Item(I) Is value) Then
                List.RemoveAt(I)
                Exit For
            End If
        Next
    End Sub
End Class

Whidbey also has added a new project item template for interfaces (see Figure 4).



Click here for a larger image.

Figure 4: Whidbey Supports Several More Template Items, Generating More Code for You.

Implementing the Broadcaster

The Broadcaster class uses the Singleton pattern to ensure only one Broadcaster exists. You typically implement the Singleton pattern by making the constructor non-public and using a Shared method to request an instance of the class, which is created only once. Also, the Broadcaster contains Add and Remove methods that add and remove IListeners from an internal ListenerCollection held by the Broadcaster. Listing 5 provides the implementation of the Broadcaster class.

Listing 5: The Broadcaster Piece of Your Solution Plays the Role of Subject in the Observer Pattern.

Public Class Broadcaster
    Private listeners As ListenerCollection = Nothing
    Private Shared FInstance As Broadcaster = Nothing

    Protected Sub New()
        listeners = New ListenerCollection
    End Sub

    Public Shared Sub Add(ByVal value As IListener)
        Instance.listeners.Add(value)
    End Sub

    Public Shared Sub Remove(ByVal value As IListener)
        Instance.listeners.Remove(value)
    End Sub

    Public Shared Sub Broadcast(ByVal message As String)
        Dim listener As IListener
        For Each listener In Instance.listeners
            If (listener.Listening) Then
                listener.Listen(message)
            End If
        Next
    End Sub

    Protected Shared ReadOnly Property Instance() As Broadcaster
        Get
            If (FInstance Is Nothing) Then
                FInstance = New Broadcaster
            End If

            Return FInstance
        End Get
    End Property
End Class

All that remains is referring to the assembly that contains the broadcaster and listener and beginning broadcasting and listening. To Broadcast information, call Broadcaster.Broadcast. To Listen, reference the assembly containing the Broadcaster and Listener, realize the IListener interface, and register as a listener with the Broadcaster. The Form code in Listing 6 demonstrates the mechanics of the Broadcaster and IListener in practice.

Reference: Form Code Listing

Listing 6 contains the complete listing for the form for your Windows FTP GUI. Keep in mind that generated code is placed in a *.Designer.vb file in Whidbey.

Listing 6: The Form Code for the GUI

Imports Ftp

Public Class Form1
    Implements IListener

    Private ftp As FtpClient = Nothing
    Private FileControl As FileListUserControl

    Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

        Broadcaster.Add(Me)

        ftp = New FtpClient("localhost", ".", "anonymous", _
            "pkimmel@softconcepts.com", 21)

        AddHandler ftp.OnTcpEvent, AddressOf OnTcpEvent

        FileControl = New FileListUserControl
        FileControl.Caption = "Remote Files"
        FileControl.Parent = Panel1
        Panel1.Controls.Add(FileControl)
        FileControl.Dock = DockStyle.Fill
    End Sub

    Public Sub Listen(ByVal message As String) Implements _
        IListener.Listen
        UpdateStatus(message)
    End Sub

    Private Sub UpdateStatus(ByVal message As String)
        StatusStripPanel1.Text = message
    End Sub

    Public ReadOnly Property Listening() As Boolean _
        Implements IListener.Listening
        Get
            Return True
        End Get
    End Property

    Private Sub AboutToolStripMenuItem_Click(ByVal sender _
    As System.Object, ByVal e As System.EventArgs) _
    Handles AboutToolStripMenuItem.Click

        Dim About As String = _
            "WinFTP Client" + Environment.NewLine + _
            "Copyright (c) 2004. All Rights Reserved." + _
            Environment.NewLine + "pkimmel@softconcepts.com"

        MessageBox.Show(About, "About WinFTP", _
            MessageBoxButtons.OK, _
            MessageBoxIcon.Information)
    End Sub

    Private Sub GetRemoteFilesToolStripMenuItem_Click(ByVal sender _
        As System.Object, ByVal e As System.EventArgs) _
        Handles GetRemoteFilesToolStripMenuItem.Click

        FileControl.SetFiles(ftp.GetFileList("*.*"))
    End Sub

    Private Sub ConnectToolStripMenuItem_Click(ByVal sender _
    As System.Object, ByVal e As System.EventArgs) _
    Handles ConnectToolStripMenuItem.Click
        ftp.Connect()
        ftp.Login()
    End Sub

    Private Sub DisconnectToolStripMenuItem_Click(ByVal sender _
    As System.Object, ByVal e As System.EventArgs) _
    Handles DisconnectToolStripMenuItem.Click
        ftp.Disconnect()
    End Sub

    Private Sub ExitToolStripMenuItem_Click(ByVal sender _
    As System.Object, ByVal e As System.EventArgs) _
    Handles ExitToolStripMenuItem.Click
        Close()
    End Sub

    Private Sub OnTcpEvent(ByVal sender As Object, ByVal e _
        As TcpEventArgs)
        UpdateStatus(e.Data)
    End Sub

    Private Sub FileToolStripMenuItem_Click(ByVal sender _
    As System.Object, ByVal e As System.EventArgs) _
    Handles FileToolStripMenuItem.Click

    End Sub
End Class

The Elements for a Complete Solution

Writing software is hard. Writing good software is especially difficult, and great software is exceedingly rare. However, great software has some common elements and one of these is the use of well-known patterns in its design and implementation.

This article has demonstrated several kinds of skills you might need to write good software. Although a complete solution would not fit in an article, what you have here are the elements that you need to implement a whole solution. For example, you used the Observer and Singleton patterns, as well as delegates (a.k.a., events) to decouple the FtpClient from any particular GUI. You extended your FtpClient a bit more and got a chance to explore some of the new controls in Whidbey. (Keep in mind that Whidbey is beta software and is subject to change.)

Biography

Paul Kimmel, the VB Today columnist, is a software architect who has written several books on .NET programming. Look for more information on patterns and software design in his upcoming book UML DeMystified from McGraw-Hill/Osborne (Spring 2005). You may contact him at pkimmel@softconcepts.com if you need assistance developing software or are interested in joining the Lansing Area .NET Users Group (glugnet.org).

Copyright © 2004 by Paul Kimmel. All Rights Reserved.





Page 3 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel