Extended WPF Application
Part 2: System messages and Help button to the window title bar
Principle
WPF does not provide dependency properties to modify the window title bar's system context menu, to add a help button to the title bar, or to handle the mouse extension buttons events, for example. We will see here how to handle the system messages in our WPF window, in order to add dependency properties bound to these messages. We will see in a further article how to modify the title bar context menu and how to handle the mouse extension buttons events.
Prerequisites
To achieve this task, we will need the following packages:
Preparing delegate registration
We need to get a notification when an event occurs. In order to do that, we need to retrieve the System.Windows.Interop.HwndSource associated to the window. Once we have this object, we can register such a delegate. There is two ways to retrieve this: on window source initialized, or at any time, using the window pointer. As the example feature used here is to add an Help button to the window title bar and assuming that feature is supposed to be inherent to that window, we use the first solution here. The article that will demonstrate how to add a notification icon in the task bar will use the second one as this feature is not supposed to be on window side but on application side, a window being able to handle more than one notification icon.
In order to do that, the OnSourceInitialized(EventArgs e) method has to be overridden (do not forget to call the base method implementation), and the overridden implementation to call System.Windows.PresentationSource.FromVisual(this) where this is the window. This method returns an instance of PresentationSource or null if the the window is disposed. So, we have to check that the returned value is actually a HwndSource. If yes, then we can continue by using this value as an HwndSource.
For clarity and utility reasons, let's put the next part of the code in a new virtual method. This method header is the same as OnSourceInitialized, but its parameter is the HwndSource retrieved in the overridden one.
Before register
Now, we'll be able to register for system messages. But, in order to get Help button click event messages, still it would be necessary that the user can click on it and so... that it appears in the window title bar. But how and when adding it?
First, we need to know if we have to add it now. So, let's consider adding a dependency property to store a boolean value that determines whether the button should be added or not. This property should have its metadata's default value set to false, as this is not the default window behavior (unless it is for your window, of course). As false is the default value for the Boolean type, we do not have to modify anything. If the default behavior for your window is to have that button, set this value to true. If you do not want your window to have the capacity to change that mode, simply skip this step and do not add any property at all.
Now we have this property, we can check its value in the new OnSourceInitialized method. If its value is true, that means that the Help button should be added. In order to do that, we need to retrieve the handle of the window. This can be done by initializing a new instance of the System.Windows.Interop.WindowInteropHelper class and retrieving its Handle property. Now, we can call the Microsoft.WindowsAPICodePack.Shell assembly's DesktopWindowManager.SetWindow method like this:
SetWindow(hwnd, IntPtr.Zero, 0, 0, 0, 0, (WindowStyles)(((long)GetWindowStyles(hwnd, GetWindowLongEnum.Style) & 0xFFFFFFFF) ^ ((uint)WindowStyles.MinimizeBox | (uint)WindowStyles.MaximizeBox)), (WindowStyles)((uint)GetWindowStyles(hwnd, GetWindowLongEnum.ExStyle) | (uint)WindowStyles.ContextHelp), SetWindowPositionOptions.NoMove | SetWindowPositionOptions.NoSize | SetWindowPositionOptions.NoZOrder | SetWindowPositionOptions.FrameChanged);
where hwnd is the window handle. Now we can register our delegate by calling the HwndSource object's AddHook method. Like for the current method itself, it would be useful to have a protected virtual method to use as delegate, so the delegate header would be defined as:
protected virtual IntPtr OnSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled);
It will be in this method that we will handle the system messages. Keep in mind that each message can modify the meaning of wParam and lParam. In the next article, we will see how to activate and deactivate an 'help mode' in our window by handling Help button user clicks.