WPF任务栏图标右键菜单

3
我正在尝试创建一个带有TaskbarIcon的WPF应用程序。 我假设如果我点击托盘栏中的图标,它将弹出一个上下文菜单, 如果我选择“退出”,那么它将显示一个消息框,询问我是否要关闭此应用程序。
问题在于,消息框会正确显示,但在我点击任何按钮之前就立即消失了。我使用调试器检查“Result”值时发现它始终为“No”。有人遇到过这个问题吗?任何线索都将不胜感激!
以下是我的.xaml代码:
<tb:TaskbarIcon x:Name="WpfTaskIcon" IconSource="/Themes/Images/TimeSync.ico"
                    ToolTipText="Hello world" >
<tb:TaskbarIcon.ContextMenu>
<ContextMenu Background="LightCoral">
    <MenuItem Header="Exit" Click="Exit_Click" />
    <MenuItem Header="Second menu Item" />
</ContextMenu>
</tb:TaskbarIcon.ContextMenu>

这是我的C#代码:
private void Exit_Click(object sender, RoutedEventArgs e)
{
     MessageBoxResult result = System.Windows.MessageBox.Show(
       "Message_ConfirmationOfExit",
        "Title_Confirmation",
        MessageBoxButton.YesNo);
    if (result == MessageBoxResult.Yes)
    {
        this.Close();
    }
}

edt: 我已添加以下代码来初始化MainWindow的可见性:

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
    this.Visibility = System.Windows.Visibility.Visible;
    MessageBox.Show("MainWindow loaded");
    MessageBoxResult result = System.Windows.MessageBox.Show(
       "Message_ConfirmationOfExit",
        "Title_Confirmation",
        MessageBoxButton.YesNo);
    if (result == MessageBoxResult.Yes)
    {
        this.Close();
    }
}
2个回答

1
这句话的意思是:“可能来得有点晚,但我可能已经找到了这个问题的原因:MessageBox使用了WindowAPI。参考:http://pinvoke.net/default.aspx/user32/MessageBox.html。”
[DllImport("user32.dll", SetLastError = true, CharSet= CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

参数包括类型为IntPtr的hWnd。
当在WPF中调用MessageBox.Show()时,MessageBox类内部调用ShowCore()方法来处理此参数。
参考:https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/MessageBox.cs 第483行。
...


if ( (options & (MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly)) ! = 0) 
{
    // Demand UnmangedCode permissions if using ServiceNotification/DefaultDesktopOnly.
    // Details in DevDiv 163043.
    SecurityHelper.DemandUnmanagedCode()
    if (owner ! = IntPtr.Zero)
    {
        throw new ArgumentException(SR.Get(SRID.CantShowMBServiceWithOwner));
    }                
}
else
{                                    
    if (owner == IntPtr.Zero)
    {
        owner = UnsafeNativeMethods.GetActiveWindow();
    }                
}

...

我认为,在 WPF 中使用 MessageBox 时,当前活动窗口会自动被选择为 hWnd,由于没有窗口,所以会使用 ContextMenu 作为窗口。
我们可以使用 API 来证明这一点:
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();

在点击事件中使用它:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    var t = GetActiveWindow();
}

然后在Spy ++中找到ContextMenu中的值。

当单击ContextMenu上的MenuItem时,ContextMenu将被关闭,此时MessageBox的所有者消失,MessageBox被强制关闭。

基于以上研究,我得出了以下解决方案。

1.延迟出现MessageBox,以便在ContextMenu关闭后显示。

await Task.Delay(200); 
MessageBox.Show("Hello, world!");

2. 调用API本身,并将hWnd设置为IntPtr.Zero。
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

并且

private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    MessageBox(IntPtr.Zero, "Hello, world!", "Test", 0);
}

3. 根据WPF中MessageBox的源代码修改MessageBoxOptions的值。
MessageBox.Show(
    "Hello, world!",
    "Test",
    MessageBoxButton.OK,
    MessageBoxImage.Error,
    MessageBoxResult.OK,
    MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly);

也许这可以为大家提供一些帮助。

==================

我认为这种方式更好:
在 App.xaml.cs 中添加一个变量:
bool isNeedShowMessageBox = false;

当菜单项被按下时:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
    isNeedShowMessageBox = true;
}

添加右键菜单事件:
<ContextMenu Closed="ContextMenu_Closed">

然后:

private void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
    if (isNeedShowMessageBox)
    {
        MessageBox.Show();
        isNeedShowMessageBox=false;
    }
}

这似乎工作得很好。
在后续的尝试中,我发现不仅是MessageBox,而且OpenFileDialog或其他类似的窗口也会被强制关闭,但如果以这种方式处理,问题就应该避免。

0

我在其他WPF场景中遇到了这个问题,原因是尚未启动主UI线程。如果在应用程序的第一个窗体加载之前调用MessageBox()MsgBox()或VB InputBox(),预期的对话框会打开,但会在一秒钟内消失。然而,它会自己创建缺少的UI线程(我想...我没有检查所有细节),因为在重复调用相同代码时,消息框不会再消失。

解决方案是在打开任何消息框之前让第一个窗体初始化(通常是主窗体)。隐形初始化也算。

检查清单问题:

  • 应用程序的任何窗体是否已加载(其Load事件是否已调用)?

解决方法(不太好看但有效):如果您没有时间对应用程序进行更大的更改,在显示第一个消息框之前存储当前时间,如果您在1500毫秒内收到回复,则不要根据其结果采取行动(它不是来自用户的),而是重新显示消息框。


是的!感谢您的建议和提示。但是,我已经在“MainWindow_Loaded”中添加了初始化MainWindow可见性的段落,然后再调用“Exit_Click”,并且我更新的代码显示在主要部分中。虽然提示和确认消息框都会正确显示,但“Exit_Click”中的确认框仍然消失... - Alanight
@Alanight - 你能试一下另一个方法吗?将消息框代码放入“_Shown”而不是“_Loaded”中。 - miroxlav
好的!非常感谢!我会尝试解决这个烦人的问题!非常感谢您的帮助和建议! - Alanight
嗨!miroxlav,在MainWindow_Loaded方法中,我让线程在消息框弹出后睡眠1,500毫秒,但问题仍然存在。 - Alanight
@Alanight - 这个想法是:(1)保存当前时间(2)显示消息框(3)如果您从消息框中收到回复,并且这是自应用启动以来的第一个消息框,请检查时间差。如果小于1500毫秒,则再次显示消息框。后续显示的消息框不会有此问题。 - miroxlav
显示剩余4条评论

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