读完问题后,我觉得很有趣,决定自己探究一下可能性并分享我的答案。
由于您标记了问题为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();
}
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
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"
对此也很感兴趣。
虽然我不是一个程序员,但已经完成了一个简单的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目的。但我已经厌倦了处理它。
[Windows.UI.PopUps.MessageDialog]
,但我认为无法从PowerShell中调用。最好的方法是使用XAML样式化窗口,使其看起来类似,这远非一行命令所能完成。这里没有快速解决方案。即使在C#中,您也需要在特定的UWP项目中完成此操作。一些[Windows.UI]
的东西确实有效,比如你会在屏幕右下角收到的通知。 - Ash