2011-01-06 21 views
33

मैं अपने आवेदन में मेनू आइटम के बारे में पुराना उम्र जोड़ना चाहता हूं। मैं इसे एप्लिकेशन के 'सिस्टम मेनू' में जोड़ना चाहता हूं (वह तब होता है जब हम ऊपर-बाएं कोने में एप्लिकेशन आइकन पर क्लिक करते हैं)। तो, मैं इसे .NET में कैसे कर सकता हूं?मैं विंडोज फॉर्म के सिस्टम मेनू को कैसे अनुकूलित कर सकता हूं?

उत्तर

76

विंडोज GetSystemMenu function के साथ अनुकूलन उद्देश्यों के लिए फ़ॉर्म के सिस्टम मेनू की एक प्रतिलिपि को संभालने में काफी आसान बनाता है। कठिन हिस्सा यह है कि AppendMenu, InsertMenu, और DeleteMenu जैसे फ़ंक्शंस का उपयोग करके, आपके द्वारा लौटाए गए मेनू में उचित संशोधन करने के लिए आप स्वयं ही हैं, जैसे कि आप Win32 API के विरुद्ध सीधे प्रोग्रामिंग कर रहे थे।

हालांकि, यदि आप बस इतना करना चाहते हैं कि एक साधारण मेनू आइटम जोड़ें, तो यह वास्तव में इतना मुश्किल नहीं है। उदाहरण के लिए, आपको केवल AppendMenu फ़ंक्शन का उपयोग करने की आवश्यकता होगी क्योंकि आप जो करना चाहते हैं वह मेनू के अंत में एक आइटम या दो जोड़ना है। कुछ और उन्नत करना (जैसे मेनू के बीच में कोई आइटम डालना, मेनू आइटम पर बिटमैप प्रदर्शित करना, मेनू आइटम चेक करना, डिफ़ॉल्ट मेनू आइटम सेट करना आदि) को थोड़ा और काम करने की आवश्यकता है। लेकिन एक बार जब आप जानते हैं कि यह कैसे किया जाता है, तो आप जंगली जा सकते हैं। documentation on menu-related functions सभी बताता है।

using System; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

public class CustomForm : Form 
{ 
    // P/Invoke constants 
    private const int WM_SYSCOMMAND = 0x112; 
    private const int MF_STRING = 0x0; 
    private const int MF_SEPARATOR = 0x800; 

    // P/Invoke declarations 
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool InsertMenu(IntPtr hMenu, int uPosition, int uFlags, int uIDNewItem, string lpNewItem); 


    // ID for the About item on the system menu 
    private int SYSMENU_ABOUT_ID = 0x1; 

    public CustomForm() 
    { 
    } 

    protected override void OnHandleCreated(EventArgs e) 
    { 
     base.OnHandleCreated(e); 

     // Get a handle to a copy of this form's system (window) menu 
     IntPtr hSysMenu = GetSystemMenu(this.Handle, false); 

     // Add a separator 
     AppendMenu(hSysMenu, MF_SEPARATOR, 0, string.Empty); 

     // Add the About menu item 
     AppendMenu(hSysMenu, MF_STRING, SYSMENU_ABOUT_ID, "&About…"); 
    } 

    protected override void WndProc(ref Message m) 
    { 
     base.WndProc(ref m); 

     // Test if the About item was selected from the system menu 
     if ((m.Msg == WM_SYSCOMMAND) && ((int)m.WParam == SYSMENU_ABOUT_ID)) 
     { 
      MessageBox.Show("Custom About Dialog"); 
     } 

    } 
} 

और यहाँ क्या तैयार उत्पाद है:

यहाँ है कि एक विभाजक रेखा और एक (एक खिड़की मेनू भी कहा जाता है) "के बारे में" अपने सिस्टम मेनू के नीचे करने के लिए आइटम कहते हैं एक फार्म के लिए पूरा कोड है ऐसा लगता है कि:

    Form with custom system menu

+0

परीक्षण किया गया है और यह बहुत अच्छी तरह से काम करता है! आपने मेरा दिन बचा लिया! धन्यवाद! :) – GiveEmTheBoot

+0

यह अच्छी तरह से काम करता है। लेकिन, मैं प्रविष्टि के लिए एक्सेलेरेटर कुंजी कैसे प्रदर्शित कर सकता हूं? (जैसे बंद करें Alt + F4 है, मैं Alt + A को इसके बारे में दिखाना चाहता हूं।) – BoltBait

+0

कभी नहीं, मुझे मेरी टिप्पणी का उत्तर मिला। आप बस अलग करने के लिए एक टैब वर्ण (\ t) का उपयोग करके "& about ... \ tAlt + A" जैसे स्ट्रिंग को जोड़ते हैं। – BoltBait

5

मूल्य-जोड़ आपको पिनवोक की आवश्यकता के लिए अपेक्षाकृत छोटा है। लेकिन यह संभव है। सिस्टम मेनू हैंडल को पुनर्प्राप्त करने के लिए GetSystemMenu() का उपयोग करें। फिर प्रविष्टि जोड़ने के लिए InsertMenuItem। आपको इसे ऑनहैंडलक्रेटेड() के ओवरराइड में करना है ताकि विंडो को फिर से बनाया जाने पर आप मेनू को फिर से बना सकें।

WM_SYSCOMMAND संदेश को पहचानने के लिए ओवरराइड करें जो उपयोगकर्ता द्वारा क्लिक किए जाने पर उत्पन्न होता है। आपको पिनवोक घोषणाओं के लिए पिनवोक.net पर जाएं।

6

मैं आगे कोड़ी ग्रे की समाधान एक कदम उठाया और इसे से बाहर एक पुन: प्रयोज्य वर्ग बनाया है। यह मेरे एप्लिकेशन लॉग सबमिट उपकरण का हिस्सा है जो इसे सिस्टम मेनू में जानकारी के बारे में छिपाना चाहिए।

https://github.com/ygoe/FieldLog/blob/master/LogSubmit/Unclassified/UI/SystemMenu.cs

यह आसानी से इस तरह इस्तेमाल किया जा सकता:

class MainForm : Form 
{ 
    private SystemMenu systemMenu; 

    public MainForm() 
    { 
     InitializeComponent(); 

     // Create instance and connect it with the Form 
     systemMenu = new SystemMenu(this); 

     // Define commands and handler methods 
     // (Deferred until HandleCreated if it's too early) 
     // IDs are counted internally, separator is optional 
     systemMenu.AddCommand("&About…", OnSysMenuAbout, true); 
    } 

    protected override void WndProc(ref Message msg) 
    { 
     base.WndProc(ref msg); 

     // Let it know all messages so it can handle WM_SYSCOMMAND 
     // (This method is inlined) 
     systemMenu.HandleMessage(ref msg); 
    } 

    // Handle menu command click 
    private void OnSysMenuAbout() 
    { 
     MessageBox.Show("My about message"); 
    } 
} 
+0

मैं आपकी तकनीक का प्रयास करना चाहता था लेकिन मुझे SystemMenu का संदर्भ नहीं मिला। यह कहां है? – John

+0

@ जॉन यह लिंक [गिटहब रिपोजिटरी] पर श्रेणी है (https://github.com/dg9ngf/FieldLog/blob /master/LogSubmit/Unclassified/UI/SystemMenu.cs) जो पी/Invoke – Yoda

+0

@ जॉन ओप्स के साथ उल्लिखित कार्यों और सौदों को प्रदान करता है, क्या मुझे उस टिप्पणी को याद आया ... मैंने इसे और अधिक भविष्य बनाने के लिए लिंक भी अपडेट किया है- सबूत। – ygoe

3

मैं जानता हूँ कि क्या यह उत्तर पुराना है, लेकिन मैं वास्तव में LonelyPixel के जवाब पसंद आया। हालांकि, इसे WPF के साथ सही तरीके से काम करने के लिए कुछ काम की आवश्यकता थी। नीचे मैंने लिखा एक डब्ल्यूपीएफ संस्करण है, तो आपको :) नहीं है। स्वीकार किए जाते हैं जवाब का

/// <summary> 
/// Extends the system menu of a window with additional commands. 
/// Adapted from: 
/// https://github.com/dg9ngf/FieldLog/blob/master/LogSubmit/Unclassified/UI/SystemMenu.cs 
/// </summary> 
public class SystemMenuExtension 
{ 
    #region Native methods 

    private const int WM_SYSCOMMAND = 0x112; 
    private const int MF_STRING = 0x0; 
    private const int MF_SEPARATOR = 0x800; 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); 

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool AppendMenu(IntPtr hMenu, int uFlags, int uIDNewItem, string lpNewItem); 

    #endregion Native methods 

    #region Private data 
    private Window window; 
    private IntPtr hSysMenu; 
    private int lastId = 0; 
    private List<Action> actions = new List<Action>(); 
    private List<CommandInfo> pendingCommands; 

    #endregion Private data 

    #region Constructors 

    /// <summary> 
    /// Initialises a new instance of the <see cref="SystemMenu"/> class for the specified 
    /// <see cref="Form"/>. 
    /// </summary> 
    /// <param name="window">The window for which the system menu is expanded.</param> 
    public SystemMenuExtension(Window window) 
    { 
     this.window = window; 
     if(this.window.IsLoaded) 
     { 
      WindowLoaded(null, null); 
     } 
     else 
     { 
      this.window.Loaded += WindowLoaded; 
     } 
    } 

    #endregion Constructors 

    #region Public methods 

    /// <summary> 
    /// Adds a command to the system menu. 
    /// </summary> 
    /// <param name="text">The displayed command text.</param> 
    /// <param name="action">The action that is executed when the user clicks on the command.</param> 
    /// <param name="separatorBeforeCommand">Indicates whether a separator is inserted before the command.</param> 
    public void AddCommand(string text, Action action, bool separatorBeforeCommand) 
    { 
     int id = ++this.lastId; 
     if (!this.window.IsLoaded) 
     { 
      // The window is not yet created, queue the command for later addition 
      if (this.pendingCommands == null) 
      { 
       this.pendingCommands = new List<CommandInfo>(); 
      } 
      this.pendingCommands.Add(new CommandInfo 
      { 
       Id = id, 
       Text = text, 
       Action = action, 
       Separator = separatorBeforeCommand 
      }); 
     } 
     else 
     { 
      // The form is created, add the command now 
      if (separatorBeforeCommand) 
      { 
       AppendMenu(this.hSysMenu, MF_SEPARATOR, 0, ""); 
      } 
      AppendMenu(this.hSysMenu, MF_STRING, id, text); 
     } 
     this.actions.Add(action); 
    } 

    #endregion Public methods 

    #region Private methods 

    private void WindowLoaded(object sender, RoutedEventArgs e) 
    { 
     var interop = new WindowInteropHelper(this.window); 
     HwndSource source = PresentationSource.FromVisual(this.window) as HwndSource; 
     source.AddHook(WndProc); 

     this.hSysMenu = GetSystemMenu(interop.EnsureHandle(), false); 

     // Add all queued commands now 
     if (this.pendingCommands != null) 
     { 
      foreach (CommandInfo command in this.pendingCommands) 
      { 
       if (command.Separator) 
       { 
        AppendMenu(this.hSysMenu, MF_SEPARATOR, 0, ""); 
       } 
       AppendMenu(this.hSysMenu, MF_STRING, command.Id, command.Text); 
      } 
      this.pendingCommands = null; 
     } 
    } 

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
    { 
     if (msg == WM_SYSCOMMAND) 
     { 
      if ((long)wParam > 0 && (long)wParam <= lastId) 
      { 
       this.actions[(int)wParam - 1](); 
      } 
     } 

     return IntPtr.Zero; 
    } 

    #endregion Private methods 

    #region Classes 

    private class CommandInfo 
    { 
     public int Id { get; set; } 
     public string Text { get; set; } 
     public Action Action { get; set; } 
     public bool Separator { get; set; } 
    } 

    #endregion Classes 
1

VB.NET संस्करण:

Imports System.Windows.Forms 
Imports System.Runtime.InteropServices 

Public Class CustomForm 
    Inherits Form 
    ' P/Invoke constants 
    Private Const WM_SYSCOMMAND As Integer = &H112 
    Private Const MF_STRING As Integer = &H0 
    Private Const MF_SEPARATOR As Integer = &H800 

    ' P/Invoke declarations 
    <DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _ 
    Private Shared Function GetSystemMenu(hWnd As IntPtr, bRevert As Boolean) As IntPtr 
    End Function 

    <DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _ 
    Private Shared Function AppendMenu(hMenu As IntPtr, uFlags As Integer, uIDNewItem As Integer, lpNewItem As String) As Boolean 
    End Function 

    <DllImport("user32.dll", CharSet := CharSet.Auto, SetLastError := True)> _ 
    Private Shared Function InsertMenu(hMenu As IntPtr, uPosition As Integer, uFlags As Integer, uIDNewItem As Integer, lpNewItem As String) As Boolean 
    End Function 


    ' ID for the About item on the system menu 
    Private SYSMENU_ABOUT_ID As Integer = &H1 

    Public Sub New() 
    End Sub 

    Protected Overrides Sub OnHandleCreated(e As EventArgs) 
     MyBase.OnHandleCreated(e) 

     ' Get a handle to a copy of this form's system (window) menu 
     Dim hSysMenu As IntPtr = GetSystemMenu(Me.Handle, False) 

     ' Add a separator 
     AppendMenu(hSysMenu, MF_SEPARATOR, 0, String.Empty) 

     ' Add the About menu item 
     AppendMenu(hSysMenu, MF_STRING, SYSMENU_ABOUT_ID, "&About…") 
    End Sub 

    Protected Overrides Sub WndProc(ByRef m As Message) 
     MyBase.WndProc(m) 

     ' Test if the About item was selected from the system menu 
     If (m.Msg = WM_SYSCOMMAND) AndAlso (CInt(m.WParam) = SYSMENU_ABOUT_ID) Then 
      MessageBox.Show("Custom About Dialog") 
     End If 

    End Sub 
End Class 
संबंधित मुद्दे