Windows 10风格的MessageBox/通知的API

7

我想知道是否有一个API可以创建类似于Windows 10风格的自定义通知,比如当更新可用时显示的那种通知?

enter image description here

我知道Citrix可以发送像这样的消息,似乎他们使用sessionmsg.exe。不幸的是,我找不到任何有关该exe支持的参数的帮助。
此外,API将是首选。
另一件事:你如何称呼这种消息?横幅?消息?消息框?系统消息?

所寻找的是UWP的[Windows.UI.PopUps.MessageDialog],但我认为无法从PowerShell中调用。最好的方法是使用XAML样式化窗口,使其看起来类似,这远非一行命令所能完成。这里没有快速解决方案。即使在C#中,您也需要在特定的UWP项目中完成此操作。一些[Windows.UI]的东西确实有效,比如你会在屏幕右下角收到的通知。 - Ash
你正在寻找Toast通知吗?https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-ux-guidance 还有MessageDialog https://learn.microsoft.com/en-us/uwp/api/windows.ui.popups.messagedialog - Simon Mourier
3个回答

5

读完问题后,我觉得很有趣,决定自己探究一下可能性并分享我的答案。

由于您标记了问题为C#,我的解决方案将基于C#。顺便说一句,我无法找到默认的API来完成任务,如果其他人找到了解决方案并提供样例,我会很乐意投票支持。

我从UWP方案入手,使用ContentDialog创建了一个非常简单的信息对话框。创建一个UWP项目并添加以下代码:

private async void ShowMessage()
{
    ContentDialog dialog = new ContentDialog
    {
        Title = "Title",
        Content = "Content text",
        Width = 200,
        Height = 600,
        Background = new SolidColorBrush(Colors.CornflowerBlue),
        Foreground = new SolidColorBrush(Colors.White),
        BorderThickness = new Thickness(1),
        BorderBrush = new SolidColorBrush(Colors.White),
        CloseButtonText = "Close",
    };

    await dialog.ShowAsync();
}

public MainPage()
{
    this.InitializeComponent();
    ShowMessage();
}

这将创建类似于:

输入图片说明

但该内容对话框将作为应用程序的一部分出现,而不是我尝试解决的Windows系统。

在ShowMessage方法之前添加以下行将最大化应用程序到整个屏幕的背景。

ApplicationView.GetForCurrentView().SuppressSystemOverlays = true;
ApplicationView.GetForCurrentView().FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Minimal;
ApplicationView.GetForCurrentView().TryEnterFullScreenMode();

但是在我看来这并不是最好的解决方案。我认为可能会有另一种方法,于是我尝试了WPF。

我创建了一个WPF项目,我的策略是启动MainWindow处于最小化模式,然后弹出内容对话框。因此,与UWP不同,WPF中没有内容对话框,我创建了类似的东西(外观和感觉)。

以下是我的MainWindow中的代码:

private readonly string _title;
private readonly string _message;

public MainWindow()
{
    _title = "Updates are available";
    _message = "Required updates need to be downloaded.";
    InitializeComponent();

    string[]? args = App.Args;
    if (args != null && args.Length > 0)
    {
        _title = args[0];
        _message = args[1];
    }

    MinimizedMainWindow();
    ShowContentDialog();
}

protected void MinimizedMainWindow()
{
    AllowsTransparency = true;
    WindowStyle = WindowStyle.None;
    WindowState = WindowState.Maximized;
    Background = Brushes.Transparent;
    Topmost = true;
}

public void ShowContentDialog()
{
    ContentDialog dialog = new ContentDialog
    {
        Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF0066CC")),
        Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFFFFFFF")),
        WindowStartupLocation = WindowStartupLocation.CenterScreen,
        SizeToContent = SizeToContent.WidthAndHeight,
        WindowStyle = WindowStyle.None,
        Padding = new Thickness(20),
        Margin = new Thickness(0),
        ResizeMode = ResizeMode.NoResize,
        Width = 600,
        Height = 200,
        Title = { Text = _title },
        Message = { Text = _message }
    };

    dialog.Show();
}

这里是我的ContentDialog.xaml文件。

<Window x:Class="NotificationSol.ContentDialog"
        xmlns:local="clr-namespace:NotificationSol"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:av="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        Title="ContentDialog" 
        av:DesignWidth="600" 
        av:DesignHeight="200"
        >
    <Grid>
        <TextBlock x:Name="Title" Margin="30,22,30,118" TextWrapping="Wrap" Text="Title" FontSize="28"/>
        <TextBlock x:Name="Message" Margin="30,70,30,70" TextWrapping="Wrap" Text="Content" FontSize="16"/>
        <Button x:Name="Button" Click="CloseButton_Click" Content="Close" HorizontalAlignment="Right" Margin="0,0,30,20" VerticalAlignment="Bottom" Width="75" Background="#FF0066CC" BorderBrush="White" Foreground="White" Padding="8,4"/>
    </Grid>
</Window>

还有ContentDialog.xaml.cs文件

public ContentDialog()
{
    InitializeComponent();
}

private void CloseButton_Click(object sender, RoutedEventArgs e)
{
    Close();
    base.OnClosed(e);
    Application.Current.Shutdown();
}

为了让我的应用程序接受命令行参数,我对App.xaml.cs进行了以下更改:
public static string[]? Args;
void AppStartup(object sender, StartupEventArgs e)
{
    if (e.Args.Length > 0)
    {
        Args = e.Args;
    }
}

将其作为启动项添加到 App.xaml 中

<Application x:Class="NotificationSol.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:NotificationSol"
             StartupUri="MainWindow.xaml"
             Startup="AppStartup">
    <Application.Resources>
         
    </Application.Resources>
</Application>

从Visual Studio运行此代码,我得到了:

输入图像描述

如果您使用命令行中的2个参数运行软件,则可以传递标题和消息。您有代码,可以创建其他方法操作或具有其他功能的按钮。现在我的按钮只是关闭对话框和应用程序。这里我将两个示例放在了我的存储库中:

https://github.com/maythamfahmi/BlogExamples/tree/master/Stackoverflow/ContentDialog


有点遗憾的是,这个功能没有更容易地内置到Windows 10中。不过还是谢谢您的调查! - Zach Sugano
是的,我同意,而且很高兴你能使用它。祝你晚上愉快☺️。 - Maytham Fahmi

1
你需要使用Winforms和WPF程序集创建一个函数来创建弹出窗口。 功能
Function New-WPFDialog() {
        <#
        .SYNOPSIS
        This neat little function is based on the one from Brian Posey's Article on Powershell GUIs
    
    .DESCRIPTION
      I re-factored a bit to return the resulting XaML Reader and controls as a single, named collection.

    .PARAMETER XamlData
     XamlData - A string containing valid XaML data

    .EXAMPLE

      $MyForm = New-WPFDialog -XamlData $XaMLData
      $MyForm.Exit.Add_Click({...})
      $null = $MyForm.UI.Dispatcher.InvokeAsync{$MyForm.UI.ShowDialog()}.Wait()

    .NOTES
    Place additional notes here.

    .LINK
      http://www.windowsnetworking.com/articles-tutorials/netgeneral/building-powershell-gui-part2.html

    .INPUTS
     XamlData - A string containing valid XaML data

    .OUTPUTS
     a collection of WPF GUI objects.
  #>
    
    Param([Parameter(Mandatory = $True, HelpMessage = 'XaML Data defining a GUI', Position = 1)]
        [string]$XamlData)
    
    # Add WPF and Windows Forms assemblies
    try {
        Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase, system.windows.forms
    }
    catch {
        Throw 'Failed to load Windows Presentation Framework assemblies.'
    }
    
    # Create an XML Object with the XaML data in it
    [xml]$xmlWPF = $XamlData
    
    # Create the XAML reader using a new XML node reader, UI is the only hard-coded object name here
    Set-Variable -Name XaMLReader -Value @{ 'UI' = ([Windows.Markup.XamlReader]::Load((new-object -TypeName System.Xml.XmlNodeReader -ArgumentList $xmlWPF))) }

    # Create hooks to each named object in the XAML reader
    $Elements = $xmlWPF.SelectNodes('//*[@Name]')
    ForEach ( $Element in $Elements ) {
        $VarName = $Element.Name
        $VarValue = $XaMLReader.UI.FindName($Element.Name)
        $XaMLReader.Add($VarName, $VarValue)
    }

    return $XaMLReader
}


Function New-PopUpWindow () {
    param(
        [string]
        $MessageText = "No Message Supplied")

    # This is the XaML that defines the GUI.
    $WPFXamL = @'
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Popup" Background="#FF0066CC" Foreground="#FFFFFFFF" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" SizeToContent="WidthAndHeight" WindowStyle="None" Padding="20" Margin="0">
    <Grid>
        <Button Name="OKButton" Content="OK" HorizontalAlignment="Right" Margin="0,0,30,20" VerticalAlignment="Bottom" Width="75" Background="#FF0066CC" BorderBrush="White" Foreground="White" Padding="8,4"/>
        <TextBlock Name="Message" Margin="100,60,100,80" TextWrapping="Wrap" Text="_CONTENT_" FontSize="36"/>
    </Grid>
</Window>
'@

    # Build Dialog
    $WPFGui = New-WPFDialog -XamlData $WPFXaml
    $WPFGui.Message.Text = $MessageText
    $WPFGui.OKButton.Add_Click( { $WPFGui.UI.Close() })
    $null = $WPFGUI.UI.Dispatcher.InvokeAsync{ $WPFGui.UI.ShowDialog() }.Wait()
}

示例调用和结果

New-PopUpWindow -MessageText "Hey there, I'm a pretty blue form"

example


0

对此也很感兴趣。

虽然我不是一个程序员,但已经完成了一个简单的WinForms C#应用程序。它使用50%透明度的黑色全屏窗体覆盖每个屏幕,然后从主屏幕窗体打开模态(OnShown TopMost)无边框678x165窗体。几乎选择了相同的颜色和字体(Segoe UI标题+Calibri文本和粗按钮),因此非常简单且类似伪造。我还添加了第二个红色按钮,具有某些特殊功能,这当然是通过标准API不可能实现的。

另一种方法是知道可以使用来自RemoteDesktop模块的Send-RDUserMessage PowerShell cmdlet调用这样的消息。我打开了模块源代码并理解了它使用wtsapi32.dll。我停止推动该方向,因为:

1. 远程桌面模块需要管理员权限,可能是因为WTSAPI调用通常也需要;但我想要从任务计划程序中以用户上下文启动横幅。 2. 在Windows 10桌面操作系统上运行Send-RDUserMessage会产生类似于小型"msg.exe"(而不是sessionmsg.exe)的消息框,但我也想让这个横幅在Windows 10工作站上出现,而不仅仅是终端服务器。但工作站使用相同的横幅来显示更新和激活信息,因此它显然不是服务器独有的功能。 3. 我对编码一点都不懂。有人可以试着使用那个dll,也许是documented at learn.microsoft.com的WTSSendMessageA 和 WTSSendMessageW方法。也许它调用另一个API,因为这样的消息不仅仅是为了RD/TS目的。但我已经厌倦了处理它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接