avalondock 技巧之如何隐藏浮动面板停靠器
之前开发的一个项目需要实现窗口的浮出,拖拽,停靠等功能,于是想到了神器Avalondock,这个框架功能相当强大,而且能实现多种主题样式的控件,特别是窗口的浮动停靠等功能。目前该框架有收费版本和开源版本,我之前的项目使用的是avalondock v2.0的,目前最新的是v4.0。
官方链接: https://archive.codeplex.com/?p=avalondock.
Git链接: https://github.com/xceedsoftware/wpftoolkit.
在使用过程中需要用到窗口的浮出,停靠功能,每当拖拽窗口时会显示类似Visual Studio的窗口停靠器,这个功能默认就可以使用,但实际使用中却并不是处处都需要这个功能,有的时候因为用户的随意拖动会导致原始窗口发生较大变化,而且这个窗口停靠器官方并没有给出隐藏显示设置入口。
经过一翻stackoverflow查询,发现外国人也有这个困扰,回复里也给出了一些方式,比如说每次保存当前窗口,当发生停靠后可以一键恢复原来的窗口等,都是从接口入手解决的。我将git上的源码拉下来研究了一番,发现只要控制拖拽事件就能阻止触发这个停靠管理器的显示,于是找到了DragService.cs文件,里面实现了对拖拽功能的实现,在窗口布局控制文件LayoutFloatingWindowControl.cs找到了对鼠标右键的按下事件:
protected virtual IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){handled = false;switch (msg){case Win32Helper.NCCALCSIZE:if (wParam != IntPtr.Zero){handled = true;var client = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));client.Bottom -= 1;Marshal.StructureToPtr(client, lParam, false);}break;//case Win32Helper.WM_NCHITTEST:// {// handled = true;// //var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();// //switch( htLocation )// //{// // case (int)HitTestResult.HTBOTTOM:// // case (int)HitTestResult.HTBOTTOMLEFT:// // case (int)HitTestResult.HTBOTTOMRIGHT:// // case (int)HitTestResult.HTLEFT:// // case (int)HitTestResult.HTRIGHT:// // case (int)HitTestResult.HTTOP:// // case (int)HitTestResult.HTTOPLEFT:// // case (int)HitTestResult.HTTOPRIGHT:// // htLocation = (int)HitTestResult.HTBORDER;// }// break;case Win32Helper.WM_ACTIVATE:if (((int)wParam & 0xFFFF) == Win32Helper.WA_INACTIVE){if (lParam == this.GetParentWindowHandle()){Win32Helper.SetActiveWindow(_hwndSrc.Handle);handled = true;}}break;case Win32Helper.WM_EXITSIZEMOVE:UpdatePositionAndSizeOfPanes();if (_dragService != null){bool dropFlag;var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition());_dragService.Drop(mousePosition, out dropFlag);_dragService = null;SetIsDragging(false);if (dropFlag)InternalClose();}break;case Win32Helper.WM_MOVING:{UpdateDragPosition();if (this.IsMaximized){this.UpdateMaximizedState(false);}}break;case Win32Helper.WM_LBUTTONUP: //set as handled right button click on title area (after showing context menu)if (_dragService != null && Mouse.LeftButton == MouseButtonState.Released){_dragService.Abort();_dragService = null;SetIsDragging(false);}break;case Win32Helper.WM_SYSCOMMAND:int command = (int)wParam & 0xFFF0;if (command == Win32Helper.SC_MAXIMIZE || command == Win32Helper.SC_RESTORE){UpdateMaximizedState(command == Win32Helper.SC_MAXIMIZE);}break;}return IntPtr.Zero;}
其中:UpdateDragPosition() 就是实现对移动过程中触发窗口停靠器
case Win32Helper.WM_MOVING:{UpdateDragPosition();if (this.IsMaximized){this.UpdateMaximizedState(false);}}break;
private void UpdateDragPosition(){if (_dragService == null){_dragService = new DragService( this );SetIsDragging(true);}var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition());_dragService.UpdateMouseLocation(mousePosition);}
改造一下,不实现DragService即可。
private void UpdateDragPosition(){if (_dragService == null){//不初始化拖拽事件//_dragService = new DragService( this );SetIsDragging(true);}var mousePosition = this.TransformToDeviceDPI(Win32Helper.GetMousePosition());if (_dragService != null){_dragService.UpdateMouseLocation(mousePosition);}}
重新生成一下,运行自己的程序,再也没有窗口停靠器的出现了。这下界面就不会被用户搞乱了,但想要实现的浮动停靠功能就没有了,于是自己手动通过按钮来触发浮出,停靠功能,而拖拽窗口并不会触发停靠事件。
private void btnFloatVideo_Click(object sender, RoutedEventArgs e){if (anchorable.IsFloating){this.btnFloatVideo.Content = "浮出窗口";anchorable.Dock();teleCenter.DockWidth = new GridLength(720);telerecord.DockWidth = new GridLength(700);}else{this.btnFloatVideo.Content = "停靠窗口";anchorable.Float();}}
这下窗口的所有控制都在你的可控之下了。