WPF Popup实现拖动

问题一.popup总是置顶,遮挡其他窗口

最近发现popup设置打开后,总是会遮挡其他窗口,而我们只想让它仅仅在应用程序的上一层即可,并不像让它在最上面

解决方案1:

继承Popup重新定义控件PopupEx,代码如下:

public class PopupEx : Popup
    {
        public static DependencyProperty TopmostProperty = Window.TopmostProperty.AddOwner(typeof(PopupEx ), 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();
        }
        protected override void OnOpened(EventArgs e)
        {
            UpdateWindow();
        }
        private void UpdateWindow()
        {
            var hwnd = ((HwndSource)PresentationSource.FromVisual(this).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 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
    }

然后在调用该控件的时候设置该PopupEx的属性 Topmost=False即可。

 

我试着用上面这种方法尝试了,但是,然并软,在new该popupEx的时侯设置topmost=false之后,还是会置顶

后来再加上了一句代码即可解决这个问题:就是在将popupEx加入到某个panel或者父容器当中,因为popup的parent的只读的,无法赋值,

所以我就让一个dockpanel.Children.Add(popupEx),配合PopupEx类,就完美解决了该问题

 

 

问题二.popup无法跟随主窗体移动或者更新状态

后来发现这个popup随着窗体移动不会消失,网上的方法就是在主窗体的locationchanged事件和sizechanged事件里面

回调一个函数,代码如下:

  Loaded += (sender, e) =>
              {
                  Window window = Window.GetWindow(this);
                  window.LocationChanged += HandleMovePopup;
                  window.SizeChanged += HandleMovePopup;
              };

 void HandleMovePopup(object sender, EventArgs e)
        {
            if (NewMsgPop != null && NewMsgPop.IsOpen)
            {
                var mi = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                mi.Invoke(NewMsgPop, null);
            }
        }

这种方法是可以解决这个移动窗体的问题。但是,还有更坑爹的事情,当程序不在最顶部,既有其他应用程序在最顶部时,

此时再让popup.isopen=true时,还是会显示在其他应用程序之前。。。。。。。。。。

真是欲哭无泪,最后的解决方案是:改成一个label, 通过设置Gird.SetZindex(label,0)或者Gird.SetZindex(label,2)来控制label的显隐。

前提是将label放进一个Grid里面

至此,这个需求算是完成了,留给我的启发是,以后能不用popup尽量不用。。。。。。。

 

当然如果大家有更好的方法,请告诉我!

 

问题三:Popup如何拖动

有几个思路,第一,是得到鼠标位置,然后通过API发消息给popup通知其修改自己的位置,第二是在mousemove事件里直接修改popup的位置。

第一种方法可参考该博客:http://www.cnblogs.com/lisweden/p/3183476.html

然后介绍第二种方法:

要引用 system.windows.itneractivity.dll

然后新建一个类,代码如下:

  public class popup_dragmove_behavior : Behavior<Popup>
    {
        public bool _mouse_down;
        Point _old_pos;

        Point _orignal_pos;
        double max_vertical_offset;
        public popup_dragmove_behavior()
        {

        }
        protected override void OnAttached()
        {
            base.OnAttached();

            AssociatedObject.MouseLeftButtonDown += (sender, e) =>
            {
                mouse_left_button_down(sender, e);
            };
            AssociatedObject.MouseLeftButtonUp += (sender, e) =>
            {
                mouse_left_button_up(sender, e);
            };
            AssociatedObject.MouseMove += (sender, e) =>
            {
                mouse_move(sender, e);
            };
            AssociatedObject.Closed += (sender, e) =>
            {
                popup_close(sender, e);
            };
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.MouseLeftButtonDown -= (sender, e) =>
            {
                mouse_left_button_down(sender, e);
            };
            AssociatedObject.MouseLeftButtonUp -= (sender, e) =>
            {
                mouse_left_button_up(sender, e);
            };
            AssociatedObject.MouseMove -= (sender, e) =>
            {
                mouse_move(sender, e);
            };
            AssociatedObject.Closed -= (sender, e) =>
            {
                popup_close(sender, e);
            };
        }

        void mouse_left_button_down(Object sender, MouseButtonEventArgs e)
        {
            _mouse_down = true;
            if (AssociatedObject.VerticalOffset == 0)
            {
                _orignal_pos = AssociatedObject.Child.PointToScreen(new Point(AssociatedObject.ActualWidth, 0));
                max_vertical_offset = 0 - _orignal_pos.Y;
            }
            _old_pos = AssociatedObject.Child.PointToScreen(e.GetPosition(AssociatedObject.Child));
            AssociatedObject.Child.CaptureMouse();
        }

        void mouse_move(Object sender, MouseEventArgs e)
        {
            if (!_mouse_down)
            {
                return;
            }
            var child_pos = e.GetPosition(AssociatedObject.Child);
            var new_pos = AssociatedObject.Child.PointToScreen(child_pos);
            var offset = new_pos - _old_pos;
            _old_pos = new_pos;
            AssociatedObject.HorizontalOffset += offset.X;
            var new_VerticalOffset = AssociatedObject.VerticalOffset + offset.Y;
            if (new_VerticalOffset < max_vertical_offset)
            {
                new_VerticalOffset = max_vertical_offset;
            }
            AssociatedObject.VerticalOffset = new_VerticalOffset;
        }

        void mouse_left_button_up(Object sender, MouseButtonEventArgs e)
        {
            _mouse_down = false;
            AssociatedObject.Child.ReleaseMouseCapture();
        }

        void popup_close(Object sender, EventArgs e)
        {
            AssociatedObject.HorizontalOffset = 0;
            AssociatedObject.VerticalOffset = 0;
        }
    }

 

然后这样调用即可:

popup_dragmove_behavior  be=new popup_dragmove_behavior();

be.Attch(your_popup);

 

 

但是这个方法有个缺点是,无法将popup拖出屏幕之外

总结:除非真的非popup不可,那么一般情况下,直接用window会省事很多

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
WPF中的Popup是一种特殊的控件,它可以显示在其他控件上方,并且可以在需要时弹出和隐藏。Popup的样式可以通过重写来自定义。 首先,在WPF中定义Popup的样式需要使用XAML语言。可以在Window或者Page的资源中定义样式,也可以在单独的资源文件中定义。 我们可以通过为Popup定义一个控制模板来重写其样式。控制模板是一种描述控件外观和行为的XAML标记。 以下是一个简单的示例,展示了如何重写Popup的样式: ```xaml <Style TargetType="Popup"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Popup"> <Grid Background="Transparent"> <!-- 在此处定义你想要的Popup的样式 --> <Border Background="White" BorderBrush="Gray" BorderThickness="1" CornerRadius="5"> <ContentPresenter /> </Border> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> ``` 在这个示例中,我们重写了Popup的样式,使其在显示时以圆角灰色边框包裹内容。可以根据需要自定义Border的属性,如背景色、边框颜色、边框粗细等。 当我们使用自定义样式的Popup控件时,只需要将该样式应用到需要的控件上即可: ```xaml <Button Content="点击弹出Popup"> <Button.Resources> <Style TargetType="Popup" BasedOn="{StaticResource {x:Type Popup}}"> <!-- 在这里进一步修改样式,如果有需要 --> </Style> </Button.Resources> <Button.Triggers> <EventTrigger RoutedEvent="Button.Click"> <BeginStoryboard> <Storyboard> <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen"> <DiscreteBooleanKeyFrame KeyTime="0" Value="True" /> </BooleanAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Button.Triggers> <Button.ContentTemplate> <DataTemplate> <Popup PlacementTarget="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Button}}" Placement="Bottom"> <!-- 在这里放置Popup的内容 --> <TextBlock Text="这是一个Popup" Margin="5" /> </Popup> </DataTemplate> </Button.ContentTemplate> </Button> ``` 通过上述方式,我们可以重写WPF中的Popup样式,实现更加个性化的外观效果。同时,通过定义控制模板,我们还可以进一步自定义Popup的布局和动画效果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值