WPF 弹出行为

WPF popup behavior

我正在尝试调整 this WPF Popup 实现来实现消息传递系统。目标是在我需要发送消息时弹出窗口,用户可以通过双击消息关闭弹出窗口,消息也会在设定时间后消失。 我现在拥有的是这个

using assembly PresentationFramework
using assembly System.Windows.Forms
using assembly System.Drawing

$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml =  '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
    <Grid Name="grid" Background="#313130" Height="200" Width="400">
        <Label Name="label" Content="Messanger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
        <TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
    </Grid>
</Window>'

$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$message = $Window.FindName('Message')

# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
    $window.Hide()
})

$messageCount = 1
do {
    if ((Get-Random -Minimum:0 -Maximum:100) -le 30) {
        $messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
        $message.Text = $messageString
        Write-Host $messageString
        $messageCount ++
        $window.Show()
        Start-Sleep -s:10
        $window.Hide()
    }

    Start-Sleep -s:5
} while ($messageCount -le 5)

这部分有效,因为第一条消息会弹出,它会在 10 秒后隐藏。但是,双击隐藏不起作用,后续显示也不会发生。我知道正在满足条件,因为控制台会显示每个新的时间消息。

所以... 我的 MouseDoubleClick 活动出了什么问题,以及 是什么让第一条消息不显示?

您正在使用的 Start-Sleep 使代码等待而不处理其他事件,例如 MouseDoubleClick。

为了让一个window保持响应,同时等待一定时间,需要添加一个System.Windows.Forms.Timer对象。 此计时器有一个 Tick 事件,您可以从 运行 停止它以继续执行代码。

我建议这样:

Add-Type -AssemblyName PresentationFramework
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$icon = [System.Drawing.Icon]::ExtractAssociatedIcon("$pshome\powershell.exe")
[xml]$xaml =  '<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="window" WindowStyle="None" Height="200" Width="400"
ResizeMode="NoResize" ShowInTaskbar="False">
    <Grid Name="grid" Background="#313130" Height="200" Width="400">
        <Label Name="label" Content="Messenger Test" Foreground="White" FontSize="18" Margin="10,10,0,15"/>
        <TextBox x:Name="Message" Height = "50" FontSize="18" Margin="10,10,0,15" />
    </Grid>
</Window>'

$window = [Windows.Markup.XamlReader]::Load([System.Xml.XmlNodeReader]::New($xaml))
$window.Left = [System.Windows.SystemParameters]::WorkArea.Width-$window.Width
$window.Top = 0
$window.Topmost = $true
$message = $Window.FindName('Message')

# create a Timer object to use instead of Start-Sleep
# old PowerShell: $timer = New-Object System.Windows.Forms.Timer
$timer = [System.Windows.Forms.Timer]::new()
$timer.Add_Tick({
    Write-Host ">> Timer Tick.."
    $timer.Stop()
})

# Close the window if it's double clicked
$window.Add_MouseDoubleClick({
    Write-Host ">> Mouse Double Click.."
    $timer.Stop()
})

$maxLoops = 5
for ($messageCount = 1; $messageCount -le $maxLoops; $messageCount++) {
    $messageString = "($messageCount) $(Get-Date -format 'HH:mm:ss')"
    $message.Text = $messageString
    Write-Host $messageString

    $window.Show()

    # start the timer to fire after 10 seconds and then disable itself
    $timer.Stop()
    $timer.Interval = 10000
    $timer.Start()
    # while the Tick event did not occur, respond to other events
    # such as e mouse double-click on the window
    while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }

    # the timer tick event happened or the user double-clicked the window
    $window.Hide()

    # end of the loop reached, no use waiting some more..?
    if ($messageCount -ge $maxLoops) { break }

    # start the timer to fire after (random) 1 - 5 seconds
    $interval = (Get-Random -Minimum 1 -Maximum 5)
    $timer.Stop()
    $timer.Interval = $interval * 1000
    $timer.Start()

    Write-Host ">> Loop wait $interval seconds.."
    while ($timer.Enabled) { [System.Windows.Forms.Application]::DoEvents() }
} 

# we're done, clean-up
$timer.Dispose()
$window.Close()