我想要的是当有任何要显示的消息时,右下角会显示一条小通知消息。如果没有,则不会显示通知消息。通知消息不应该窃取焦点或阻止主应用程序。
我所拥有的是将Task作为一种消息服务运行的应用程序。此应用程序包含多个对话框,这些对话框将作为模式对话框打开。
当消息到达应用程序时,它将被添加到可观察列表中。这将以显示通知消息的形式触发事件处理程序,并重新绘制以显示列表中的第一个项目。 当读取/关闭消息时,它将从列表中删除,该列表再次触发事件,并使用列表中第一项的信息更新表单。 如果列表为空,表单将被隐藏。
我的问题是,如果我收到一条消息并显示通知消息表单,并且在关闭它之前在主应用程序中打开一个模态对话框,带有通知消息的表单仍然在所有内容之上,甚至是模态对话框,但它不可点击。
我搜索并阅读了多个论坛以获得答案,但未能得出答案。
可以在Github上找到模拟此行为的小型测试应用程序。 https://github.com/Oneleg/NotificationMessage
一些快速信息:
NotificationMessage表单包含:
关于如何解决这个问题的任何想法?
答案 0 :(得分:1)
看起来我能够回答我自己的问题。
答案是将NotificationMessage创建为具有自己的messagepump的应用程序。
Application.Run(New NotificationMessage(_messageList))
经过一些修改后,我的Main现在看起来像这样:
Imports System.Threading
Imports System.Threading.Tasks
Public Class frmMain
Private _notificationMessage As NotificationMessage
Private _task As Task
Private _messageList As ObservableGenericList(Of String) = New ObservableGenericList(Of String)
Private ReadOnly _cancelMessages As CancellationTokenSource = New CancellationTokenSource()
Private Sub btnModal_Click(sender As System.Object, e As System.EventArgs) Handles btnModal.Click
frmModal.ShowDialog()
End Sub
Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
AddHandler _messageList.Changed, AddressOf MessageListChanged
End Sub
Private Sub NotificationMessageLoop(mess As String)
_notificationMessage = New NotificationMessage(_messageList)
_messageList.Add(mess)
Application.Run(_notificationMessage)
End Sub
Private Sub btnMessage_Click(sender As System.Object, e As System.EventArgs) Handles btnMessage.Click
Dim newMessage = String.Format("Message no {0}", _messageList.Count + 1)
If _task Is Nothing Then
_task = Task.Factory.StartNew(Sub() NotificationMessageLoop(newMessage), _cancelMessages.Token)
Else
_messageList.Add(newMessage)
End If
End Sub
Private Sub MessageListChanged()
If Not _messageList.Any Then
_cancelMessages.Cancel()
End If
End Sub
End Class
NotificationMessage看起来像这样:
Imports System.Runtime.InteropServices
Public Class NotificationMessage
Public Sub New(messages As ObservableGenericList(Of String))
InitializeComponent()
_messages = messages
AddHandler _messages.Changed, AddressOf ListChanged
End Sub
Private ReadOnly _messages As ObservableGenericList(Of String)
Private Delegate Sub ListChangedDelegate()
Private Sub ListChanged()
If InvokeRequired Then
BeginInvoke(New ListChangedDelegate(AddressOf ListChanged))
Return
End If
If _messages.Any Then
Dim message As String = _messages.First
txtMessage.Text = message
lblCounter.Text = String.Format("({0} messages)", _messages.Count)
Show()
Else
Hide()
End If
End Sub
Private Sub MessageLoad(sender As System.Object, e As EventArgs) Handles MyBase.Load
Left = Screen.PrimaryScreen.WorkingArea.Width - Width
Top = Screen.PrimaryScreen.WorkingArea.Height - Height
End Sub
Private Sub btnClose_Click(sender As System.Object, e As System.EventArgs) Handles btnClose.Click
_messages.RemoveFirst()
End Sub
#Region "Overrides"
Private Const WS_EX_NOACTIVATE = &H8000000 ' Do not steal focus
Private Const WS_EX_TOOLWINDOW = &H80 ' Makes form hidden from Alt + Tab window
Private Const WS_EX_TOPMOST = &H8 ' Makes window topmost
''' <summary> Indicates whether the window will be activated when it is shown. </summary>
''' <remarks> http://msdn.microsoft.com/en-us/library/system.windows.forms.form.showwithoutactivation.aspx </remarks>
Protected Overrides ReadOnly Property ShowWithoutActivation() As Boolean
Get
Return True
End Get
End Property
''' <summary> Override for creation parameters that are set when control handle is created. </summary>
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim params As CreateParams = MyBase.CreateParams
params.ExStyle = params.ExStyle Or WS_EX_NOACTIVATE Or WS_EX_TOOLWINDOW Or WS_EX_TOPMOST
Return params
End Get
End Property
#End Region
End Class
我现在有一条通知消息,只有在显示任何消息时才会显示,当新消息到达时不会窃取焦点,即使在主应用程序中打开模式窗体后也可以点击它