1

私が欲しいのは、表示するメッセージがあるときに右下隅に表示される小さな通知メッセージです。何もない場合、通知メッセージは表示されません。通知メッセージがフォーカスを奪ったり、メイン アプリケーションをブロックしたりしてはなりません。

私が持っているのは、タスクを一種のメッセージ サービスとして実行するアプリケーションです。このアプリケーションには、モーダル ダイアログとして開く複数のダイアログが含まれています。

メッセージがアプリケーションに到着すると、監視可能なリストに追加されます。これにより、通知メッセージを表示するフォームでイベント ハンドラが起動され、再描画されてリストの最初の項目が表示されます。メッセージが読み取られるか閉じられると、リストから削除されてイベントが再び発生し、リストの最初の項目からの情報でフォームが更新されます。リストが空の場合、フォームは非表示になります。

私の問題は、メッセージを受け取って通知メッセージフォームが表示され、それを閉じる前にメインアプリケーションでモーダルダイアログが開かれた場合、通知メッセージを含むフォームがモーダルダイアログであってもすべての上にあることです。しかし、それはクリック可能ではありません。

いくつかのフォーラムを検索して回答を読みましたが、回答を見つけることができませんでした。

この動作をシミュレートする小さなテスト アプリケーションは、Github にあります。 https://github.com/Oneleg/NotificationMessage

簡単な情報:

NotificationMessage フォームには次のものがあります。

  • FormBorderStyle = なし
  • 最上位 = False
  • Show() で表示されます
  • ShowWithoutActivation() をオーバーロードします
  • WS_EX_NOACTIVATE WS_EX_TOOLWINDOW WS_EX_TOPMOST で CreateParams をオーバーロードします。

これをどのように解決できるかについてのアイデアはありますか?

4

1 に答える 1

1

自分の質問に答えることができるようです。

答えは、独自のメッセージポンプを持つアプリケーションとして NotificationMessage を作成することです。

Application.Run(New NotificationMessage(_messageList))

いくつかの変更を加えた後、メインは次のようになります。

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 に答える