我有一个像这样定义的Popup:
<Popup
Name="myPopup"
StaysOpen="True"
Placement="Bottom"
PlacementRectangle="0,20,0,20"
PlacementTarget="{Binding ElementName=myPopupAnchor}">
<TextBlock ... />
</Popup>
我已为事件myPopupAnchor
和MouseEnter
的{{1}}元素添加了事件处理程序。两个事件处理程序切换弹出窗口的可见性。
我的问题是myPopupAnchor的位置仅在首次显示弹出窗口时被读取,或者被隐藏然后再次显示。如果锚移动,则弹出窗口不会。
我正在寻找解决这个问题的方法,我想要一个动人的Popup。我是否可以通知WPF MouseLeave
绑定已更改并应再次阅读?我可以手动设置弹出窗口的位置吗?
目前,我有一个非常粗略的解决方法,涉及关闭然后再次打开弹出窗口,这会导致一些重新绘制问题。
答案 0 :(得分:71)
我看了几个选项和样本。对我来说似乎最有用的事情是“碰撞”导致Popup自行重新定位的属性之一。我使用的属性是HorizontalOffset。
我将其设置为(本身+ 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;
};
}
移动窗口后,弹出窗口将重新定位。不会注意到HorizontalOffset中的细微变化,因为窗口和弹出窗口仍在移动。
我还在评估在其他交互过程中控件保持打开的情况下,弹出控件是否是最佳选项。我认为Ray Burns suggestion to put this stuff in an Adorner layer对于某些场景来说似乎是一种很好的方法。
答案 1 :(得分:22)
仅仅添加到NathanAW的优秀解决方案,我认为我指出了一些背景,例如 其中 在这种情况下放置C#代码。我还是WPF的新手,所以我一开始就想弄清楚NathanAW的代码放在哪里。当我尝试将该代码放在托管我的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's comment关于类似的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;
};
}
}
}
答案 2 :(得分:20)
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);
}
}
答案 3 :(得分:3)
如果你想移动弹出窗口,有一个简单的技巧:改变它的位置,然后设置:
IsOpen = false;
IsOpen = true;
答案 4 :(得分:3)
要添加到Jason Frank的答案,如果WPF UserControl最终托管在WinForms ElementHost中,Window.GetWindow()
方法将不起作用。我需要找到的是我的UserControl所在的ScrollViewer,因为它是显示滚动条的元素。
这种通用的递归方法(修改了另一个答案)将有助于在逻辑树中找到特定类型的父级(也可以使用可视树),如果找到则返回它。
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事件。
答案 5 :(得分:1)
我从Jason修改了代码,因为如果窗口未激活,Popup已经在Foreground中。 Popup类中是否有任何选项,或者我的解决方案是否正确?
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;
}
答案 6 :(得分:0)
你不能这样做。当弹出窗口显示在屏幕上时,如果其父级被重新定位,则它不会重新定位。这是Popup控件的行为。 检查一下:http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx
您可以使用Window(使用WindowStyle = None)代替Popup来解决您的问题。
答案 7 :(得分:-1)
在以下位置下载弹出式弹出位置示例
http://msdn.microsoft.com/en-us/library/ms771558(v=VS.90).aspx
代码示例使用带有Rect对象的CustomPopupPlacement类,并绑定到水平和垂直偏移以移动弹出窗口。
<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}"