2017-08-15 64 views
0

我有一個WPF應用程序,使用winforms NotifyIcon在托盤上顯示上下文菜單。當我執行以下步驟時,圖標消失。在托盤上的通知圖標什麼導致我的NotifyIcon在Alt + F4之後隱藏?

  1. 右鍵
  2. 選擇顯示一個模式對話框
  3. 上下文菜單項辭退該對話框
  4. 按Alt + F4

這裏是一個最小的例子,我看到這個錯誤。

XAML:

<Window x:Class="killtrayicon.MainWindow" 
    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:local="clr-namespace:killtrayicon" 
    mc:Ignorable="d" 
    Title="MainWindow" Height="350" Width="525"> 
<Grid> 
    <Button Content="button" Click="Button_Click"/> 
</Grid> 
</Window> 

後面的代碼:

namespace killtrayicon 
{ 
    using System.Windows; 

    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     private System.Windows.Forms.NotifyIcon notifyIcon = new System.Windows.Forms.NotifyIcon(); 

     public MainWindow() 
     { 
      InitializeComponent(); 

      notifyIcon.Icon = Properties.Resources.icon; 
      notifyIcon.Visible = true; 
      notifyIcon.Text = "test"; 
      notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(); 
      notifyIcon.ContextMenu.MenuItems.Add("click", (s, e) => 
      { 
       MessageBox.Show("menu"); 
      }); 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      notifyIcon.Icon = Properties.Resources.icon; 
     } 
    } 
} 

用鼠標點擊主窗口中的重置按鈕的圖標和通知圖標會再次出現。所以通知圖標本身並未被刪除。檢查NotifyIcon的實例顯示它在重置圖標前仍然可見,並且Icon屬性指向我資源中的有效ICO。

我懷疑上下文菜單是問題,因爲如果我通過單擊托盤圖標顯示模式對話框,則不會發生此問題。

如何獲取NotifyIcon不響應Alt + F4?

編輯:此問題與重複,但該問題沒有示例代碼來重現問題(無效鏈接),提交給Microsoft的問題鏈接也是一個死鏈接,並且沒有可接受的答案實際解決方案

+0

Wow a downvote for a better worded question without dead link? – cppguy

回答

1

我發現瞭解決方案。 NotifyIcon創建一個隱藏NativeWindow作爲由Shell_NotifyIcon創建的圖標生成的窗口消息的收件人。該窗口正在使用默認的窗口過程,它像其他窗口一樣處理Alt+F4。把它變成WM_CLOSE。您需要使用Win32 API對HWND進行子類化,NativeWindow,攔截WM_CLOSE,並忽略它。

首先添加一些Win32的方法從COMCTL32.DLL:

public static class Comctl32 
{ 
    public const string DLL = "comctl32.dll"; 

    public const uint WM_CLOSE = 0x0010; 
    public const uint WM_NCDESTROY = 0x0082; 

    public delegate IntPtr SubclassWndProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData); 

    [DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    public static extern bool SetWindowSubclass(
     [param: In] 
      IntPtr hWnd, 
     [param: In] 
      SubclassWndProc pfnSubclass, 
     [param: In] 
      UIntPtr uIdSubclass, 
     [param: In] 
      UIntPtr dwRefData); 

    [DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    public static extern bool RemoveWindowSubclass(
     [param: In] 
      IntPtr hWnd, 
     [param: In] 
      SubclassWndProc pfnSubclass, 
     [param: In] 
      UIntPtr uIdSubclass); 

    [DllImport(DLL, CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)] 
    public static extern IntPtr DefSubclassProc(
     [param: In] 
      IntPtr hWnd, 
     [param: In, MarshalAs(UnmanagedType.U4)] 
      uint uMsg, 
     [param: In] 
      UIntPtr WPARAM, 
     [param: In] 
      UIntPtr LPARAM); 
} 

然後將代碼添加到您的NotifyIcon子類隱藏的托盤圖標的窗口。你需要使用反射來獲取窗口,因爲它不是公開:

private Native.Comctl32.SubclassWndProc subclassWndProc; 

... 

// Get the HWND from the notify icon 
Type notifyIconType = typeof(System.Windows.Forms.NotifyIcon); 
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance; 
var window = notifyIconType.GetField("window", hidden).GetValue(this.notifyIcon) as System.Windows.Forms.NativeWindow; 

// Inject our window proc to intercept window messages 
this.subclassWndProc = this.TrayWndProc; 
Native.Comctl32.SetWindowSubclass(window.Handle, this.subclassWndProc, UIntPtr.Zero, UIntPtr.Zero); 

然後攔截WM_CLOSE忽略Alt+F4。我們還確保在未分類上WM_NCDESTROY

private IntPtr TrayWndProc(IntPtr hWnd, uint uMsg, UIntPtr wParam, UIntPtr lParam, UIntPtr uIdSubclass, UIntPtr dwRefData) 
{ 
    switch (uMsg) 
    { 
     // Ignore the close message to avoid Alt+F4 killing the tray icon 
     case Native.Comctl32.WM_CLOSE: 
      return IntPtr.Zero; 

     // Clean up subclassing 
     case Native.Comctl32.WM_NCDESTROY: 
      Native.Comctl32.RemoveWindowSubclass(hWnd, this.subclassWndProc, UIntPtr.Zero)) 
      break; 
    } 

    // Invoke the default window proc 
    return Native.Comctl32.DefSubclassProc(hWnd, uMsg, wParam, lParam); 
}