当显示模态对话框时,表单在顶部但不可单击

时间:2012-11-22 13:14:48

标签: vb.net focus modal-dialog topmost createparams

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

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

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

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

我搜索并阅读了多个论坛以获得答案,但未能得出答案。

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

一些快速信息:

NotificationMessage表单包含:

  • FormBorderStyle =无
  • Topmost = False
  • 显示为Show()
  • 重载ShowWithoutActivation()
  • 使用WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST
  • 重载CreateParams

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

1 个答案:

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

我现在有一条通知消息,只有在显示任何消息时才会显示,当新消息到达时不会窃取焦点,即使在主应用程序中打开模式窗体后也可以点击它