WPF 解决Popup控件跟随窗体移动、滚动位置变化

解决思想:只要查找父级Window及ScrollViewer控件,并注册LocationChanged及SizeChanged事件,当触发事件时改变Poup位置即可代码如下


   
   
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Controls.Primitives;
  10. using System.Windows.Interop;
  11. using System.Windows.Media;
  12. namespace DataGridCrl
  13. {
  14. public class MyPopup: Popup
  15. {
  16. /// <summary>
  17. /// 是否窗口随动,默认为随动(true)
  18. /// </summary>
  19. public bool IsPositionUpdate
  20. {
  21. get { return ( bool)GetValue(IsPositionUpdateProperty); }
  22. set { SetValue(IsPositionUpdateProperty, value); }
  23. }
  24. public static readonly DependencyProperty IsPositionUpdateProperty =
  25. DependencyProperty.Register( "IsPositionUpdate", typeof( bool), typeof(MyPopup), new PropertyMetadata( true, new PropertyChangedCallback(IsPositionUpdateChanged)));
  26. private static void IsPositionUpdateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  27. {
  28. (d as MyPopup).pup_Loaded(d as MyPopup, null);
  29. }
  30. /// <summary>
  31. /// 加载窗口随动事件
  32. /// </summary>
  33. public MyPopup()
  34. {
  35. this.Loaded += pup_Loaded;
  36. }
  37. /// <summary>
  38. /// 加载窗口随动事件
  39. /// </summary>
  40. private void pup_Loaded(object sender, RoutedEventArgs e)
  41. {
  42. Popup pup = sender as Popup;
  43. var win = VisualTreeHelper.GetParent(pup);
  44. var scro = VisualTreeHelper.GetParent(pup);
  45. while (win != null && (win as Window) == null)
  46. {
  47. win = VisualTreeHelper.GetParent(win);
  48. }
  49. if ((win as Window) != null)
  50. {
  51. (win as Window).LocationChanged -= PositionChanged;
  52. (win as Window).SizeChanged -= PositionChanged;
  53. if (IsPositionUpdate)
  54. {
  55. (win as Window).LocationChanged += PositionChanged;
  56. (win as Window).SizeChanged += PositionChanged;
  57. }
  58. }
  59. while (scro != null && (scro as ScrollViewer) == null)
  60. {
  61. scro = VisualTreeHelper.GetParent(scro);
  62. }
  63. if ((scro as ScrollViewer) != null)
  64. {
  65. (scro as ScrollViewer).ScrollChanged -= PositionScroChanged;
  66. (scro as ScrollViewer).SizeChanged -= PositionChanged;
  67. if (IsPositionUpdate)
  68. {
  69. (scro as ScrollViewer).ScrollChanged += PositionScroChanged;
  70. (scro as ScrollViewer).SizeChanged += PositionChanged;
  71. }
  72. }
  73. }
  74. /// <summary>
  75. /// 刷新位置
  76. /// </summary>
  77. private void PositionChanged(object sender, EventArgs e)
  78. {
  79. try
  80. {
  81. var method = typeof(Popup).GetMethod( "UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  82. if ( this.IsOpen)
  83. {
  84. method.Invoke( this, null);
  85. }
  86. }
  87. catch
  88. {
  89. return;
  90. }
  91. }
  92. /// <summary>
  93. /// 刷新位置
  94. /// </summary>
  95. private void PositionScroChanged(object sender, EventArgs e)
  96. {
  97. try
  98. {
  99. var method = typeof(Popup).GetMethod( "UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  100. if ( this.IsOpen)
  101. {
  102. method.Invoke( this, null);
  103. }
  104. }
  105. catch
  106. {
  107. return;
  108. }
  109. }
  110. //是否最前默认为非最前(false)
  111. public static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner( typeof(Popup), new FrameworkPropertyMetadata( false, OnTopmostChanged));
  112. public bool Topmost
  113. {
  114. get { return ( bool)GetValue(TopmostProperty); }
  115. set { SetValue(TopmostProperty, value); }
  116. }
  117. private static void OnTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
  118. {
  119. (obj as MyPopup).UpdateWindow();
  120. }
  121. /// <summary>
  122. /// 重写拉开方法,置于非最前
  123. /// </summary>
  124. /// <param name="e"></param>
  125. protected override void OnOpened(EventArgs e)
  126. {
  127. UpdateWindow();
  128. }
  129. /// <summary>
  130. /// 刷新Popup层级
  131. /// </summary>
  132. private void UpdateWindow()
  133. {
  134. var hwnd = ((HwndSource)PresentationSource.FromVisual( this.Child)).Handle;
  135. RECT rect;
  136. if (NativeMethods.GetWindowRect(hwnd, out rect))
  137. {
  138. NativeMethods.SetWindowPos(hwnd, Topmost ? -1 : -2, rect.Left, rect.Top, ( int) this.Width, ( int) this.Height, 0);
  139. }
  140. }
  141. [ StructLayout(LayoutKind.Sequential)]
  142. public struct RECT
  143. {
  144. public int Left;
  145. public int Top;
  146. public int Right;
  147. public int Bottom;
  148. }
  149. #region P/Invoke imports & definitions
  150. public static class NativeMethods
  151. {
  152. [ DllImport("user32.dll")]
  153. [ return: MarshalAs(UnmanagedType.Bool)]
  154. internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
  155. [ DllImport("user32", EntryPoint = "SetWindowPos")]
  156. internal static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags);
  157. }
  158. #endregion
  159. }
  160. }

如果窗口变化或滚动条时需要关闭Poup如下代码

1、添加暴漏的触发事件便于通知外部关闭Poup

  public event EventHandler NotifyUpdatePosition;
   
   

2、在滚动条滚动或窗口变化时触发事件


   
   
  1. /// <summary>
  2. /// 刷新位置
  3. /// </summary>
  4. private void PositionChanged(object sender, EventArgs e)
  5. {
  6. try
  7. {
  8. var method = typeof(Popup).GetMethod( "UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  9. if ( this.IsOpen)
  10. {
  11. NotifyUpdatePosition?.Invoke( null, null);
  12. //method.Invoke(this, null);
  13. }
  14. }
  15. catch
  16. {
  17. return;
  18. }
  19. }
  20. /// <summary>
  21. /// 刷新位置
  22. /// </summary>
  23. private void PositionScroChanged(object sender, EventArgs e)
  24. {
  25. try
  26. {
  27. var method = typeof(Popup).GetMethod( "UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  28. if ( this.IsOpen)
  29. {
  30. if (e is ScrollChangedEventArgs sc)
  31. {
  32. if (sc.HorizontalChange != 0 || sc.VerticalChange != 0)
  33. {
  34. NotifyUpdatePosition?.Invoke( null, null);
  35. }
  36. }
  37. //NotifyUpdatePosition?.Invoke(null, null);
  38. //method.Invoke(this, null);
  39. }
  40. }
  41. catch
  42. {
  43. return;
  44. }
  45. }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值