65.9K
CodeProject 正在变化。 阅读更多。
Home

InfoIP - 一个查找您的 WAN IP 的工具

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2014年9月8日

CPOL

5分钟阅读

viewsIcon

19664

downloadIcon

549

通过电子邮件发送您的 WAN IP 和其他信息的 SW

 下载 InfoIP.zip(源代码、安装项目、exe 和 setup.msi)

引言

如今,一些 ISP 会为新客户提供自己的路由器,这些路由器的配置设置似乎已被锁定 这款小巧 的软件 旨在 克服这一困难 ,通过电子邮件发送用于远程桌面连接到您 PC 的必需信息,例如 LAN IP、WAN IP 和默认网关 IP

背景

我之所以从事这个项目,是因为我的一位同事问我是否有软件可以让他知道 WAN IP,因为他使用的软件在升级了路由器固件(由您 ISP 提供的非个人路由器)后停止工作了,因为无法实现适当的配置。

这就是为什么我开始着手开发这个工具,其目的是查找并发送 WAN IP 到电子邮件。

 

使用代码和要点

在此文章中,我上传了源代码(解决方案还包括 VS 2010 的安装项目)以及 exe

最有趣的类 是: 

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

我希望很快能将它们 连同整个项目翻译成 C#

InfoViewModel.vb

在此类中实现了 MVVM 逻辑,基于 InfoData ,该类包含 UI 中显示的信息。InfoViewModel 的特点是它实现了 IDataErrorInfo INotifyPropertyChanged,因此允许您分别验证单个信息以及在 UI 中更新和更改信息。

#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
#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

 

此处展示了如何通过适当的正则表达式验证电子邮件地址

    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

 

此处展示了如何通过两个属性的PropertyChanged事件自动更新 UI,例如

    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
    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

此类与Crypt类一起使用, MainWindow ,负责从 app.config 获取/设置信息并公开

  • 一个名为GetValueAppSettingShared函数
    ''' <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
  • 一个名为SetValueAppSettingShared子过程
    ''' <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

此类负责编码和解码将保存在 app.config 中的密码, 通过使用 算法RijndaelManaged(Microsoft 提供)加密后
加密的特点是使用 MAC 地址——通过Network类找到——与VariabiliGlobali.BaseStringForRijndael中设置的字符串一起,创建 256 位的密钥初始化向量从而 尽可能 确保 信息的安全性

    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
    ''' <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
    ''' <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

此类显然负责将 UI 中显示的信息发送给您。其特点是Invia函数,该函数 根据 调用者(按需邮件或自动邮件) 传入 最后两个参数 的特定值
But除此之外 ,主要的是 SmtpClient 类的实例 创建,并设置.DeliveryMethod = SmtpDeliveryMethod.Network.UseDefaultCredential = False ,这对于正确的身份验证是必需的。

    ''' <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

此类负责查找以下信息

  • MAC 地址 ,在以太网和无线存在 的情况下返回无线接口的值,如此处所示
    ''' <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
    ''' <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
  • 默认网关 IP
    ''' <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
    ''' <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

这是 管理 应用程序 的类, 从最广义上讲 事实上, 在这里 您可以指定:

<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>
  • 一些事件,例如Application_Startup例如 以验证 没有 多个应用程序实例在运行)和Application_DispatcherUnhandledException用于捕获未处理的异常
    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
    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
#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

这是主窗口的类,它的特点是 使用了BackgroundWorker,以启用 Extended WPF Toolkit 的BusyIndicator类的使用,在电子邮件发送期间

    ''' <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

如上所示,对于自动电子邮件发送功能 ,使用了一个特定的忙碌指示器BusyIndicator_InvioAuto,这允许在超时到期时更新值(见第二张图)——请注意,所需超时值作为参数传递给mBackgroundWorker.RunWorkerAsync
超时管理使用了此类中的两个属性,然后通过<Window.Resources>标签在 XAML 中公开

  • 属性
#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
  • XAML <Window.Resources>
    <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>
  • XAML BusyIndicator_InvioAuto
<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}"/>

此外如上所示,在第一个代码片段中,mBackgroundWorker变量通过设置 DoWorker RunWorkerCompleted事件的两个处理程序来工作

    ''' <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

 

该类的 另一项功能 是使用 验证(见InfoViewModel.vb)来验证 UI 中 存在的信息 确保按钮 的正常运行以发送 电子邮件。 验证 是通过在MainWindow.xaml的 XAML 中使用MultiValidatorToBoolean来完成的,该类在Application.xaml.vb中被定义为MultiConverter,并调用ValidatorConveter

    <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>

 

应用程序的最新功能是有机会通过通知图标将其放入系统托盘
为了实现这一点,可以使用项目 WPF NotifyIcon更全面 但由于时间紧迫 我决定采用 WinForms 提供的解决方案,在程序集引用中添加System.Windows.FormsSystem.Drawing
因此,通过这段代码,目标已经达成。

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

 

结论

希望这篇文章和附件对您有所帮助,如果您发现错误或有改进意见,请联系我。祝您编码愉快!Smile | :)

历史

更新日志

+ 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

……在代码隐藏中由……返回 :P

    ''' <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
© . All rights reserved.