因为我以前没用过VLC控件,但是最近有小伙伴问到我这个问题。
说是使用了LibVLCSharp.WPF里面的视频播放控件,无法处理鼠标点击事件。
其实这种一般原因也比较明确,就是LibVLCSharp.WPF里面的视频播放器是一个Win32的控件,直接通过WPF的事件处理机制,是无法捕获到鼠标点击 事件的。
除非这个控件自己抛出了事件,否则无法直接处理。
在我前面的一篇文章《WPF创建不规则窗体时WebBrowser控件不显示的问题》里我大概介绍过这个问题,主要是由于空域问题导致。
我在网上搜了一圈,发现解决方法比较少。这里分享一下解决方案,给有需要的小伙伴。
这里提供两种解决方案。
一、添加一个事件处理面板
因为我们点击事件被VideoView把事件“吞了”。
在WPF里,会有事件路由机制,但是Win32里没有,所以这个事件就是直接被吞了。
仔细观察发现,VideoView是一个内容控件,但是运行后,发现VideoView.Content是为空的,说明VideoView只是一个宿主控件而已。内部会进行重绘(猜想,未经证实)。
此时我们只需要设置一下VideoView.Content为一个面板,并设置透明度为几乎透明的状态,这样,我们能看到视频播放效果,也能对事件进行处理。
1 <Grid> 2 <vlc:VideoView x:Name="videoView"> 3 <Grid MouseLeftButtonDown="videoView_MouseDown"> 4 <Grid.Background> 5 <SolidColorBrush Color="White" Opacity="0.01"></SolidColorBrush> 6 </Grid.Background> 7 </Grid> 8 </vlc:VideoView> 9 10 </Grid>
事件处理函数如下
1 private void videoView_MouseDown(object sender, MouseButtonEventArgs e) 2 { 3 if(e.ClickCount == 2) 4 { 5 MessageBox.Show("双击"); 6 } 7 }
这样我们就能对鼠标点击事件进行处理
注意:双击和单击的处理需要通过两次点击时间间隔去判断,而不能通过e.ClickCount,这里就不做详细介绍了
二、使用钩子函数
我们使用Spyxx对控件的窗口句柄进行查看
然后将这个值跟LibVLCSharp.Shared.MediaPlayer.Hwnd进行对比,发现是一样的。
我们可以借助SetWinodwsHookEx函数,来获取这个句柄的Win32消息。
但是初次尝试,发现创建钩子不成功,后面又尝试了一下,使用子窗口Direct3D11 output这个窗口是可以的。
实现步骤如下:
1、导入Winapi函数
1 [DllImport("user32.dll", SetLastError = true)] 2 private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId); 3 4 [DllImport("user32.dll", SetLastError = true)] 5 [return: MarshalAs(UnmanagedType.Bool)] 6 private static extern bool UnhookWindowsHookEx(IntPtr hhk); 7 8 [DllImport("user32.dll")] 9 private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); 10 11 [DllImport("user32.dll")] 12 private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId); 13 14 private const int WH_MOUSE = 7; 15 private const int WH_MOUSE_LL = 14; 16 private const int WM_LBUTTONDOWN = 0x0201; 17 private const int WM_RBUTTONDOWN = 0x0204; 18 private const int WM_LBUTTONDBLCLK = 0x0203; 19 20 private static IntPtr hookID = IntPtr.Zero; 21 private static LowLevelMouseProc mouseProc = HookCallback; 22 23 private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam); 24 25 [DllImport("user32.dll")] 26 private static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam); 27 28 private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam); 29 30 public static List<IntPtr> GetAllChildHandles(IntPtr parentHwnd) 31 { 32 List<IntPtr> result = new List<IntPtr>(); 33 EnumChildWindows(parentHwnd, (hWnd, lParam) => 34 { 35 result.Add(hWnd); 36 return true; 37 }, IntPtr.Zero); 38 return result; 39 }
2、在窗口加载事件里创建钩子
1 public async void SetHook(IntPtr hwnd) 2 { 3 //等待,防止运行较慢,窗口未显示 4 await Task.Delay(1000); 5 6 var childWindowList = GetAllChildHandles(hwnd); 7 8 if(childWindowList == null || childWindowList.Count == 0) 9 { 10 return; 11 } 12 13 hookID = SetWindowsHookEx(WH_MOUSE, mouseProc, IntPtr.Zero, GetWindowThreadProcessId(childWindowList[0], out _)); 14 }
3、钩子处理函数
1 private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) 2 { 3 if (nCode >= 0) 4 { 5 if((int)wParam == WM_LBUTTONDBLCLK) 6 { 7 MessageBox.Show("双击"); 8 } 9 } 10 return CallNextHookEx(hookID, nCode, wParam, lParam); 11 }
4、在主窗口关闭时,卸载钩子
1 private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) 2 { 3 Unhook(); 4 }
运行效果如下: