web-dev-qa-db-ja.com

アンカー要素が移動したときにWPFポップアップを移動するにはどうすればよいですか?

このように定義されたポップアップがあります:

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

イベントmyPopupAnchorおよびMouseEnterMouseLeave要素にイベントハンドラーを追加しました。 2つのイベントハンドラーは、ポップアップの表示を切り替えます。

私の問題は、myPopupAnchorの位置が、ポップアップが最初に表示されるか、非表示になってから再び表示されるときにのみ読み取られることです。アンカーが移動しても、ポップアップは移動しません。

私はこれを回避する方法を探しています、私は感動的なポップアップが欲しいです。 PlacementTargetバインディングが変更され、再度読み取る必要があることをWPFに通知できますか?ポップアップの位置を手動で設定できますか?

現在、非常に大雑把な回避策があり、ポップアップを閉じてから再び開くことで、再描画の問題が発生します。

42
Mizipzor

いくつかのオプションとサンプルを確認しました。私にとって最も効果があると思われるのは、Popupがそれ自体で位置を変更するプロパティの1つを「バンプ」することです。使用したプロパティは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;
    };
}

ウィンドウを移動すると、ポップアップが再配置されます。ウィンドウとポップアップはとにかくすでに移動しているため、Horizo​​ntalOffsetの微妙な変更は気づかれません。

他の操作中にコントロールが開いたままの場合、ポップアップコントロールが最適なオプションであるかどうかをまだ評価しています。 このようなものをAdornerレイヤーに入れるというレイ・バーンズの提案 は、いくつかのシナリオでは良いアプローチのように思えます。

72
NathanAW
    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);
        }
    }
21
AQL.exe

Jason Frankの答えに追加すると、最終的にWPF UserControlがWinForms ElementHostでホストされている場合、Window.GetWindow()アプローチは機能しません。私が見つける必要があるのは、スクロールバーを表示する要素であるため、ユーザーコントロールが配置された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イベントです。

3
zemien

ポップアップを移動したい場合は、簡単なトリックがあります:その位置を変更してから、設定します:

IsOpen = false;
IsOpen = true;
3
Aurelien Ribon

ウィンドウがアクティブ化されていない場合、ポップアップはすでにフォアグラウンドにあるため、Jasonからコードを変更しました。 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;
    }
2
Gigabyte

これはできません。 Popupが画面に表示されるとき、その親が再配置されても、ポップアップ自体は再配置されません。これがPopupコントロールの動作です。これを確認してください: http://msdn.Microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx

popupの代わりにWindow(with WindowStyle = None)を使用して問題を解決できます。

0
viky