【问题标题】:WPF popup behaviorWPF 弹出行为
【发布时间】:2020-06-07 13:10:12
【问题描述】:

我正在尝试调整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 事件有什么问题,以及 什么是在第一次显示后保留消息?

【问题讨论】:

    标签: wpf powershell popup


    【解决方案1】:

    您正在使用的Start-Sleep 使代码在不处理其他事件(如 MouseDoubleClick)时等待。

    为了让一个窗口保持响应并同时等待一段时间,你需要添加一个System.Windows.Forms.Timer对象。 此 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()
    

    【讨论】:

    • 啊哈!这完全有道理。我可以看到,当我转向 WPF 和 Classes 时,可能需要重新考虑很多 PS cmdlet。有很多东西要学,而且只是星期一。 :)
    • 所以,我在一个 2 核 VM 上工作,但我看到了一些奇怪的东西。只要它正在处理,我就使用 50% 的 CPU。我什至将检查之间的间隔增加到 10-50 秒,但仍然保持在 50%。完成时或如果我终止执行,我几乎立即降至 1% 的基本状态。这似乎……错了。有没有那么重的计时器的替代品?我曾希望通过计划任务将 PS 作为后台服务运行,并每 10 秒左右检查一次新消息,但在架构师工作站上,我绝对不能 100% 占用整个核心。
    • @Gordon 该信息应该在问题中。从未提及进行此练习的原因。对于你想要的,我想最好创建一个service 并让它显示弹出窗口,然后销毁窗口和计时器。需要时再次显示弹出窗口,销毁等。这样您就不会将所有内容都保留在内存中等待弹出的原因。
    • 我一直在努力保持简单,想学一件事,然后扩展。至于服务,据我所知,它们仅作为系统运行,因此必须以不同方式处理用户看到的消息。但是,我明天有一些关于该链接的阅读。我还在寻找消息队列的 LM 注册表中某个键的更改,所以也许我可以对事件做一些事情,而不是在计时器上轮询。有很多变化可以尝试。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    • 1970-01-01
    • 1970-01-01
    • 2012-06-29
    • 2012-09-28
    • 1970-01-01
    • 2016-12-21
    相关资源
    最近更新 更多