Loading

WPF 模仿微信顶部断网提示气泡

直接看顶部气泡的效果吧

 

 

 

 顶部气泡主要要做三个工作

1.定位到顶部居中

2.气泡需要跟随窗体

3.气泡不可以遮挡住其他程序界面

 

原生的WPF Poupu控件不会跟随目标移动且在Z轴上会置顶,所以存在打开其他程序被气泡遮挡的问题。我们需要一一解决。

1.气泡跟随目标移动,采用附加属性的方法,在change方法里设置气泡的位置与目标的位置相对变化即可,直接上代码

public static class PopopHelper
    {
        public static DependencyObject GetPopupPlacementTarget(DependencyObject obj)
        {
            return (DependencyObject)obj.GetValue(PopupPlacementTargetProperty);
        }

        public static void SetPopupPlacementTarget(DependencyObject obj, DependencyObject value)
        {
            obj.SetValue(PopupPlacementTargetProperty, value);
        }

        // Using a DependencyProperty as the backing store for PopupPlacementTarget.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PopupPlacementTargetProperty =
            DependencyProperty.RegisterAttached("PopupPlacementTarget", typeof(DependencyObject), typeof(PopopHelper), new PropertyMetadata(null, OnPopupPlacementTargetChanged));

        private static void OnPopupPlacementTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != null)
            {
                DependencyObject popupPopupPlacementTarget = e.NewValue as DependencyObject;
                Popup pop = d as Popup;

                Window w = Window.GetWindow(popupPopupPlacementTarget);
                if (null != w)
                {
                    w.LocationChanged += delegate
                    {
                        var offset = pop.HorizontalOffset;
                        pop.HorizontalOffset = offset + 1;
                        pop.HorizontalOffset = offset;
                    };
                }
            }
        }

    }

在xaml中的使用办法:

helpers:PopopHelper.PopupPlacementTarget="{Binding ElementName=mainwindow}"

2.需要气泡不置顶遮挡其他打开的程序,这里是借鉴的网上的一张办法,亲测可用。

public class PopupNonTopmost : Popup
    {
        public static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner(
            typeof(PopupNonTopmost),
            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 PopupNonTopmost).UpdateWindow();
        }

        protected override void OnOpened(EventArgs e)
        {
            UpdateWindow();
        }

        private void UpdateWindow()
        {
            var hwnd = ((HwndSource)PresentationSource.FromVisual(this.Child)).Handle;
            RECT rect;

            if (GetWindowRect(hwnd, out rect))
            {
                SetWindowPos(hwnd, Topmost ? -1 : -2, rect.Left, rect.Top, (int)this.Width, (int)this.Height, 0);
            }
        }

        #region P/Invoke imports & definitions

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [DllImport("user32", EntryPoint = "SetWindowPos")]
        private static extern int SetWindowPos(IntPtr hWnd, int hwndInsertAfter, int x, int y, int cx, int cy, int wFlags);

        #endregion
    }

在xaml中就需要把我们的Popup改成这里的继承控件了。

helpers:PopupNonTopmost 

3.定位问题,

如果是置顶,大家可以用top,然后设置width来居中,不要问我为什么width可以居中,我自己试的。。。

也可以用官方的办法,采用custom的定位方式,然后后台代码去调位置,但是这样调试比较麻烦,可能需要一直开关调试程序。

我建议用两种一起使用如下。

Placement="Custom" 

将 PopupNonTopmost控件的定位方式设置为Custom,自定义

后台创建如下方法

public CustomPopupPlacement[] placePopup(Size popupSize,
                                           Size targetSize,
                                           Point offset)
        {
            //调整y轴
            CustomPopupPlacement placement1 =
               new CustomPopupPlacement(new Point(50, -90), PopupPrimaryAxis.Vertical);

            //调整x轴
            CustomPopupPlacement placement2 =
                new CustomPopupPlacement(new Point(500 ,1000), PopupPrimaryAxis.Horizontal);

            CustomPopupPlacement[] ttplaces =
                    new CustomPopupPlacement[] { placement1, placement2 };
            return ttplaces;
        }

最后将方法设置到控件的定位回滚事件上 ,pop1是控件名。

pop1.CustomPopupPlacementCallback =new CustomPopupPlacementCallback(placePopup);

然后就是需要自己调试那两个point了,必要的时候可以试试Popup的width属性,同样可以设置位置,亲测。

 

最后一切完毕就有最开始的效果了。

posted @ 2020-06-09 18:12  BruceNeter  阅读(789)  评论(0编辑  收藏  举报