InfoIP - 一个查找您的 WAN IP 的工具
通过电子邮件发送您的 WAN IP 和其他信息的 SW
![]() |
下载 InfoIP.zip(源代码、安装项目、exe 和 setup.msi) |
引言
如今,一些 ISP 会为新客户提供自己的路由器,这些路由器的配置设置似乎已被锁定。 这款小巧 的软件 旨在 克服这一困难 ,通过电子邮件发送用于远程桌面连接到您 PC 的必需信息,例如 LAN IP、WAN IP 和默认网关 IP。
背景
我之所以从事这个项目,是因为我的一位同事问我是否有软件可以让他知道 WAN IP,因为他使用的软件在升级了路由器固件(由您 ISP 提供的非个人路由器)后停止工作了,因为无法实现适当的配置。
这就是为什么我开始着手开发这个工具,其目的是查找并发送 WAN IP 到电子邮件。
使用代码和要点
在此文章中,我上传了源代码(解决方案还包括 VS 2010 的安装项目)以及 exe。
最有趣的类 是:
我希望很快能将它们 连同整个项目翻译成 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 获取/设置信息并公开
- 一个名为
GetValueAppSetting
的Shared
函数
''' <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
- 一个名为
SetValueAppSetting
的Shared
子过程
''' <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
- WAN IP,为了 检索 此信息,有必要 使用网站 http://checkip.dyndns.org/ 提供的服务,使用
WebClient
类
''' <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
这是 管理 应用程序 的类, 从最广义上讲 。 事实上, 在这里 您可以指定:
- 启动时 您将 看到的 窗口(见
StartupUri
)- 它的Application.xaml
<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
- 类 Converter,然后在
Application.xaml
(见第一个代码片段)中指定,以允许在 其他类中使用它们
#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.Forms
和System.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
结论
希望这篇文章和附件对您有所帮助,如果您发现错误或有改进意见,请联系我。祝您编码愉快!
历史
更新日志
+ 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