22

私が本当に欲しいのは、マウスクリックイベントを無視するが、マウスの入力イベントと離脱イベントをトラップするバージョンの IsHitTestVisible です。

背景: 情報オーバーレイは、フォーカスのあるコントロールの下にポップアップ表示されます。これは要件であるため、この動作を自由に削除することはできません。これは、イメージ ブラシで塗りつぶされた Rectangle シェイプを含む adorner を使用して実装されます。すべてのコントロールはプログラムで作成され、XAML は関与しません。

望ましい動作: ユーザーが Rectangle の上にマウスを置くと、部分的に透明になる必要があります。これは、オーバーレイの下にある他のコントロールを表示してクリックできるようにするためです。ユーザーがオーバーレイをクリックすると、ユーザーがクリックした場所で、オーバーレイの下にあるコントロールにクリックが渡される必要があります。

問題: IsHitTestVisibleを True に設定してマウス クリックが通過できるようにすると、MouseEnter イベントが発生しません。

IsHitTestVisible True のままにして、2 ~ 3 個を除くすべてのイベントを装飾の下の正しいコントロールに渡す簡単な方法はありますか? WPFは明らかに私のためにこれを行うことができるので、カーソルの下にあるコントロールを計算する必要のないソリューションを探しています。

または、IsHitTestVisible を False に設定してから、別の簡単な方法を使用して、マウスがアドナーの上にあることを判断できますか?

更新:私はまだ答えを望んでいますが、今のところ最も有望な解決策は、IsHitTestVisible を true のままにして、WPF ヒット テスト API を使用して、マウス カーソルの下にあるコントロールの種類を把握することです。それが私が知っているものだったら、それに Click コマンドを送ります。ただし、これが価値があるかどうかはわかりません。今のところ、クリックするとオーバーレイが消えるので、ユーザーは 2 回クリックするだけです。

ありがとう!

4

1 に答える 1

7

IsHitTestVisible="False" はすべてのマウス操作を無効にするため、これはクリーンな方法ではないようです。私は試した

  • OnPreviewMouseDown で IsHitTestVisible="False" を設定し、UIAutomation を使用して adorner を再クリックしますが、サイコロは使用しません
  • Opacity を Adorner の下の「見えない」要素にバインドして、クリックが通過するようにします。それは問題なく通過しましたが、もちろん次のレベルでも問題は同じだったのでサイコロはありませんでした。

これを実際に機能させる唯一の方法は、OnMouseDown で IsHitTestVisible="False" を設定し、SendInput を使用して新しい MouseClick をシミュレートすることです。あまりきれいではありませんが、それは仕事を成し遂げます。

protected override void OnMouseDown(MouseButtonEventArgs e)
{
    IsHitTestVisible = false;

    Action action = () =>
    {
        MouseSimulator.ClickLeftMouseButton();
        IsHitTestVisible = true;
    };
    this.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle); 
}

public class MouseSimulator
{
    [DllImport("user32.dll", SetLastError = true)]
    static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

    [StructLayout(LayoutKind.Sequential)]
    struct INPUT
    {
        public SendInputEventType type;
        public MouseKeybdhardwareInputUnion mkhi;
    }
    [StructLayout(LayoutKind.Explicit)]
    struct MouseKeybdhardwareInputUnion
    {
        [FieldOffset(0)]
        public MouseInputData mi;

        [FieldOffset(0)]
        public KEYBDINPUT ki;

        [FieldOffset(0)]
        public HARDWAREINPUT hi;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
    [StructLayout(LayoutKind.Sequential)]
    struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }
    struct MouseInputData
    {
        public int dx;
        public int dy;
        public uint mouseData;
        public MouseEventFlags dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }
    [Flags]
    enum MouseEventFlags : uint
    {
        MOUSEEVENTF_MOVE = 0x0001,
        MOUSEEVENTF_LEFTDOWN = 0x0002,
        MOUSEEVENTF_LEFTUP = 0x0004,
        MOUSEEVENTF_RIGHTDOWN = 0x0008,
        MOUSEEVENTF_RIGHTUP = 0x0010,
        MOUSEEVENTF_MIDDLEDOWN = 0x0020,
        MOUSEEVENTF_MIDDLEUP = 0x0040,
        MOUSEEVENTF_XDOWN = 0x0080,
        MOUSEEVENTF_XUP = 0x0100,
        MOUSEEVENTF_WHEEL = 0x0800,
        MOUSEEVENTF_VIRTUALDESK = 0x4000,
        MOUSEEVENTF_ABSOLUTE = 0x8000
    }
    enum SendInputEventType : int
    {
        InputMouse,
        InputKeyboard,
        InputHardware
    }

    public static void ClickLeftMouseButton()
    {
        INPUT mouseDownInput = new INPUT();
        mouseDownInput.type = SendInputEventType.InputMouse;
        mouseDownInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;
        SendInput(1, ref mouseDownInput, Marshal.SizeOf(new INPUT()));

        INPUT mouseUpInput = new INPUT();
        mouseUpInput.type = SendInputEventType.InputMouse;
        mouseUpInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTUP;
        SendInput(1, ref mouseUpInput, Marshal.SizeOf(new INPUT()));
    }
}
于 2010-12-16T08:08:22.383 に答える