I'm trying to create an WPF application with TaskbarIcon, I suppose if I click the icon in the traybar, it will popup a Contextmenu, and if I select "Exit", then it will show a messagebox asking me whether I want to close this app or not.
Here's the problem, the MessageBox show correctly, but it just disappear IMMEDIATELY BEFORE I click ANY button, and I use debugger to check the "Result" value, I found it's always "No". Does any encounter this problem before? Any single clue would be appreciated!!
Here's my .xaml code:
<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>
and Here's my c# code:
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 : I've add this to initialize the visibility of 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();
}
}
It may come a little too late, but I may have figured out the cause of this problem:
The MessageBox use a WindowAPI.
Reference: 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);
The parameters include the hWnd of type IntPtr.
When MessageBox.Show() is called in WPF, the ShowCore() method is called inside the MessageBox class, which internally handles this parameter.
Reference: https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/MessageBox.cs at line 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();
}
}
...
I think, when using MessageBox in WPF, the currently active window is automatically selected as the hWnd, and since there is no window, the ContextMenu is used as the window.
We can prove this using the API:
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
And use it in click event:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var t = GetActiveWindow();
}
Then find the t value in spy++, is the ContextMenu.
When the MenuItem on the ContextMenu is clicked, the ContextMenu will be closed, at that time the owner of MessageBox disappears and MessageBox is forced to be closed.
Based on the above research, I got the following solutions.
1.Delay the appearance of the MessageBox so that it is displayed after the ContextMenu is closed.
await Task.Delay(200);
MessageBox.Show("Hello, world!");
2.Call the API itself and set hWnd to IntPtr.Zero.
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);
and
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MessageBox(IntPtr.Zero, "Hello, world!", "Test", 0);
}
3.Modify the value of MessageBoxOptions according to the source code of MessageBox in WPF.
MessageBox.Show(
"Hello, world!",
"Test",
MessageBoxButton.OK,
MessageBoxImage.Error,
MessageBoxResult.OK,
MessageBoxOptions.ServiceNotification | MessageBoxOptions.DefaultDesktopOnly);
Maybe this can provide some help for everyone.
==================
I think this way is better:
Add a variable to App.xaml.cs:
bool isNeedShowMessageBox = false;
When the MenuItem is pressed:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
isNeedShowMessageBox = true;
}
Add event for ContextMenu:
<ContextMenu Closed="ContextMenu_Closed">
Then:
private void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
if (isNeedShowMessageBox)
{
MessageBox.Show();
isNeedShowMessageBox=false;
}
}
This seems to be working just fine.
In subsequent attempts I found that not only MessageBox, but also OpenFileDialog or other similar windows would have the same forced closure, but if handled in this way, the problem should be avoided.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With