2017-08-15 2 views
0

J'ai une application WPF en utilisant une winforms NotifyIcon pour afficher un menu contextuel sur le plateau. Lorsque j'effectue les étapes suivantes, l'icône disparaît.Qu'est-ce qui cause mon NotifyIcon à se cacher après Alt + F4?

  1. Faites un clic droit sur l'icône Notify dans la barre
  2. Sélectionnez un élément de menu contextuel qui affiche une boîte de dialogue modale
  3. Rejeter cette boîte de dialogue
  4. Appuyez sur Alt + F4

Voici une exemple minimal où je vois ce bug.

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> 
code

derrière:

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; 
     } 
    } 
} 

En cliquant sur le bouton dans ma fenêtre principale remet à zéro l'icône et l'icône Notify apparaît à nouveau. L'icône de notification n'a donc pas été supprimée. L'inspection de l'instance de NotifyIcon indique qu'elle est toujours visible avant la réinitialisation de l'icône et que la propriété Icon pointe vers un ICO valide dans mes ressources.

Je suppose que le menu contextuel est le problème parce que si je montre une boîte de dialogue modale en cliquant sur l'icône de la barre d'état, ce problème ne se produit pas.

Comment faire pour que NotifyIcon ne réponde pas à Alt + F4?

Modifier: Cette question est un double de mais cette question n'a pas d'exemple de code pour reproduire le problème (lien mort), le lien au problème soumis à Microsoft est aussi un lien mort et il n'y a pas de réponse acceptée avec un solution réelle.

+0

Wow un downvote pour une question mieux formulée sans lien mort? – cppguy

Répondre

1

J'ai découvert la solution. Le NotifyIcon crée un NativeWindow masqué en tant que destinataire des messages de fenêtre générés par l'icône créée par Shell_NotifyIcon. Cette fenêtre utilise la fenêtre par défaut proc qui gère Alt+F4 comme toute autre fenêtre. En le transformant en WM_CLOSE. Vous devez utiliser l'API Win32 pour sous-classer le HWND dans celui NativeWindow, intercepter le WM_CLOSE et l'ignorer.

d'abord ajouter quelques méthodes de 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); 
} 

Puis ajouter du code à votre NotifyIcon sous-classe la fenêtre icône de la barre cachée. Vous devrez utiliser la réflexion pour obtenir à la fenêtre, car ce n'est pas publique:

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); 

ensuite intercepter le WM_CLOSE à ignorer Alt+F4. Nous veillons également à ce à un sous-classe sur 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); 
}