Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / encryption

InfoIP a tool to find your WAN IP

5.00/5 (3 votes)
8 Sep 2014CPOL5 min read 18.7K   525  
A SW to send your WAN IP and other info via email
Image 1  Download InfoIP.zip (Source code, Setup Project, exe and setup.msi)

Introduction

Nowadays some ISPs provide new customers with their own routers which seem to have locked configuration settings. This tiny software is designed to overcome this difficulty sending via email essential information such as LAN IP, WAN IP and Default Gateway IP necessary for the remote desktop connection to your PC.

Background

I have worked on this project because a colleague of mine asked me if there was a software that would allow to know the WAN IP, because the software used, after the upgrade the router's firmware (router personal and not provided by your ISP), has stopped to work because there is no possible to implement an opportune configuration.

That’s why I have started work on this tool whose aim is find and send via email the WAN IP.

Image 2

Image 3

 

Using the code & Point of Interest

In this article I upload the source code (the solution also includes the project for the setup, for VS 2010) that exe.

The most interesting classes are: 

  1. InfoViewModel.vb
  2. Config.vb
  3. Crypt.vb
  4. Email.vb
  5. Network.vb
  6. Application.xaml.vb
  7. MainWindow.xaml.vb

I hope to soon translate them with the entire project in C#.

InfoViewModel.vb

In this class has been implemented logic MVVM, based on the class InfoData that contains the informations shown in UI. The peculiarity of InfoViewModel is that implements IDataErrorInfo and INotifyPropertyChanged, so this allows you respectively to validate the individual information and to update and change the information in the UI.

VB.NET
#Region "IDataErrorInfo"
     Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            Return "Errors in the validation of the informations inserted"
        End Get
    End Property

    Default Public ReadOnly Property Item([property] As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            Select Case [property]
                Case "EmailFrom"
                    Return Me.ValidateEmailFrom()
                Case "Server"
                    Return Me.ValidateServer()
                Case "Port"
                    Return Me.ValidatePort()
                Case "EmailTo"
                    Return Me.ValidateEmailTo()
                Case "Timeout"
                    Return Me.ValidateTimeout()
            End Select
            Return Nothing
        End Get
    End Property

#End Region
VB.NET
#Region "INotifyPropertyChanged"
     Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    Protected Overridable Sub OnPropertyChanged(ByVal propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

#End Region

 

Here is shown how to validate the email address through the appropriate regular expression:

VB.NET
    Private Function ValidateEmailFrom() As String
        If Me.mIsChangedEmailFrom Then
            If String.IsNullOrEmpty(Me.EmailFrom.ToString()) Then
                Return "email in SMTP is missing"
            End If
            If Not ((New Regex("^(\s*,?\s*[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})+\s*$")).IsMatch(Me.EmailFrom.ToString())) Then
                Return "email in SMTP is wrong"
            End If
        End If
        Return Nothing
    End Function

 

Here instead is shown how to automatically update the UI through the event PropertyChanged on two properties eg:

VB.NET
Public Property DefaultGatewayIP As IPAddress
    Get
        Return mInfoData.DefaultGatewayIP
    End Get
    Set(value As IPAddress)
        If mInfoData.DefaultGatewayIP IsNot Nothing Then
            If Not mInfoData.DefaultGatewayIP.Equals(value) Then
                mInfoData.DefaultGatewayIP = value
                Me.OnPropertyChanged("DefaultGatewayIP")
            End If
        Else
            mInfoData.DefaultGatewayIP = value
            Me.OnPropertyChanged("DefaultGatewayIP")
        End If
    End Set
End Property
VB.NET
Public Property EmailFrom As String
    Get
        Return mInfoData.EmailFrom
    End Get
    Set(value As String)
        If String.Compare(mInfoData.EmailFrom, value) <> 0 Then
            mIsChangedEmailFrom = True
            mInfoData.EmailFrom = value
            Me.OnPropertyChanged("EmailFrom")
        End If
    End Set
End Property

 

 

Config.vb

This class used together the Crypt class, from the MainWindow class, is responsible to get/set the informations from/to app.config and exposes:

  • a Shared function named GetValueAppSetting
VB.NET
''' <summary>
''' Funzione statica che restituisce il valore di una chiave del file di configurazione
''' </summary>
''' <param name="keyAppSetting">La chiave</param>
''' <returns>Il valore della chiave</returns>
''' <remarks></remarks>
Public Shared Function GetValueAppSetting(keyAppSetting As String) As String
    Try
        Return ConfigurationManager.AppSettings(keyAppSetting)
    Catch generatedExceptionName As Exception
        Return ""
    End Try
End Function
  • a Shared sub named SetValueAppSetting
VB.NET
''' <summary>
''' Metodo statico che consente di valorizzare una chiave nel file di configurazione.
''' Se la chiave non è presente viene creata e valorizzata
''' </summary>
''' <param name="keyAppSetting">La chiave</param>
''' <param name="valore">Il valore</param>
''' <remarks></remarks>
Public Shared Sub SetValueAppSetting(keyAppSetting As String, valore As String)
    Dim myConfiguration As Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
    If myConfiguration.AppSettings.Settings(keyAppSetting) IsNot Nothing Then
        myConfiguration.AppSettings.Settings(keyAppSetting).Value = valore
    Else
        myConfiguration.AppSettings.Settings.Add(keyAppSetting, valore)
    End If
    myConfiguration.Save(ConfigurationSaveMode.Modified)
End Sub

 

Crypt.vb

This class is responsible to encode and decode the password that will be saved in the app.config, after have been encrypted through use of the algorithm <a href="http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndaelmanaged%28v=vs.100%29.aspx">RijndaelManaged</a> (made available by Microsoft).
The peculiarity of encryption is to use the MAC address - found through the <a href="#Network">Network</a> class - together at the string set in VariabiliGlobali.BaseStringForRijndael, to create the secret key and the initialization vector, both of 256 bit, so to ensure as far as possible the information security.

VB.NET
Public Sub New()
    Dim network As New Network
    mMacAddress = network.MacAddress
    If String.IsNullOrEmpty(mMacAddress) Then
        mMacAddress = DefaultMacAddress
    End If
    mKey = StrReverse(mKey) + StrReverse(mMacAddress)
    mIV = mIV + mMacAddress
    mRijndael.KeySize = 256
    mRijndael.BlockSize = 256
    mRijndael.Key = ASCIIEncoding.ASCII.GetBytes(mKey)
    mRijndael.IV = ASCIIEncoding.ASCII.GetBytes(mIV)
End Sub
VB.NET
''' <summary>
''' Funzione che effettua la crittografia di una stringa in chiaro
''' </summary>
''' <param name="input">La stringa da crittografare</param>
''' <returns>
''' Restituisce una stringa a partire da input rappresentate una matrice di Unsigned Integer a 8 bit nella rappresentazione equivalente codificata con cifre base 64.
''' </returns>
''' <remarks></remarks>
Public Function Encode(input As String) As String
    Dim toEncrypt() As Byte = Encoding.UTF8.GetBytes(input)
    Dim output() As Byte = mRijndael.CreateEncryptor().TransformFinalBlock(toEncrypt, 0, toEncrypt.Length)
    Return Convert.ToBase64String(output)
End Function
VB.NET
''' <summary>
''' Funzione che effettua la decrittografia di una stringa ottenuta da Encode
''' </summary>
''' <param name="input">Una stringa rappresentate una matrice di Unsigned Integer a 8 bit nella rappresentazione equivalente codificata con cifre base 64</param>
''' <returns>
''' Restituisce una stringa a partire da input per la quale avviene una decodifica di tutti i byte della matrice di byte
''' </returns>
''' <remarks>Crypt.Encode dovrà essere obbligatoriuamente eseguito prima</remarks>
Public Function Decode(input As String) As String
    Dim toDecrypt As [Byte]() = Convert.FromBase64String(input)
    Dim output As [Byte]() = mRijndael.CreateDecryptor().TransformFinalBlock(toDecrypt, 0, toDecrypt.Length)
    Return Encoding.UTF8.GetString(output)
End Function

 

Email.vb

This class is obviously responsible for sending you a mail with the info shown in UI. The peculiarity is the function Invia that on the basis of the caller (mail on demand or automatic mail) that will pass particular values ​​for the last two parameters.
But other than that the main thing is the instance creation of the SmtpClient class with the set of .DeliveryMethod = SmtpDeliveryMethod.Network and .UseDefaultCredential = False  necessary to allow the correct authentication

VB.NET
    ''' <summary>
    ''' Funzione che invia la Email
    ''' </summary>
    ''' <param name="oggetto">L'oggetto della Email</param>
    ''' <param name="testo">Il testo della Email</param>
    ''' <param name="mailMittente">Il mittente delle Email</param>
    ''' <param name="mailDestinatari">Il destinatario della Email</param>
    ''' <param name="smtp">Il server SMTP</param>
    ''' <param name="isSsl">Usare o meno una connessione SSL</param>
    ''' <param name="portSmtp">Porta del server SMTP</param>
    ''' <param name="password">Password per l'autenticazione</param>
    ''' <param name="showException">
    ''' Indica se segnalare o meno l'eccezione
    ''' Utile nell'invio automatico sse si verifica al primo invio in modo da fermare il BackgroundWorker che altrimenti andrebbe sempre in errore
    ''' </param>
    ''' <param name="forceSend">
    ''' Indica se continuare ad effettuare dei tentativi fino a quando la email non viene spedita.
    ''' Utile nell'invio automatica sse si verifica il cambio del WAN IP
    ''' </param>
    ''' <returns>
    ''' T, se tutto ok
    ''' F, altrimenti
    ''' </returns>
    ''' <remarks></remarks>
    Public Function Invia(oggetto As String, testo As String, mailMittente As String, mailDestinatari As Dictionary(Of String, String),
                          smtp As String, isSsl As Boolean, portSmtp As String, password As String,
                          Optional showException As Boolean = True, Optional forceSend As Boolean = False) As Boolean
        Dim result As Boolean = False
        Dim mSmtpClient As SmtpClient = Nothing
        Try
invia:
            If isSsl Then
                mSmtpClient = New SmtpClient(smtp, Convert.ToInt32(portSmtp).ToString()) With { _
                    .EnableSsl = isSsl, _
                    .DeliveryMethod = SmtpDeliveryMethod.Network, _
                    .UseDefaultCredentials = False _
                }
                mSmtpClient.Credentials = New NetworkCredential(mailMittente, password)
            Else
                mSmtpClient = New SmtpClient(smtp)
                mSmtpClient.DeliveryMethod = SmtpDeliveryMethod.Network
            End If

            Me.Messaggio.Subject = oggetto + " " + DateTime.Now.ToString()

            Dim mittente As New MailAddress(mailMittente)
            Me.Messaggio.From = mittente

            Dim destinatario As New MailAddress(mailDestinatari("To"))

            Dim mCc As String = String.Empty
            Dim mBcc As String = String.Empty
            If mailDestinatari.ContainsKey("Cc") Then
                mCc = mailDestinatari("Cc")
            End If
            If mailDestinatari.ContainsKey("Bcc") Then
                mBcc = mailDestinatari("Bcc")
            End If
            Me.Messaggio.[To].Add(destinatario)
            If mCc <> "" Then
                Me.Messaggio.CC.Add(mCc)
            End If
            If mBcc <> "" Then
                Me.Messaggio.Bcc.Add(mBcc)
            End If

            Me.Messaggio.IsBodyHtml = True
            Me.Messaggio.BodyEncoding = UTF8Encoding.UTF8
            Me.Messaggio.Body = testo.ToString()

            mSmtpClient.Send(Me.Messaggio)
            result = True
        Catch ex As Exception
            If showException Then
segnalaUgualmenteErrore:
                Dim messaggioSpecifico As String = String.Empty
                If Not showException Then
                    messaggioSpecifico = vbNewLine + vbNewLine + "NOTE:" + vbNewLine +
                        "Ten attempts have already been made WITHOUT SUCCESS, so the automatic procedure WILL BE INTERRUPTED!!"
                End If
                MessageBox.Show(ex.Message + messaggioSpecifico,
                                "Email",
                                MessageBoxButton.OK, MessageBoxImage.Exclamation)
                Return result
            End If
            If Not forceSend Then
                GoTo segnalaUgualmenteErrore
            Else
                Thread.Sleep(1000)
                mSmtpClient.Dispose()
                GoTo invia
            End If
        End Try
        Return result
    End Function

 

Network.vb

This class is responsible to find the follow information:

  • MAC address that, in the case where Ethernet and Wireless are present, returns the value for the Wireless interface like is visible here
VB.NET
''' <summary>
''' Funzione che restituisce il MAC address dell'interfaccia
''' </summary>
''' <returns>
''' Restituisce il MAC address Ethernet o Wireless, nel caso in cui siano entrambe presenti restituisce il valore relativo all'interfaccia Wireless
''' </returns>
''' <remarks></remarks>
Private Function GetMacAddress() As String
    Dim result As String = String.Empty
    Dim theNetworkInterfaces() As System.Net.NetworkInformation.NetworkInterface = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
    For Each currentInterface As System.Net.NetworkInformation.NetworkInterface In theNetworkInterfaces
        If currentInterface.NetworkInterfaceType = Net.NetworkInformation.NetworkInterfaceType.Ethernet Then
            result = currentInterface.GetPhysicalAddress().ToString()
        End If
        If currentInterface.NetworkInterfaceType = Net.NetworkInformation.NetworkInterfaceType.Wireless80211 Then
            result = currentInterface.GetPhysicalAddress().ToString()
        End If
    Next
    Return result
End Function
  • LAN IP
VB.NET
''' <summary>
''' Funzione che restituisce l'IP della LAN
''' </summary>
''' <returns>LAN IP</returns>
''' <remarks></remarks>
Private Function GetLanIP() As String
    Dim result As String = String.Empty
    Try
        result = (From ip In Dns.GetHostEntry(Dns.GetHostName()).AddressList Where ip.AddressFamily = AddressFamily.InterNetwork Select ip)(0).ToString()
    Catch ex As Exception
        MessageBox.Show(ex.Message + vbNewLine + vbNewLine + "Program execution will continue!", "GetLanIP", MessageBoxButton.OK, MessageBoxImage.Exclamation)
    End Try
    Return result
End Function
  • Default Gateway IP
VB.NET
''' <summary>
''' Funzione che restituisce l'IP del default Gateway
''' </summary>
''' <returns>Router IP</returns>
''' <remarks></remarks>
Private Function GetDefaultGatewayIP() As IPAddress
    Dim result As Object = Nothing
    Try
        Dim card = NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault()
        If card IsNot Nothing Then
            Dim address = card.GetIPProperties().GatewayAddresses.FirstOrDefault()
            If address IsNot Nothing Then
                result = address.Address
            End If
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message + vbNewLine + vbNewLine + "Program execution will continue!", "GetDefaultGateway", MessageBoxButton.OK, MessageBoxImage.Exclamation)
    End Try
    Return result
End Function
  • WAN IP, to retrieve this information has been necessary use the service available on the site http://checkip.dyndns.org/ using the class WebClient:
VB.NET
''' <summary>
''' Funzione che restituisce l'IP della WAN
''' </summary>
''' <returns>WAN IP</returns>
''' <remarks>
''' Fa utilizzo del servizio web http://checkip.dyndns.org/
''' </remarks>
Private Function GetWanIP() As String
    Dim result As String = String.Empty
    Try
        result = (New Regex("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")) _
                 .Matches((New WebClient()).DownloadString(CheckIpDynDns))(0).ToString()
        If String.IsNullOrEmpty(result) Then
            Throw New Exception(CheckIpDynDns + " result not available. Program execution will continue!")
        End If
    Catch ex As Exception
        MessageBox.Show(ex.Message + vbNewLine + vbNewLine + "Program execution will continue!", "GetExternalIp", MessageBoxButton.OK, MessageBoxImage.Exclamation)
    End Try
    Return result
End Function

 

Application.xaml.vb

This is the class that manages the application in the widest sense of the term. In fact, here you can specify:

  • what is the window that you will see at startup (see StartupUri) - its <a id="Application.xaml" name="Application.xaml">Application.xaml</a>
XML
<Application x:Class="Application"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:me="clr-namespace:InfoIP"
             xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
             StartupUri="MainWindow.xaml"
             DispatcherUnhandledException="Application_DispatcherUnhandledException"
             ShutdownMode="OnLastWindowClose">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!--<ResourceDictionary Source="/Xceed.Wpf.AvalonDock.Themes.Metro;component/Theme.xaml"/>-->
            </ResourceDictionary.MergedDictionaries>
            <!-- Converter -->
            <me:BooleanToVisibilityConverter x:Key="CollapsedIfFalse" TriggerValue="False" IsHidden="False"/>
            <me:BooleanToEnableConverter x:Key="EnabledIfFalse"/>
            <me:BooleansToBooleanConverter x:Key="MultiBooleanToBoolean"/>
            <me:ValidatorConverter x:Key="MultiValidatorToBoolean"/>
            <me:BooleanToVisibilityConverter x:Key="CollapsedIfTrue" TriggerValue="True" IsHidden="False"/>
            <!-- Style -->
...
            <SolidColorBrush x:Key="BackgroundReadonly">#FFEBEBEB</SolidColorBrush>
        </ResourceDictionary>
    </Application.Resources>
</Application>
  • some events like, Application_Startup (eg to verify that there are not multiple instances of application running) and Application_DispatcherUnhandledException (to catch the unhandled exception)
VB.NET
Private Sub Application_Startup(ByVal sender As Object, ByVal e As System.Windows.StartupEventArgs) Handles Me.Startup
    ...

    Dim mutexName = Me.Info.AssemblyName
    If Not MutexExists(mutexName) Then
        mInstanceMutex = New Mutex(True, mutexName)
    Else
        MessageBox.Show(Me.Info.AssemblyName.ToString() + " is already running",
                        Me.Info.AssemblyName.ToString(),
                        MessageBoxButton.OK, MessageBoxImage.Warning)
        Me.Shutdown()
        Exit Sub
    End If

    ' CurrentUICulture: considera i regional setting del SO
    ' CurrentCulture: considera i regional setting dell'utente
    'Dim cultureName = Thread.CurrentThread.CurrentCulture.Name
    'Dim xmlLang = XmlLanguage.GetLanguage(cultureName)
    'FrameworkElement.LanguageProperty.OverrideMetadata(GetType(FrameworkElement), New FrameworkPropertyMetadata(xmlLang))
    'vedi http://stackoverflow.com/questions/4041197/how-to-set-and-change-the-culture-in-wpf applica le impostazioni scelte all'exe e a tutte le dll
    FrameworkElement.LanguageProperty.OverrideMetadata(GetType(FrameworkElement), New FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)))
End Sub
VB.NET
Private Sub Application_DispatcherUnhandledException(sender As System.Object, e As System.Windows.Threading.DispatcherUnhandledExceptionEventArgs)
    Try
        Application.Current.MainWindow.Cursor = Cursors.Arrow
    Catch ex As Exception
    End Try

    Dim msg As String = "ATTENTION!" + vbNewLine + "There was an error!" + vbNewLine + "The application will terminate" + vbNewLine + vbNewLine + "Error Message: " + e.Exception.Message
    If e.Exception.InnerException IsNot Nothing Then
        msg += vbNewLine + e.Exception.InnerException.Message
    End If

    MessageBox.Show(msg, System.Reflection.Assembly.GetEntryAssembly.GetName.Name, MessageBoxButton.OK, MessageBoxImage.Error)

    Process.GetCurrentProcess.Kill()
    e.Handled = True
End Sub
VB.NET
#Region "ValidatorConverter"
 ''' <summary>
''' From Validators to Boolean
''' </summary>
''' <remarks></remarks>
Public Class ValidatorConverter
    Implements IMultiValueConverter

    Public Function Convert(values() As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
        Dim isValid As Boolean
        For Each v In values
            If v.Equals(DependencyProperty.UnsetValue) Then
                isValid = True
            Else
                isValid = False
                Exit For
            End If
        Next
        Return isValid
    End Function

    Public Function ConvertBack(value As Object, targetTypes() As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function

End Class

#End Region

 

MainWindow.xaml.vb

This is the class for the main window, it is characterized by the utilization of BackgroundWorker, to enable the use of the class BusyIndicator of the Extended WPF Toolkit while occurs the email despatch.

VB.NET
''' <summary>
''' Metodo che provvede all'invio automatico delle informazioni allo scadere del timeout impostato da interfaccia
''' </summary>
''' <param name="verificaSeInviare">Indica se verificare il cambio delle informazioni prima di effettuare l'invio delle stesse</param>
''' <remarks></remarks>
Private Sub SendInfoAuto(Optional ByVal verificaSeInviare As Boolean = False)
    If Not BusyIndicator_InvioAuto.IsBusy Then
        Me.BusyProgressValue = 0.0
        BusyIndicator_InvioAuto.BusyContent = "Seconds to check: "
        BusyIndicator_InvioAuto.IsBusy = True
        mBackgroundWorker = New BackgroundWorker()
        mBackgroundWorker.WorkerSupportsCancellation = True
        AddHandler mBackgroundWorker.DoWork, AddressOf Me.DoWorkAuto
        AddHandler mBackgroundWorker.RunWorkerCompleted, AddressOf Me.OnWorkCompletedAuto
        mBackgroundWorker.RunWorkerAsync(New Tuple(Of Dispatcher, Dispatcher, Integer, Boolean) _
                                         (Me.Dispatcher,
                                          BusyIndicator_InvioAuto.Dispatcher,
                                          Convert.ToInt32(IIf(String.IsNullOrEmpty(Me.TextBox_Timeout.Text.ToString()), 60, Me.TextBox_Timeout.Text)),
                                          verificaSeInviare))
    Else
        If mBackgroundWorker IsNot Nothing AndAlso mBackgroundWorker.IsBusy Then
            mBackgroundWorker.CancelAsync()
        End If
        BusyIndicator_InvioAuto.IsBusy = False
    End If
End Sub

As shown above for the automatic email despatch feature has been used a specifc busy indicatator BusyIndicator_InvioAuto, this to allow the update of value while expires the timeout (see second figure) - note that the value of timeout desider is passed like parameter in mBackgroundWorker.RunWorkerAsync.
The management of the timeout uses two properties of this class, then exposed in XAML through the tag <Window.Resources>:

  • the properties
VB.NET
#Region "Proprietà"
 #Region "Pubbliche"
     Public Property BusyProgressValue() As Double
        Get
            Return CType(GetValue(BusyProgressValueProperty), Double)
        End Get
        Set(ByVal value As Double)
            SetValue(BusyProgressValueProperty, value)
        End Set
    End Property

    Public Property BusyProgressMaximum() As Double
        Get
            Return CType(GetValue(BusyProgressMaximumProperty), Double)
        End Get
        Set(ByVal value As Double)
            SetValue(BusyProgressMaximumProperty, value)
        End Set
    End Property

#Region "Condivise"
     Public Shared ReadOnly BusyProgressValueProperty As DependencyProperty = _
        DependencyProperty.Register("BusyProgressValue", GetType(Double), GetType(MainWindow), New PropertyMetadata(0.0))

    Public Shared ReadOnly BusyProgressMaximumProperty As DependencyProperty = _
        DependencyProperty.Register("BusyProgressMaximum", GetType(Double), GetType(MainWindow), New PropertyMetadata(0.0))

#End Region

#End Region

#Region "Private"
 
#End Region

#End Region
  • the XAML <Window.Resources>
XML
<Window.Resources>
    <Style x:Key="busyProgressBarStyle" TargetType="ProgressBar">
        <Setter Property="IsIndeterminate" Value="False"/>
        <Setter Property="Minimum" Value="0"/>
        <Setter Property="Maximum" Value="{Binding ElementName=mainWindow, Path=BusyProgressMaximum}"/>
        <Setter Property="Height" Value="16"/>
        <Setter Property="Value" Value="{Binding ElementName=mainWindow, Path=BusyProgressValue}"/>
        <Setter Property="Background" Value="{StaticResource BackgroundReadonly}"/>
    </Style>
</Window.Resources>
  • the XAML BusyIndicator_InvioAuto
XML
<xctk:BusyIndicator x:Name="BusyIndicator_InvioAuto" IsBusy="False" ProgressBarStyle="{StaticResource busyProgressBarStyle}"
                    Width="154" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Margin="5"
                    Background="{StaticResource BackgroundReadonly}"/>

Furthermore as shown above in first code snippet, the mBackgroundWorker variable does its work by setting two handlers for the events DoWorker and RunWorkerCompleted:

VB.NET
''' <summary>
''' Metodo che esegue SendInfo per inviare la email con le informazioni, dopo di che attende il timeout impostato prima di terminare il job
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub DoWorkAuto(sender As Object, e As DoWorkEventArgs)
    Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
    Dim arg As Tuple(Of Dispatcher, Dispatcher, Integer, Boolean) = CType(e.Argument, Tuple(Of Dispatcher, Dispatcher, Integer, Boolean))
    Dim mainWindowDispatcher As Dispatcher = arg.Item1
    Dim busyIndicatorDispatcher As Dispatcher = arg.Item2
    Dim timeout As Integer = arg.Item3
    Dim verificaSeInviare As Boolean = arg.Item4
    Me.Dispatcher.BeginInvoke(DispatcherPriority.Send, DirectCast(Sub()
                                                                      If Not SendInfo("-= AUTO =-", verificaSeInviare, True) Then
                                                                          mStopMailAuto = True
                                                                          worker.CancelAsync()
                                                                      End If
                                                                  End Sub, SendOrPostCallback), Nothing)
    For i As Integer = 1 To timeout
        If worker.CancellationPending Then
            e.Cancel = True
            Exit Sub
        End If
        mainWindowDispatcher.Invoke(New Action(Of Integer)(AddressOf Me.UpdateProgressBarValue), i)
        busyIndicatorDispatcher.Invoke(New Action(Of Integer)(AddressOf Me.UpdateCaption), timeout - i)
        Thread.Sleep(1000)
    Next
End Sub

''' <summary>
''' Metodo che disabilita la visualizzazione del BusyIndicator_InvioAuto nel momento in cui il job è terminato
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub OnWorkCompletedAuto(sender As Object, e As RunWorkerCompletedEventArgs)
    Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)

    If Object.ReferenceEquals(mBackgroundWorker, worker) Then
        mBackgroundWorker.Dispose()
        mBackgroundWorker = Nothing

        BusyIndicator_InvioAuto.IsBusy = False
        Thread.Sleep(1000)
        If Not mStopMailAuto Then
            SendInfoAuto(True)
        End If
    End If
End Sub

 

Another feature of the class is the use of validation (see <a href="#InfoViewModel">InfoViewModel.vb</a>) for the information present in UI, to ensure the proper functioning of the buttons for sending the email. The validation is specifically carried out by using in the XAML of MainWindow.xaml by the MultiValidatorToBoolean that is defined in the class <a href="#Application">Application.xaml.vb</a> like a MultiConverter call <a href="#ValidatorConverter">ValidatorConveter</a>:

XML
    <Button Name="Button_MailAuto" VerticalAlignment="Top" HorizontalAlignment="Right" Width="40" Height="40" Grid.Row="1" Grid.Column="3"
            ToolTip="Start the check to send automatic email with the informations" Margin="5"
            Visibility="{Binding ElementName=BusyIndicator_InvioAuto, Path=IsBusy, Converter={StaticResource CollapsedIfTrue}}">
        <Image Source="/InfoIP;component/Images/mail_start.ico" Stretch="None"/>
        <Button.Style>
            <Style TargetType="Button">
           <Style.Triggers>
               <Trigger Property="IsEnabled" Value="False">
              <Setter Property="BorderBrush" Value="Red"/>
               </Trigger>
           </Style.Triggers>
            </Style>
        </Button.Style>
        <Button.IsEnabled>
            <MultiBinding Converter="{StaticResource MultiValidatorToBoolean}">
           <Binding ElementName="TextBox_EmailFrom" Path="(Validation.Errors)[0].ErrorContent"/>
           <Binding ElementName="TextBox_Server" Path="(Validation.Errors)[0].ErrorContent"/>
           <Binding ElementName="TextBox_Port" Path="(Validation.Errors)[0].ErrorContent"/>
           <Binding ElementName="TextBox_EmailTo" Path="(Validation.Errors)[0].ErrorContent"/>
           <Binding ElementName="TextBox_Timeout" Path="(Validation.Errors)[0].ErrorContent"/>
            </MultiBinding>
        </Button.IsEnabled>
    </Button>

 

The latest application's feature is the opportunity to put it in the system tray through the notify icon.
To realize ir there is the chance to use the project WPF NotifyIcon (much more complete), but having had little time to devote myself to it, I decided to adopt the solution provided by WinForms adding the System.Windows.Forms and System.Drawing in assembly references.
So through this code the goal has been reached:

VB.NET
Class MainWindow
    Inherits System.Windows.Window

...

    ''' <summary>
    ''' Metodo che gestisce la creazione della Notify Icon per la System Tray Application
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub SetNotifyIconInSystemTrayApplication()
        mNotifyIcon = New System.Windows.Forms.NotifyIcon()
        mNotifyIcon.Icon = New System.Drawing.Icon(FileIco)
        mNotifyIcon.Text = Assembly.GetExecutingAssembly.GetName.Name.ToString()
        mNotifyIcon.BalloonTipText = Me.Title
        mNotifyIcon.Visible = True
        AddHandler mNotifyIcon.DoubleClick, AddressOf RipristinaDaSystemTray
    End Sub

    ''' <summary>
    ''' Metodo che gestisce il ripristino della Window da System Tray
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub RipristinaDaSystemTray(sender As Object, e As EventArgs)
        Me.Show()
        Me.WindowState = WindowState.Normal
    End Sub

...

    Protected Overrides Sub OnStateChanged(e As EventArgs)
        If Me.WindowState = WindowState.Minimized Then
            Me.Hide()
            Me.ShowInTaskbar = False
            If mNotifyIcon IsNot Nothing Then
                mNotifyIcon.ShowBalloonTip(400)
                mNotifyIcon.Visible = True
            End If
        Else
            mNotifyIcon.Visible = False
            Me.ShowInTaskbar = True
        End If
        MyBase.OnStateChanged(e)
    End Sub


    Protected Overrides Sub OnClosed(e As EventArgs)
        mNotifyIcon.Dispose()
        mNotifyIcon = Nothing
        MyBase.OnClosed(e)
    End Sub

 

Conclusion

I hope this article with the attachments will help you, if you find bugs or improvements please contact me. Happy coding! Smile | :)

History

The changelog:

+ version 1.0.0 (03/07/2014):
 - 

+ beta version (20/06/2014):
 - After the beta test release that without much smartness sent you an email at the end of timeout (in short a spam software :P), I decided to improve this tool and this article is the result. I hope that like you ;P

... that in the code behind is returned by...     :P

VB.NET
''' <summary>
''' Funzione che restituisce la versione del software
''' </summary>
''' <returns>La versione corrente dell'assembly</returns>
''' <remarks></remarks>
Private Function GetVersion() As String
    Return Assembly.GetExecutingAssembly.GetName.Name.ToString() + " - v. " +
         Assembly.GetExecutingAssembly.GetName.Version.Major.ToString() _
         & "." & Assembly.GetExecutingAssembly.GetName.Version.Minor.ToString() _
         & "." & Assembly.GetExecutingAssembly.GetName.Version.Build.ToString()
End Function

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)