Форма сверху, но не кликабельная, когда отображается модальное диалоговое окно
То, что я хочу, - это небольшое уведомление, которое отображается в правом нижнем углу, когда есть какие-либо сообщения, которые будут показаны. Если их нет, уведомление не будет отображаться. Уведомление не должно красть фокус или блокировать основное приложение.
У меня есть приложение, которое запускает Задачу как своего рода службу сообщений. Это приложение содержит несколько диалогов, которые открываются как модальные диалоги.
Когда сообщение поступает в приложение, оно добавляется в наблюдаемый список. Это запускает обработчик событий в форме, показывающей сообщение уведомления, и он перерисовывается, чтобы показать первый элемент в списке. Когда сообщение прочитано / закрыто, оно удаляется из списка, который снова запускает событие, и форма обновляется информацией из первого элемента в списке. Если список пуст, форма скрыта.
Моя проблема в том, что, если я получаю сообщение и отображается форма сообщения уведомления, и, прежде чем я его закрываю, в главном приложении открывается модальное диалоговое окно, моя форма с уведомлением по-прежнему находится поверх всего, даже модального диалога. но это не кликабельно.
Я искал и прочитал несколько форумов для ответа, но не смог найти ответ.
Небольшое тестовое приложение, имитирующее это поведение, можно найти на Github. https://github.com/Oneleg/NotificationMessage
Немного быстрой информации:
Форма NotificationMessage имеет:
- FormBorderStyle = Нет
- Topmost = Ложь
- Показывается с помощью Show()
- Перегрузки ShowWithoutActivation()
- Перегружает CreateParams с помощью WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST
Любые идеи о том, как я мог бы решить это?
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
Теперь у меня есть уведомление, которое видно только тогда, когда есть какие-либо сообщения для показа, не крадет фокус, когда приходит новое сообщение, всегда вверху и доступно для нажатия даже после открытия модальной формы в главном приложении.