Microsoft Most Valuable Professional

Chris Pietschmann

An MVP From Wisconsin

Add System Menu Items to WPF Window using Win32 API

A couple weeks ago, I blog how to "Add System Menu Items to a Form using Windows API". Being that WPF has it's differences from Windows Forms, I began to wonder how simple or difficult this would be to do within a WPF application. As it turns out, there are a couple of extra things you need to do, but the code is pretty much the same.

This is something I used to have a code snippet for in VB6, but I haven't needed to do this in a while so I never wrote up an example in .NET. So, here's the simple code necessary to add some custom menu items to your applications System Menu within a WPF application. If you're not familiar, the System Menu is the menu that shows up if you click on the apps icon in the upper left or if you right click the app in the taskbar.

Below is a screenshot of it in action along with the complete source code in C#.

using System;
using System.Windows;
using System.Runtime.InteropServices;
using System.Windows.Interop;

namespace WpfApplication3
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        #region Win32 API Stuff

        // Define the Win32 API methods we are going to use
        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);

        /// Define our Constants we will use
        public const Int32 WM_SYSCOMMAND = 0x112;
        public const Int32 MF_SEPARATOR = 0x800;
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_STRING = 0x0;

        #endregion

        // The constants we'll use to identify our custom system menu items
        public const Int32 _SettingsSysMenuID = 1000;
        public const Int32 _AboutSysMenuID = 1001;

        /// <summary>
        /// This is the Win32 Interop Handle for this Window
        /// </summary>
        public IntPtr Handle
        {
            get
            {
                return new WindowInteropHelper(this).Handle;
            }
        }

        public Window1()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            /// Get the Handle for the Forms System Menu
            IntPtr systemMenuHandle = GetSystemMenu(this.Handle, false);

            /// Create our new System Menu items just before the Close menu item
            InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator
            InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _SettingsSysMenuID, "Settings...");
            InsertMenu(systemMenuHandle, 7, MF_BYPOSITION, _AboutSysMenuID, "About...");

            // Attach our WndProc handler to this Window
            HwndSource source = HwndSource.FromHwnd(this.Handle);
            source.AddHook(new HwndSourceHook(WndProc));
        }

        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Check if a System Command has been executed
            if (msg == WM_SYSCOMMAND)
            {
                // Execute the appropriate code for the System Menu item that was clicked
                switch (wParam.ToInt32())
                {
                    case _SettingsSysMenuID:
                        MessageBox.Show("\"Settings\" was clicked");
                        handled = true;
                        break;
                    case _AboutSysMenuID:
                        MessageBox.Show("\"About\" was clicked");
                        handled = true;
                        break;
                }
            }

            return IntPtr.Zero;
        }
    }
}

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: General | Win32API
Posted by crpietschmann on Thursday, March 27, 2008 4:41 PM
Permalink | Comments (0) | Post RSSRSS comment feed


Add System Menu Items to a Form using Windows API

This is something I used to have a code snippet for in VB6, but I haven't needed to do this in a while so I never wrote up an example in .NET. So, here's the simple code necessary to add some custom menu items to your applications System Menu. If you're not familiar, the System Menu is the menu that shows up if you click on the apps icon in the upper left or if you right click the app in the taskbar.

Below is a screenshot of it in action along with the complete source code in C#.

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

namespace WinFormsSystemMenuTest
{
    public partial class Form1 : Form
    {
        #region Win32 API Stuff

        // Define the Win32 API methods we are going to use
        [DllImport("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem, string lpNewItem);

        /// Define our Constants we will use
        public const Int32 WM_SYSCOMMAND = 0x112;
        public const Int32 MF_SEPARATOR = 0x800;
        public const Int32 MF_BYPOSITION = 0x400;
        public const Int32 MF_STRING = 0x0;
       
        #endregion
       
        // The constants we'll use to identify our custom system menu items
        public const Int32 _SettingsSysMenuID = 1000;
        public const Int32 _AboutSysMenuID = 1001;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            /// Get the Handle for the Forms System Menu
            IntPtr systemMenuHandle = GetSystemMenu(this.Handle, false);

            /// Create our new System Menu items just before the Close menu item
            InsertMenu(systemMenuHandle, 5, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty); // <-- Add a menu seperator
            InsertMenu(systemMenuHandle, 6, MF_BYPOSITION, _SettingsSysMenuID, "Settings...");
            InsertMenu(systemMenuHandle, 7, MF_BYPOSITION, _AboutSysMenuID, "About...");
        }

        protected override void WndProc(ref Message m)
        {
            // Check if a System Command has been executed
            if (m.Msg == WM_SYSCOMMAND)
            {
                // Execute the appropriate code for the System Menu item that was clicked
                switch (m.WParam.ToInt32())
                {
                    case _SettingsSysMenuID:
                        MessageBox.Show("\"Settings\" was clicked");
                        break;
                    case _AboutSysMenuID:
                        MessageBox.Show("\"About\" was clicked");
                        break;
                }
            }
           
            base.WndProc(ref m);
        }
    }
}

Here's a link on how to "Add System Menu Items to WPF Window using Win32 API"

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: General | Win32API
Posted by crpietschmann on Tuesday, March 04, 2008 7:34 PM
Permalink | Comments (1) | Post RSSRSS comment feed


VB.NET: Syntax Highlighting in a RichTextBox control

This last weekend I expirimented a little bit with extending the functionality of the RichTextBox control. Below you'll find an example of a small class that enherits from the RichTextBox control and allows you to implement syntax highlighting (with the use of a couple Win32 API call to smooth over the process of course.) The code pretty much speaks for itself.

Public Class SyntaxRTB

   Inherits System.Windows.Forms.RichTextBox

   Private
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
      
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

 

   Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Integer) As Integer

   Private _SyntaxHighlight_CaseSensitive As Boolean = False

   Private Words As New DataTable

   'Contains Windows Messages for the SendMessage API call
   Private Enum EditMessages
      LineIndex = 187
      LineFromChar = 201
      GetFirstVisibleLine = 206
      CharFromPos = 215
      PosFromChar = 1062
   End Enum

   Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
      
ColorVisibleLines()
   
End Sub

   Public Sub ColorRtb()
      
Dim FirstVisibleChar As Integer
      
Dim i As Integer = 0

      
While i < Me.Lines.Length
         FirstVisibleChar = GetCharFromLineIndex(i)
         ColorLineNumber(i, FirstVisibleChar)
         i += 1
      End While
   
End Sub

   Public Sub ColorVisibleLines()
      
Dim FirstLine As Integer = FirstVisibleLine()
      Dim LastLine As Integer = LastVisibleLine()
      Dim FirstVisibleChar As Integer

      
If (FirstLine = 0) And (LastLine = 0) Then
         
'If there is no text it will error, so exit the sub
         
Exit Sub
      
Else
         While FirstLine < LastLine
            FirstVisibleChar = GetCharFromLineIndex(FirstLine)
            ColorLineNumber(FirstLine, FirstVisibleChar)
            FirstLine += 1
         
End While
      
End If

   
End Sub

   Public Sub ColorLineNumber(ByVal LineIndex As Integer, ByVal lStart As Integer)
      
Dim i As Integer = 0
      
Dim Instance As Integer
      
Dim LeadingChar, TrailingChar As String
      
Dim SelectionAt As Integer = Me.SelectionStart
      
Dim MyRow As DataRow
      
Dim Line() As String, MyI As Integer, MyStr As String

      
' Lock the update
      
LockWindowUpdate(Me.Handle.ToInt32)

      MyI = lStart

      
If CaseSensitive Then
         
Line = Split(Me.Lines(LineIndex).ToString, " ")
      
Else
         
Line = Split(Me.Lines(LineIndex).ToLower, " ")
      
End If

      
For Each MyStr In Line
         
Me.SelectionStart = MyI
         Me.SelectionLength = MyStr.Length

         
If Words.Rows.Contains(MyStr) Then
            
MyRow = Words.Rows.Find(MyStr)
            
If (Not CaseSensitive) Or (CaseSensitive And MyRow("Word") = MyStr) Then
               
Me.SelectionColor = Color.FromName(MyRow("Color"))
            
End If
         Else
            
Me.SelectionColor = Color.Black
         
End If

         
MyI += MyStr.Length + 1
      Next

      ' Restore the selectionstart
      
Me.SelectionStart = SelectionAt
      
Me.SelectionLength = 0
      
Me.SelectionColor = Color.Black

      
' Unlock the update
      
LockWindowUpdate(0)
   
End Sub

   Public Function GetCharFromLineIndex(ByVal LineIndex As Integer) As Integer
      
Return SendMessage(Me.Handle, EditMessages.LineIndex, LineIndex, 0)
   
End Function

   Public Function FirstVisibleLine() As Integer
      
Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)
   
End Function

   Public Function LastVisibleLine() As Integer
      
Dim LastLine As Integer = FirstVisibleLine() + (Me.Height / Me.Font.Height)

      
If LastLine > Me.Lines.Length Or LastLine = 0 Then
         
LastLine = Me.Lines.Length
      End If

      
Return LastLine
   
End Function

   Public Sub New()
      
Dim MyRow As DataRow
      
Dim arrKeyWords() As String, strKW As String

      
Me.AcceptsTab = True

      
''Load all the keywords and the colors to make them 
      
Words.Columns.Add("Word")
      Words.PrimaryKey =
New DataColumn() {Words.Columns(0)}
      Words.Columns.Add("Color")

      arrKeyWords =
New String() {"select", "insert", "delete", _
         "truncate", "from", "where", "into", "inner", "update", _
         "outer", "on", "is", "declare", "set", "use", "values", "as", _
         "order", "by", "drop", "view", "go", "trigger", "cube", _
         "binary", "varbinary", "image", "char", "varchar", "text", _
         "datetime", "smalldatetime", "decimal", "numeric", "float", _
         "real", "bigint", "int", "smallint", "tinyint", "money", _
         "smallmoney", "bit", "cursor", "timestamp", "uniqueidentifier", _
         "sql_variant", "table", "nchar", "nvarchar", "ntext", "left", _
         "right", "like","and","all","in","null","join","not","or"}

      For Each strKW In arrKeyWords
         MyRow = Words.NewRow()
         MyRow("Word") = strKW
         MyRow("Color") = Color.LightCoral.Name
         Words.Rows.Add(MyRow)
      
Next

   
End Sub

   Public Property CaseSensitive() As Boolean
      
Get
         
Return _SyntaxHighlight_CaseSensitive
      
End Get
      
Set(ByVal Value As Boolean)
         _SyntaxHighlight_CaseSensitive = Value
      
End Set
   End Property

 

End Class   

 

Update July 8th, 2008: Here's a link that shows a couple tips that may help in writing your own Syntax Highlighting RichTextBox control:

http://codebetter.com/blogs/patricksmacchia/archive/2008/07/07/some-richtextbox-tricks.aspx

Currently rated 3.3 by 4 people

  • Currently 3.25/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: General | Win32API
Posted by crpietschmann on Tuesday, May 24, 2005 9:18 PM
Permalink | Comments (3) | Post RSSRSS comment feed


About the author

I'm Chris Pietschmann, go to the About Me page to learn more about me.

Search

Sponsors

Web.Maps.VE - ASP.NET AJAX Virtual Earth Mapping Server Control

Recent comments

Disclaimer


This work is licensed under a Creative Commons Attribution 3.0 United States License, unless explicitly stated otherwise within the posted content.
© Copyright 2004 - 2008 Chris Pietschmann