在我的应用程序中,我使用了ToastForms类向用户显示实时警报的弹出通知。我一直在努力研究从数据库中轮询警报的最佳方法,并经常将它们呈现给用户。我查询数据库,找到所有新警报,然后每隔10秒左右将这些警报作为弹出窗口呈现给用户。我一直在尝试为这个过程决定最好的方法/练习,因为我不想在我不断轮询数据库时导致任何高CPU使用率或程序挂起。
经过一段时间的实验,我决定将我的想法转移到使用System.Timers.Timer并将我的代码放在Get_Alerts过程中:
Private Sub frmNewDashboard_Load(sender As Object, e As EventArgs) Handles MyBase.Load
...
tmr_GetAlerts = New System.Timers.Timer(10000)
AddHandler tmr_GetAlerts.Elapsed, AddressOf GetAlerts
tmr_GetAlerts.AutoReset = False
tmr_GetAlerts.Start()
...
End Sub
Private Sub GetAlerts(source As Object, e As ElapsedEventArgs)
...
'Query the database and populate a datatable
'Determine all alert types
'Handle Major Alerts, Minor Alerts, etc.
If (Current_User.EnableNotifications = True) And (Current_User.NP_AMajor = True) Then
If MajorCount > 1 Then
Dim slice As ToastForm
slice = New ToastForm((Current_User.Notify_Seconds * 1000), MajorCount & " New Major Alert(s) Detected")
slice.Height = 100
slice.Show()
End If
End If
...
'Same code repeats to handle Minor Alerts
End Sub
上面的代码用于在普通的Forms.Timer上正常工作,但是,因为它将它移动到System.Timers.Timer我发现ToastForm会弹出正常但似乎挂起并且永远不会关闭:
它没有产生任何错误,所以我不确定错误在哪里。我假设它与在我的计时器的不同线程上打开ToastForm有关,但我不确定。
任何帮助将不胜感激。感谢。
更新 下面是运行Toastform的代码。我从网上找到的一些代码导入了这个类,所以它不是我的代码。我只是传递参数。在我介绍System.Timers.Timer之前,它一切正常(并且关闭)。
Imports System.Runtime.InteropServices
Public Class ToastForm
Private _item As ListViewItem = Nothing
Private TooltipVisible As Boolean = False
Private SelectedCallQueue As String = Nothing
Private SelectedOverduePeriod As String
Private OnlineUserCount As Integer
#Region " Variables "
''' <summary>
''' The list of currently open ToastForms.
''' </summary>
Private Shared openForms As New List(Of ToastForm)
''' <summary>
''' Indicates whether the form can receive focus or not.
''' </summary>
Private allowFocus As Boolean = False
''' <summary>
''' The object that creates the sliding animation.
''' </summary>
Private animator As FormAnimator
''' <summary>
''' The handle of the window that currently has focus.
''' </summary>
Private currentForegroundWindow As IntPtr
#End Region 'Variables
#Region " APIs "
''' <summary>
''' Gets the handle of the window that currently has focus.
''' </summary>
''' <returns>
''' The handle of the window that currently has focus.
''' </returns>
<DllImport("user32")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
''' <summary>
''' Activates the specified window.
''' </summary>
''' <param name="hWnd">
''' The handle of the window to be focused.
''' </param>
''' <returns>
''' True if the window was focused; False otherwise.
''' </returns>
<DllImport("user32")> _
Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
End Function
#End Region 'APIs
#Region " Constructors "
''' <summary>
''' Creates a new ToastForm object that is displayed for the specified length of time.
''' </summary>
''' <param name="lifeTime">
''' The length of time, in milliseconds, that the form will be displayed.
''' </param>
Public Sub New(ByVal lifeTime As Integer, ByVal message As String)
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
'Set the time for which the form should be displayed and the message to display.
Me.lifeTimer.Interval = lifeTime
Me.messageLabel.BackColor = ColorTranslator.FromHtml(Current_User.NWC)
Me.messageLabel.Text = message
'Display the form by sliding up.
Me.animator = New FormAnimator(Me, _
FormAnimator.AnimationMethod.Slide, _
FormAnimator.AnimationDirection.Up, _
200)
End Sub
#End Region 'Constructors
#Region " Methods "
''' <summary>
''' Displays the form.
''' </summary>
''' <remarks>
''' Required to allow the form to determine the current foreground window before being displayed.
''' </remarks>
Public Shadows Sub Show()
Try
'Determine the current foreground window so it can be reactivated each time this form tries to get the focus.
Me.currentForegroundWindow = GetForegroundWindow()
'Display the form.
MyBase.Show()
'Play a notification sound
If Current_User.NotifySound = True Then NotificationSound.Play()
Catch ex As Exception
ErrorTrap(ex, "ToastForm: Show()")
End Try
End Sub
#End Region 'Methods
#Region " Event Handlers "
Private Sub ToastForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
'Display the form just above the system tray.
Me.Location = New Point(Screen.PrimaryScreen.WorkingArea.Width - Me.Width - 5, _
Screen.PrimaryScreen.WorkingArea.Height - Me.Height - 5)
'Move each open form upwards to make room for this one.
For Each openForm As ToastForm In ToastForm.openForms
openForm.Top -= Me.Height + 5
Next
'Add this form from the open form list.
ToastForm.openForms.Add(Me)
'Start counting down the form's liftime.
Me.lifeTimer.Start()
Catch ex As Exception
ErrorTrap(ex, "ToastForm: ToastForm_Load()")
End Try
End Sub
Private Sub ToastForm_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Activated
Try
'Prevent the form taking focus when it is initially shown.
If Not Me.allowFocus Then
'Activate the window that previously had the focus.
SetForegroundWindow(Me.currentForegroundWindow)
End If
Catch ex As Exception
ErrorTrap(ex, "ToastForm: ToastForm_Activated()")
End Try
End Sub
Private Sub ToastForm_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
Try
'Once the animation has completed the form can receive focus.
Me.allowFocus = True
'Close the form by sliding down.
Me.animator.Direction = FormAnimator.AnimationDirection.Down
Catch ex As Exception
ErrorTrap(ex, "ToastForm: ToastForm_Shown()")
End Try
End Sub
Private Sub ToastForm_FormClosed(ByVal sender As Object, ByVal e As FormClosedEventArgs) Handles MyBase.FormClosed
Try
'Move down any open forms above this one.
For Each openForm As ToastForm In ToastForm.openForms
If openForm Is Me Then
'The remaining forms are below this one.
Exit For
End If
openForm.Top += Me.Height + 5
Next
'Remove this form from the open form list.
ToastForm.openForms.Remove(Me)
Catch ex As Exception
ErrorTrap(ex, "ToastForm: ToastForm_FormClosed()")
End Try
End Sub
Private Sub lifeTimer_Tick(ByVal sender As Object, ByVal e As EventArgs) Handles lifeTimer.Tick
Try
'The form's lifetime has expired.
Me.Close()
Catch ex As Exception
ErrorTrap(ex, "ToastForm: lifeTimer_Tick()")
End Try
End Sub
#End Region 'Event Handlers
End Class
答案 0 :(得分:0)
我认为这里的解决方案可能是在UI线程上创建通知表单。您仍然可以执行查询以在辅助线程上获取数据,然后封送对UI线程的方法调用以显示通知。
一个简单的选择是使用Windows.Forms.Timer
而不是Timers.Timer
,并在RunWorkerAsync
事件处理程序中的BackgroundWorker
上调用Tick
。然后,您可以在DoWork
事件处理程序中执行查询,该处理程序在辅助线程上执行,并在RunWorkerCompleted
事件处理程序中显示通知,该处理程序在UI线程上执行。