1

我想要的是一个小的通知消息,当有任何消息要显示时,它会显示在右下角。如果没有,则不会显示通知消息。通知消息不应窃取焦点或阻塞主应用程序。

我所拥有的是一个将任务作为一种消息服务运行的应用程序。此应用程序包含多个作为模式对话框打开的对话框。

当消息到达应用程序时,它会被添加到可观察列表中。这会在显示通知消息的表单中触发一个事件处理程序,并重新绘制它以显示列表中的第一项。当一条消息被读取/关闭时,它会从再次触发事件的列表中删除,并使用列表中第一项的信息更新表单。如果列表为空,则隐藏表单。

我的问题是,如果我收到一条消息并显示通知消息表单,并且在我关闭它之前,在主应用程序中打开了一个模式对话框,我的通知消息表单仍然位于所有内容之上,甚至是模式对话框,但它不可点击。

我已经搜索并阅读了几个论坛以寻求答案,但一直未能找到答案。

可以在 Github 上找到一个模拟这种行为的小型测试应用程序。 https://github.com/Oneleg/NotificationMessage

一些快速信息:

NotificationMessage 表单具有:

  • FormBorderStyle = 无
  • 最顶层 = 假
  • 用 Show() 显示
  • 重载 ShowWithoutActivation()
  • 使用 WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST 重载 CreateParams

关于如何解决这个问题的任何想法?

4

1 回答 1

1

看起来我将能够回答我自己的问题。

答案是将 NotificationMessage 创建为具有自己的消息泵的应用程序。

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

我现在有一条通知消息,该消息仅在有任何消息要显示时才可见,在新消息到达时不会窃取焦点,始终位于顶部并且即使在主应用程序中打开模态表单后也可单击。

于 2012-11-23T09:33:18.070 回答