Skip to content

[C# WPF] システムメニューが開かれたタイミングでなにかしたい

メニューが開かれたときには設定を解除しないといけない案件がありまして、メインメニューとコンテキストメニューはContextMenuOpendのイベントハンドラを使えば対応できたのですが、システムメニューどうするんだ?ということで調べた結果です。WPFだけではシステムメニューが開かれるタイミングを取得できなかったので、WndProcをフックすることになりました。

最小構成のサンプルです。
システムメニューが開かれるとデバッグ出力します。
メインメニューとコンテキストメニューは、メッセージが飛んでこないことの確認用です。

<window
  x:class="Sample_WndProc.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:Sample_WndProc"
  mc:ignorable="d" title="MainWindow" height="350" width="525"
  loaded="Window_Loaded"
>
    <dockpanel>
        <menu dockpanel.dock="Top" verticalalignment="Top">
            <menuitem header="File">
                <menuitem header="File 1"/>
                <menuitem header="File 2"/>
                <menuitem header="File 3"/>
            </menuitem>
        </menu>

    <window.contextmenu>
        <contextmenu>
            <menuitem header="menu 1"/>
            <menuitem header="menu 2"/>
            <menuitem header="menu 3"/>
        </contextmenu>
    </window.contextmenu>
</window>
using System;
using System.Windows;
using System.Windows.Interop;

namespace Sample_WndProc
{
    /// 
    /// MainWindow.xaml の相互作用ロジック
    /// 
    public partial class MainWindow : Window
    {
        private HwndSource _hwndSource = null;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            _hwndSource = PresentationSource.FromVisual(this) as HwndSource;
            if (_hwndSource != null)
            {
                _hwndSource.AddHook(WndProc);
            }
        }

        private enum WindowMessage : uint
        {
            WM_INITMENU = 0x0116
        }

        private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == (int)WindowMessage.WM_INITMENU)
            {
                System.Diagnostics.Debug.Print("init menu");
            }

            return IntPtr.Zero;
        }
    }
}

メインメニューやコンテキストメニューを開くときにも呼ばれそうだったのですが、呼ばれなかったのでWM_INITMENUのメッセージを使用しています(mfcでは呼ばれたような気が。

おしまい。

Be First to Comment

コメントを残す

メールアドレスが公開されることはありません。

*

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください