WPF 实现Popup不在最上层显示、随窗口移动

文章目录


前言

由于WPF 默认的Popup总是显示在所有窗口的前面,如何让popup 层只显示在该父级之上,并随着父级而动呢?下面来看实现。


一、定义类继承Popup类

c 复制代码
public class PopupEx : 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(PopupEx), new PropertyMetadata(true, new PropertyChangedCallback(IsPositionUpdateChanged)));

    private static void IsPositionUpdateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as PopupEx).pup_Loaded(d as PopupEx, null);
    }

    /// <summary>  
    /// 加载窗口随动事件  
    /// </summary>  
    public PopupEx()
    {
        this.Loaded += pup_Loaded;
    }

    /// <summary>  
    /// 加载窗口随动事件  
    /// </summary>  
    private void pup_Loaded(object sender, RoutedEventArgs e)
    {
        Popup pup = sender as Popup;
        var win = 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;
            }
        }
    }

    /// <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;
        }
    }

    //是否最前默认为非最前(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 PopupEx).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
}

二、使用

1.在XAML头部加入链接

xml 复制代码
 xmlns:Component="clr-namespace:ZzgkChatRoom.Component"

2. 在XAML文件使用

代码如下(示例):

xml 复制代码
  <Button
      x:Name="EmojiBtn"
      Grid.Column="0"
      Margin="0,0,0,0"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
      Click="EmojiBtn_Click"
      Content="0 2 0 0"
      Style="{StaticResource ChatBottomBtn}"
      Tag="{x:Static Icon:PackIconVaadinIconsKind.SmileyOutline}" />
	<Component:PopupEx
    	x:Name="EmojiPop"
    	AllowsTransparency="True"
   	 	HorizontalOffset="-40"
    	IsOpen="False"
    	Placement="Top"
   	 	PlacementTarget="{Binding ElementName=EmojiBtn}"
    	VerticalOffset="-5">
    <Border
        Width="400"
        Height="200"
        Background="#ffffff"
        BorderBrush="#646465"
        BorderThickness="1"
        CornerRadius="3" />
	</Component:PopupEx>

总结

相关推荐
wqq10271 小时前
WPF 从Main()方法启动
wpf
明耀6 小时前
WPF ListBox双击事件
wpf
wqq10277 小时前
WPF 依赖注入启动的问题
wpf
wqq102710 小时前
WPF 使用 DI EF CORE SQLITE
sqlite·wpf
Marzlam1 天前
一文读懂WPF系列之MVVM
wpf
Marzlam1 天前
一文读懂WPF系列之依赖属性与附加属性
wpf
zxb11c1 天前
WPF 中的元素继承层次结构 ,以下是对图中内容的详细说明:
wpf
Zhen (Evan) Wang1 天前
Margin和Padding在WPF和CSS中的不同
css·wpf
Marzlam2 天前
一文读懂WPF布局
wpf
WineMonk2 天前
.NET WPF 控件类分层结构
.net·wpf