当它的锚元素移动时,如何移动WPF Popup?

我有一个像这样定义的Popup:

<Popup Name="myPopup" StaysOpen="True" Placement="Bottom" PlacementRectangle="0,20,0,20" PlacementTarget="{Binding ElementName=myPopupAnchor}"> <TextBlock ... /> </Popup> 

我已经添加了事件处理程序到MouseEnterMouseLeave事件的myPopupAnchor元素。 两个事件处理程序切换popup窗口的可见性。

我的问题是myPopupAnchor的位置只在popup窗口第一次显示时被读取,或者被隐藏然后再次显示。 如果锚点移动,popup不会。

我正在寻找解决方法,我想要一个移动的Popup。 我可以通知WPF的PlacementTarget绑定已经改变,应该再次阅读? 我可以手动设置popup窗口的位置吗?

目前,我有一个非常粗糙的解决方法,涉及closures,然后再次打开popup,这会导致一些重新绘制的问题。

我看了一些选项和样品。 似乎最适合我的东西是“碰撞”导致Popup自行重新定位的属性之一。 我使用的属性是Horizo​​ntalOffset。

我把它设置为(自己+ 1),然后将其设置回原来的值。 我在窗口重新定位时运行的事件处理程序中执行此操作。

 // Reference to the PlacementTarget. DependencyObject myPopupPlacementTarget; // Reference to the popup. Popup myPopup; Window w = Window.GetWindow(myPopupPlacementTarget); if (null != w) { w.LocationChanged += delegate(object sender, EventArgs args) { var offset = myPopup.HorizontalOffset; myPopup.HorizontalOffset = offset + 1; myPopup.HorizontalOffset = offset; }; } 

当窗口移动时,popup窗口将重新定位。 Horizo​​ntalOffset的细微变化没有被注意到,因为窗口和popup窗口已经在移动了。

我还在评估在其他交互期间控件保持打开的情况下,popup式控件是否是最佳select。 我在想, Ray Burnsbuild议把这个东西放到Adorner层看起来是一些很好的方法。

  private void ppValues_Opened(object sender, EventArgs e) { Window win = Window.GetWindow(YourControl); win.LocationChanged += new EventHandler(win_LocationChanged); } void win_LocationChanged(object sender, EventArgs e) { if (YourPopup.IsOpen) { var mi = typeof(Popup).GetMethod("UpdatePosition", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); mi.Invoke(YourPopup, null); } } 

为了增加NathanAW的上述优秀解决scheme ,我想我会指出一些上下文 ,比如在这种情况下放置C#代码的位置。 对WPF来说,我还是个新手,所以我开始努力弄清楚把NathanAW的代码放在哪里。 当我试图把代码放在托pipe我的Popup的UserControl的构造函数中时, Window.GetWindow()总是返回Null (所以“Bump”代码从不执行)。 所以我认为其他的新手可能会从上下文中看到事物。

在上下文中显示C#之前,下面是一些示例XAML上下文,以显示一些相关的元素及其名称:

 <UserControl x:Class="MyNamespace.View1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <TextBlock x:Name="popupTarget" /> <Popup x:Name="myPopup" Placement="Bottom" PlacementTarget="{Binding ElementName=popupTarget}" > (popup content here) </Popup> </UserControl> 

然后,在代码隐藏中,为了避免让Window.GetWindow()返回Null ,请联系Loaded事件的处理程序以容纳NathanAW的代码(请参阅Peter Walke对类似的stackoverflow讨论的评论 )。 下面是我的UserControl代码中的所有内容:

 public partial class View1 : UserControl { // Constructor public View1() { InitializeComponent(); // Window.GetWindow() will return Null if you try to call it here! // Wire up the Loaded handler instead this.Loaded += new RoutedEventHandler(View1_Loaded); } /// Provides a way to "dock" the Popup control to the Window /// so that the popup "sticks" to the window while the window is dragged around. void View1_Loaded(object sender, RoutedEventArgs e) { Window w = Window.GetWindow(popupTarget); // w should not be Null now! if (null != w) { w.LocationChanged += delegate(object sender2, EventArgs args) { var offset = myPopup.HorizontalOffset; // "bump" the offset to cause the popup to reposition itself // on its own myPopup.HorizontalOffset = offset + 1; myPopup.HorizontalOffset = offset; }; // Also handle the window being resized (so the popup's position stays // relative to its target element if the target element moves upon // window resize) w.SizeChanged += delegate(object sender3, SizeChangedEventArgs e2) { var offset = myPopup.HorizontalOffset; myPopup.HorizontalOffset = offset + 1; myPopup.HorizontalOffset = offset; }; } } } 

如果你想移动popup窗口,有一个简单的技巧:改变它的位置,然后设置:

 IsOpen = false; IsOpen = true; 

我从Jason修改了代码,因为如果窗口未被激活,Popup已经在前台。 在popup式课程中有任何选项,或者我是我的解决scheme好吗?

 private void FullLoaded(object sender, RoutedEventArgs e) { Window CurrentWindow = Window.GetWindow(this.Popup); if (CurrentWindow != null) { CurrentWindow.LocationChanged += (object innerSender, EventArgs innerArgs) => { this.RedrawPopup(); }; CurrentWindow.SizeChanged += (object innerSender, SizeChangedEventArgs innerArgs) => { this.RedrawPopup(); }; CurrentWindow.Activated += (object innerSender, EventArgs innerArgs) => { if (this.m_handleDeActivatedEvents && this.m_ShowOnActivated) { this.Popup.IsOpen = true; this.m_ShowOnActivated = false; } }; CurrentWindow.Deactivated += (object innerSender, EventArgs innerArgs) => { if (this.m_handleDeActivatedEvents && this.Popup.IsOpen) { this.Popup.IsOpen = false; this.m_ShowOnActivated = true; } }; } } private void RedrawPopup() { double Offset = this.Popup.HorizontalOffset; this.Popup.HorizontalOffset = Offset + 1; this.Popup.HorizontalOffset = Offset; } 

你不可以做这个。 当popup窗口显示在屏幕上时,如果其父对象被重新定位,它不会重新定位。 那就是Popup控件的行为。 检查此: http : //msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx

你可以使用Window(WindowStyle = None)而不是Popup来解决你的问题。

在以下位置下载popup式popup窗口示例:

http://msdn.microsoft.com/en-us/library/ms771558(v=VS.90).aspx

代码示例使用具有Rect对象的类CustomPopupPlacement,并绑定到水平和垂直偏移量来移动popup窗口。

 <Popup Name="popup1" Placement="Bottom" AllowsTransparency="True" IsOpen="{Binding ElementName=popupOpen, Path=IsChecked}" HorizontalOffset="{Binding ElementName=HOffset, Path=Value, Mode=TwoWay}" VerticalOffset="{Binding ElementName=VOffset, Path=Value, Mode=TwoWay}" 

要添加到Jason Frank的答案,如果WPF UserControl最终托pipe在WinForms ElementHost中,则Window.GetWindow()方法将不起作用。 我需要find的是我的UserControl所在的ScrollViewer,因为这是显示滚动条的元素。

这个通用的recursion方法(修改掉另一个答案)将有助于在逻辑树中find特定types的父(也可以使用可视化树),如果find则返回它。

 public static T FindLogicalParentOf<T>(DependencyObject child) where T: FrameworkElement { DependencyObject parent = LogicalTreeHelper.GetParent(child); //Top of the tree if (parent == null) return null; T parentWindow = parent as T; if (parentWindow != null) { return parentWindow; } //Climb a step up return FindLogicalParentOf<T>(parent); } 

调用这个帮助器方法而不是Window.GetWindow()并继续Jason的订阅正确事件的答案。 在ScrollViewer的情况下,它是ScrollChanged事件。