Question

I have a WPF app (written in C#) which has a Listview control which scrolls perfectly with the mouse wheel when the app is in focus. However when the app is not in focus, even when the mouse pointer is over the app & list view area, the Listview does not scroll. I continue to see mousehover related effects on the app but no mousewheel event is received. This is inline with how most of the other apps work on my desktop however some of them (like Facebook messenger) support scrolling without focus which i would like to mimic in my WPF app.

I have searched MSDN forums and Stackoverflow and seen multiple solutions for Windows Forms however they were questions asked over 5 years ago and i was wondering if someone has managed to do it relatively easily on .net 4.5 and can point me to possible solutions.

---Edit---

I was able to progress to some extent on this thanks to this thread C# ListView mouse wheel scroll without focus

Here is how my the function that receives the mousewheel looks

private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
            Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
            Console.WriteLine((short)((hookStruct.mouseData)>>16));                
            MouseWheelEventArgs myArgs = new MouseWheelEventArgs(System.Windows.Input.Mouse.PrimaryDevice, (int)hookStruct.time, (short)((hookStruct.mouseData)>>16));
            myMainFrame.SidePanelControl.ScrollTheListView(myArgs);
            }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

As you can see i am initializing a MouseWheelEventArgs instance and have the time, delta and the mouse device attributes.

How do i go about passing this mousewheel event to my listview scrollviewer?

Was it helpful?

Solution

Managed to get this working. Here is my class. All one needs to do to use the class is

  1. Initialize InterceptMouse passing it the app/listview/etc pointer
  2. Start intercepting the mouse when the app is not in focus but the corresponding mouseenter event has occured.
  3. As long as the event is mousewheel the scrollviewer of the listview will be sent the mouseWheel event.
  4. Stop intercepting the mouse when the app gets activated or mouseleave is called.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Forms;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Input;
using System.Diagnostics;
using System.Windows.Forms.Integration;
namespace myApp.HelperClasses
{
    public class InterceptMouse
    {
        public static System.Windows.Controls.ListView myListview;
        private static LowLevelMouseProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;


        public InterceptMouse(System.Windows.Controls.ListView myListviewParam)
        {
            myListview = myListviewParam;
        }

        public static void StartIntercepting()
        {
            _hookID = SetHook(_proc);
        }
        public static void StopIntercepting()
        {
            UnhookWindowsHookEx(_hookID);
        }

        private static IntPtr SetHook(LowLevelMouseProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_MOUSE_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);


        private static IntPtr HookCallback(
            int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 &&
                MouseMessages.WM_MOUSEWHEEL == (MouseMessages)wParam)
            {
                MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                //Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y + "," + hookStruct.mouseData);

                var delta = (short)((hookStruct.mouseData) >> 16);
                var mouse = InputManager.Current.PrimaryMouseDevice;
                var args = new MouseWheelEventArgs(mouse, Environment.TickCount, delta);
                args.RoutedEvent = WindowsFormsHost.MouseWheelEvent;

                Decorator border = VisualTreeHelper.GetChild(myListview, 0) as Decorator;
                // Get scrollviewer
                ScrollViewer scrollViewer = border.Child as ScrollViewer;
                scrollViewer.RaiseEvent(args);
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }



        private const int WH_MOUSE_LL = 14;

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook,
            LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
            IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top