当锚元素移动时,如何移动 WPF 弹出窗口?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1600218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I move a WPF Popup when its anchor element moves?
提问by Mizipzor
I have a Popup defined like this:
我有一个这样定义的弹出窗口:
<Popup
Name="myPopup"
StaysOpen="True"
Placement="Bottom"
PlacementRectangle="0,20,0,20"
PlacementTarget="{Binding ElementName=myPopupAnchor}">
<TextBlock ... />
</Popup>
I have added event handlers to the myPopupAnchor
element for the events MouseEnter
and MouseLeave
. The two event handlers toggle the popup's visibility.
我已将事件处理程序添加到myPopupAnchor
事件MouseEnter
和MouseLeave
. 这两个事件处理程序切换弹出窗口的可见性。
My problem is the position of myPopupAnchor is only read when the popup is first shown, or hidden and then shown again. If the anchor moves, the popup does not.
我的问题是 myPopupAnchor 的位置仅在弹出窗口首次显示或隐藏然后再次显示时读取。如果锚点移动,则弹出窗口不会移动。
I'm looking for ways to work around this, I want a moving Popup. Can I notify WPF that the PlacementTarget
binding has changed and should be read again? Can I manually set the popup's position?
我正在寻找解决此问题的方法,我想要一个移动的弹出窗口。我可以通知 WPFPlacementTarget
绑定已更改并应再次读取吗?我可以手动设置弹出窗口的位置吗?
Currently, I have a very crude workaround that involves closing and then opening the popup again, which causes some repainting issues.
目前,我有一个非常粗略的解决方法,包括关闭然后再次打开弹出窗口,这会导致一些重绘问题。
回答by NathanAW
I looked at a couple options and samples out there. The thing that seems to work best for me is to "bump" one of the properties that causes the Popup to reposition itself on its own. The property that I used is HorizontalOffset.
我查看了一些选项和示例。对我来说似乎最有效的方法是“碰撞”导致 Popup 自行重新定位的属性之一。我使用的属性是 HorizontalOffset。
I set it to (itself + 1) and then set it back the original value. I do this in an event handler that runs when the window is repositioned.
我将它设置为(本身+ 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;
};
}
When the window is moved, the popup will reposition. The subtle change in the HorizontalOffset is not noticed because the window and popup are already moving anyway.
当窗口移动时,弹出窗口将重新定位。HorizontalOffset 的细微变化没有被注意到,因为无论如何窗口和弹出窗口已经在移动。
I'm still evaluating whether a popup control is the best option in cases where the control stays open during other interaction. I'm thinking that Ray Burns suggestion to put this stuff in an Adorner layerseems like a good approach for some scenarios.
我仍在评估弹出控件是否是控件在其他交互期间保持打开状态的最佳选择。我认为Ray Burns 建议将这些东西放在 Adorner 层中似乎是某些场景的好方法。
回答by Jason Frank
Just to add on to NathanAW's great solutionabove, I thought I'd point out some context, such as where to place the C# code in this case. I'm still pretty new to WPF so I struggled at first to figure out where to put NathanAW's code. When I tried putting that code in the constructor for the UserControl that hosted my Popup, Window.GetWindow()
always returned Null
(so the "bump" code never executed). So I thought that other newbies might benefit from seeing things in context.
只需添加到NathanAW的伟大的解决方案上面,我想我会指出一些方面,如 这里放置C#代码在这种情况下。我对 WPF 还是很陌生,所以一开始我很难弄清楚将 NathanAW 的代码放在哪里。当我尝试将该代码放入承载我的弹出窗口的 UserControl 的构造函数中时,Window.GetWindow()
总是返回Null
(因此“凹凸”代码从未执行)。所以我认为其他新手可能会从在上下文中看待事物中受益。
Before showing the C# in context, here's some example XAML context to show some relevant elements and their names:
在上下文中显示 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>
Then in the code-behind, to avoid having Window.GetWindow()
return Null
, wire up a handler to the Loaded event to house NathanAW's code (see Peter Walke's commenton a similar stackoverflow discussion for example). Here's exactly how it all looked in my UserControl code-behind:
然后在代码隐藏中,为了避免Window.GetWindow()
return 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;
};
}
}
}
回答by AQL.exe
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);
}
}
回答by zemien
To add to Jason Frank's answer, the Window.GetWindow()
approach wouldn't work if the WPF UserControl is ultimately hosted in an WinForms ElementHost. What I needed to find was the ScrollViewer that my UserControl was placed in, as that was the element showing the scrollbars.
要添加到 Jason Frank 的答案,Window.GetWindow()
如果 WPF UserControl 最终托管在 WinForms ElementHost 中,则该方法将不起作用。我需要找到的是放置我的 UserControl 的 ScrollViewer,因为它是显示滚动条的元素。
This generic recursive method (modified off another answer) will help find the parent of a particular type in the logical tree (it's possible to use the visual tree too), and return it if found.
这种通用递归方法(修改了另一个答案)将有助于在逻辑树中找到特定类型的父级(也可以使用可视化树),如果找到则返回它。
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);
}
Call this helper method instead of Window.GetWindow()
and continue with Jason's answer of subscribing to the right events. In the case of ScrollViewer, it's the ScrollChanged event instead.
调用此辅助方法而不是Window.GetWindow()
并继续 Jason 的订阅正确事件的回答。在 ScrollViewer 的情况下,它是 ScrollChanged 事件。
回答by Aurelien Ribon
If you want to move the popup, there is a simple trick : change its position,then set :
如果你想移动弹出窗口,有一个简单的技巧:改变它的位置,然后设置:
IsOpen = false;
IsOpen = true;
回答by Gigabyte
I modified the Code from Jason, because the Popup is already in Foreground if the Window is not Activated. Is there any Option in the Popup class or i is my solution ok?
我修改了 Jason 的代码,因为如果窗口未激活,则弹出窗口已经在前台。Popup 类中是否有任何 Option 或者我的解决方案可以吗?
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;
}
回答by viky
You can not do this. When Popup is displayed on the screen, it does not reposition itself if its parent is repositioned. Thats the behavior of Popup control. check this: http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx
你不可以做这个。当 Popup 显示在屏幕上时,如果重新定位其父项,它不会重新定位自身。这就是 Popup 控件的行为。检查这个:http: //msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx
you can use a Window(with WindowStyle=None) instead of Popup that may solve your problem.
您可以使用 Window(with WindowStyle=None) 而不是 Popup 来解决您的问题。
回答by Zamboni
Download the Popup Popup Position Sample at:
在以下位置下载弹出弹出位置示例:
http://msdn.microsoft.com/en-us/library/ms771558(v=VS.90).aspx
http://msdn.microsoft.com/en-us/library/ms771558(v=VS.90).aspx
The code sample uses the class CustomPopupPlacement with a Rect object, and binds to horizontal and vertical offsets to move the Popup.
代码示例使用类 CustomPopupPlacement 和一个 Rect 对象,并绑定到水平和垂直偏移以移动 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}"