当WPF弹出窗口的锚元素移动时,如何移动它?

当WPF弹出窗口的锚元素移动时,如何移动它?,wpf,popup,binding,Wpf,Popup,Binding,我有一个定义如下的弹出窗口: <Popup Name="myPopup" StaysOpen="True" Placement="Bottom" PlacementRectangle="0,20,0,20" PlacementTarget="{Binding ElementName=myPopupAnchor}"> <TextBlock ... /> </Popup> 我已经为事件MouseEnter和Mo

我有一个定义如下的弹出窗口:

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

我已经为事件
MouseEnter
MouseLeave
myPopupAnchor
元素添加了事件处理程序。两个事件处理程序切换弹出窗口的可见性

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

我正在寻找解决这个问题的方法,我想要一个移动的弹出窗口。我是否可以通知WPF,
PlacementTarget
绑定已更改,应该重新读取?我可以手动设置弹出窗口的位置吗


目前,我有一个非常粗糙的解决方法,包括关闭然后再次打开弹出窗口,这会导致一些重新绘制问题。

您不能这样做。当弹出窗口显示在屏幕上时,如果父窗口被重新定位,它不会重新定位自身。这就是弹出控件的行为。 选中此项:


您可以使用窗口(WindowStyle=None)代替弹出窗口来解决问题。

下载弹出位置示例,网址为:

代码示例将CustomPopUpplace类与Rect对象一起使用,并绑定到水平和垂直偏移以移动弹出窗口

<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}"

我查看了一些选项和示例。对我来说,最有效的方法是“碰撞”一个属性,它会导致弹出窗口自行重新定位。我使用的属性是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;
    };
}
移动窗口时,弹出窗口将重新定位。没有注意到水平偏移的细微变化,因为窗口和弹出窗口已经在移动了


我仍在评估弹出控件是否是在其他交互过程中保持打开状态的情况下的最佳选择。我认为对于某些场景来说,这似乎是一种很好的方法。

如果您想移动弹出窗口,有一个简单的技巧:更改其位置,然后设置:

IsOpen = false;
IsOpen = true;

为了补充上面NathanAW的伟大解决方案,我想我应该指出一些上下文,比如在本例中C代码的位置。我还是WPF的新手,所以一开始我很难弄清楚把NathanAW的代码放在哪里。当我尝试将该代码放入承载弹出窗口的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>

我修改了Jason的代码,因为如果窗口没有激活,弹出窗口已经在前台了。弹出类中是否有任何选项,或者我的解决方案是否正确

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;
    }

再加上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);
    }
公共静态T FindLogicalParentOf(DependencyObject子对象),其中T:FrameworkElement
{
DependencyObject父级=LogicalTreeHelper.GetParent(子级);
//树梢
if(parent==null)返回null;
T parentWindow=作为T的父项;
if(parentWindow!=null)
{
返回父窗口;
}
//爬上台阶
返回FindLogicalParentOf(父级);
}

调用此帮助程序方法而不是
Window.GetWindow()
,然后继续Jason关于订阅正确事件的回答。对于ScrollViewer,它是ScrollChanged事件。

属性更改时弹出窗口是否移动?或者它们只是在弹出窗口最初显示时读取的?弹出窗口移动。下载该示例并进行尝试。我尝试了此示例,但在移动窗口时,它似乎不会移动弹出窗口。该示例不再可用:(正如NathanAW所说,当窗口移动时,弹出窗口不会更新其位置。Wtf?可以通过反射来运行/调用私有方法?这并不能给我一种良好的感觉…在这一点上它对我来说很方便…但不知何故,我觉得这样做很奇怪…我的意思是
私有
有什么原因吗…@Marcel Benthin是的。),但您不应该在私有方法上使用明显的名称,也可以混淆它们。这里的私有并不意味着安全,也不是这样。它只是一种建立干净接口并隐藏调用方不应该关心的内容(或者在某些情况下可能有危险的内容)的方法。如果您的安全性依赖于成员隐私,那么您就有麻烦了。我认为,在这种情况下,调用内部方法Reposition()更安全。根据,它在调用UpdatePosition()之前会进行一些检查.@tom.maruska您的链接已断开,新链接是:您考虑过的装饰层方法有任何更新吗?我已经在很多方面使用了装饰层,并发现它工作得很好。装饰层有点挑战性,需要一些实验,但总的来说似乎是一个不错的选择。+1@NathanAW我希望我能+gig您的ans答:很好。我阅读了整个
组合框
实现。但我不知道它如何移动
弹出框
。虽然没有关于
组合框
如何移动
弹出框
的指导,但你有什么想法吗?@Javad_阿米里,我希望我知道,但我不确定。我怀疑大多数人都逃避了这个事实
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;
    }
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);
    }