【问题标题】:Form on top but not clickable when modal dialog is shown显示模态对话框时,窗体在顶部但不可点击
【发布时间】:2012-11-10 21:35:55
【问题描述】:

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

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

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

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

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

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

一些快速信息:

NotificationMessage 表单有:

  • FormBorderStyle = 无
  • 最上面 = 错误
  • 用 Show() 显示
  • 重载 ShowWithoutActivation()
  • 使用 WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST 重载 CreateParams

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

【问题讨论】:

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


    【解决方案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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多