解决思想:只要查找父级Window及ScrollViewer控件,并注册LocationChanged及SizeChanged事件,当触发事件时改变Poup位置即可代码如下
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Runtime.InteropServices;
-
using System.Text;
-
using System.Threading.Tasks;
-
using System.Windows;
-
using System.Windows.Controls;
-
using System.Windows.Controls.Primitives;
-
using System.Windows.Interop;
-
using System.Windows.Media;
-
-
namespace
DataGridCrl
-
{
-
public
class
MyPopup:
Popup
-
{
-
/// <summary>
-
/// 是否窗口随动,默认为随动(true)
-
/// </summary>
-
public
bool IsPositionUpdate
-
{
-
get {
return (
bool)GetValue(IsPositionUpdateProperty); }
-
set { SetValue(IsPositionUpdateProperty,
value); }
-
}
-
-
public
static
readonly DependencyProperty IsPositionUpdateProperty =
-
DependencyProperty.Register(
"IsPositionUpdate",
typeof(
bool),
typeof(MyPopup),
new PropertyMetadata(
true,
new PropertyChangedCallback(IsPositionUpdateChanged)));
-
-
private static void IsPositionUpdateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-
{
-
(d
as MyPopup).pup_Loaded(d
as MyPopup,
null);
-
}
-
-
/// <summary>
-
/// 加载窗口随动事件
-
/// </summary>
-
public MyPopup()
-
{
-
this.Loaded += pup_Loaded;
-
}
-
-
/// <summary>
-
/// 加载窗口随动事件
-
/// </summary>
-
private void pup_Loaded(object sender, RoutedEventArgs e)
-
{
-
Popup pup = sender
as Popup;
-
var win = VisualTreeHelper.GetParent(pup);
-
var scro = VisualTreeHelper.GetParent(pup);
-
while (win !=
null && (win
as Window) ==
null)
-
{
-
win = VisualTreeHelper.GetParent(win);
-
}
-
if ((win
as Window) !=
null)
-
{
-
(win
as Window).LocationChanged -= PositionChanged;
-
(win
as Window).SizeChanged -= PositionChanged;
-
if (IsPositionUpdate)
-
{
-
(win
as Window).LocationChanged += PositionChanged;
-
(win
as Window).SizeChanged += PositionChanged;
-
}
-
}
-
while (scro !=
null && (scro
as ScrollViewer) ==
null)
-
{
-
scro = VisualTreeHelper.GetParent(scro);
-
}
-
if ((scro
as ScrollViewer) !=
null)
-
{
-
(scro
as ScrollViewer).ScrollChanged -= PositionScroChanged;
-
(scro
as ScrollViewer).SizeChanged -= PositionChanged;
-
if (IsPositionUpdate)
-
{
-
(scro
as ScrollViewer).ScrollChanged += PositionScroChanged;
-
(scro
as ScrollViewer).SizeChanged += PositionChanged;
-
}
-
}
-
}
-
-
/// <summary>
-
/// 刷新位置
-
/// </summary>
-
private void PositionChanged(object sender, EventArgs e)
-
{
-
try
-
{
-
var method =
typeof(Popup).GetMethod(
"UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
-
if (
this.IsOpen)
-
{
-
method.Invoke(
this,
null);
-
}
-
}
-
catch
-
{
-
return;
-
}
-
}
-
-
/// <summary>
-
/// 刷新位置
-
/// </summary>
-
private void PositionScroChanged(object sender, EventArgs e)
-
{
-
try
-
{
-
var method =
typeof(Popup).GetMethod(
"UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
-
if (
this.IsOpen)
-
{
-
method.Invoke(
this,
null);
-
}
-
}
-
catch
-
{
-
return;
-
}
-
}
-
-
//是否最前默认为非最前(false)
-
public
static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner(
typeof(Popup),
new FrameworkPropertyMetadata(
false, OnTopmostChanged));
-
public
bool Topmost
-
{
-
get {
return (
bool)GetValue(TopmostProperty); }
-
set { SetValue(TopmostProperty,
value); }
-
}
-
private static void OnTopmostChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
-
{
-
(obj
as MyPopup).UpdateWindow();
-
}
-
-
/// <summary>
-
/// 重写拉开方法,置于非最前
-
/// </summary>
-
/// <param name="e"></param>
-
protected override void OnOpened(EventArgs e)
-
{
-
UpdateWindow();
-
}
-
-
/// <summary>
-
/// 刷新Popup层级
-
/// </summary>
-
private void UpdateWindow()
-
{
-
var hwnd = ((HwndSource)PresentationSource.FromVisual(
this.Child)).Handle;
-
RECT rect;
-
if (NativeMethods.GetWindowRect(hwnd,
out rect))
-
{
-
NativeMethods.SetWindowPos(hwnd, Topmost ?
-1 :
-2, rect.Left, rect.Top, (
int)
this.Width, (
int)
this.Height,
0);
-
}
-
}
-
-
[
StructLayout(LayoutKind.Sequential)]
-
public
struct RECT
-
{
-
public
int Left;
-
public
int Top;
-
public
int Right;
-
public
int Bottom;
-
}
-
#region P/Invoke imports & definitions
-
public
static
class
NativeMethods
-
{
-
-
-
[
DllImport("user32.dll")]
-
[
return: MarshalAs(UnmanagedType.Bool)]
-
internal static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
-
[
DllImport("user32", EntryPoint = "SetWindowPos")]
-
internal static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags);
-
}
-
#endregion
-
}
-
}
如果窗口变化或滚动条时需要关闭Poup如下代码
1、添加暴漏的触发事件便于通知外部关闭Poup
public event EventHandler NotifyUpdatePosition;
2、在滚动条滚动或窗口变化时触发事件
-
/// <summary>
-
/// 刷新位置
-
/// </summary>
-
private void PositionChanged(object sender, EventArgs e)
-
{
-
try
-
{
-
var method =
typeof(Popup).GetMethod(
"UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
-
if (
this.IsOpen)
-
{
-
NotifyUpdatePosition?.Invoke(
null,
null);
-
//method.Invoke(this, null);
-
}
-
}
-
catch
-
{
-
return;
-
}
-
}
-
-
/// <summary>
-
/// 刷新位置
-
/// </summary>
-
private void PositionScroChanged(object sender, EventArgs e)
-
{
-
try
-
{
-
var method =
typeof(Popup).GetMethod(
"UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
-
if (
this.IsOpen)
-
{
-
if (e
is ScrollChangedEventArgs sc)
-
{
-
if (sc.HorizontalChange !=
0 || sc.VerticalChange !=
0)
-
{
-
NotifyUpdatePosition?.Invoke(
null,
null);
-
}
-
}
-
//NotifyUpdatePosition?.Invoke(null, null);
-
//method.Invoke(this, null);
-
}
-
}
-
catch
-
{
-
return;
-
}
-
}