Domanda

Ho un Popup definito in questo modo:

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

Ho aggiunto i gestori di eventi all'elemento myPopupAnchor per gli eventi MouseEnter e MouseLeave . I due gestori di eventi attivano la visibilità del popup.

Il mio problema è che la posizione di myPopupAnchor viene letta solo quando il popup viene mostrato per la prima volta o nascosto e quindi mostrato di nuovo. Se l'ancora si sposta, il popup non lo fa.

Sto cercando modi per aggirare questo, voglio un Popup in movimento. Posso comunicare a WPF che l'associazione PlacementTarget è cambiata e deve essere letta di nuovo? Posso impostare manualmente la posizione del popup?

Attualmente, ho una soluzione molto grezza che prevede la chiusura e quindi l'apertura di nuovo del popup, che causa alcuni problemi di riverniciatura.

È stato utile?

Soluzione

Ho guardato un paio di opzioni e campioni là fuori. La cosa che sembra funzionare meglio per me è "sbattere". una delle proprietà che fa riposizionare il Popup da solo. La proprietà che ho usato è HorizontalOffset.

L'ho impostato su (se stesso + 1) e quindi ho ripristinato il valore originale. Lo faccio in un gestore di eventi che viene eseguito quando la finestra viene riposizionata.

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

Quando la finestra viene spostata, il popup verrà riposizionato. Il sottile cambiamento in HorizontalOffset non si nota perché la finestra e il popup si stanno già spostando comunque.

Sto ancora valutando se un controllo popup è l'opzione migliore nei casi in cui il controllo rimane aperto durante altre interazioni. Sto pensando che suggerimento di Ray Burns di mettere questa roba in un Il livello Adorner sembra un buon approccio per alcuni scenari.

Altri suggerimenti

Solo per aggiungere alla grande soluzione di NathanAW sopra, ho pensato di indicare qualche contesto , come dove per inserire il codice C # in questo caso. Sono ancora abbastanza nuovo in WPF, quindi all'inizio ho faticato a capire dove mettere il codice di NathanAW. Quando ho provato a inserire quel codice nel costruttore per UserControl che ospitava il mio Popup, Window.GetWindow () restituiva sempre Null (quindi il codice " bump " mai eseguito) . Quindi ho pensato che altri neofiti potrebbero trarre vantaggio dal vedere le cose nel contesto.

Prima di mostrare il C # nel contesto, ecco alcuni esempi del contesto XAML per mostrare alcuni elementi rilevanti e i loro nomi:

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

Quindi nel code-behind, per evitare di avere Window.GetWindow () restituire Null , collegare un gestore all'evento Loaded per ospitare il codice di NathanAW (vedere < a href = "https://stackoverflow.com/a/304604/718325"> Il commento di Peter Walke su una discussione stackoverflow simile, ad esempio). Ecco esattamente come appariva nel mio code-dietro 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;
            };
        }
    }
}
    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);
        }
    }

Se vuoi spostare il popup, c'è un semplice trucco: cambiarne la posizione, quindi impostare:

IsOpen = false;
IsOpen = true;

Per aggiungere alla risposta di Jason Frank, l'approccio Window.GetWindow () non funzionerebbe se il UserControl di WPF alla fine è ospitato in un ElementHost di WinForms. Quello che dovevo trovare era lo ScrollViewer in cui era posizionato il mio UserControl, poiché quello era l'elemento che mostrava le barre di scorrimento.

Questo metodo ricorsivo generico (modificato da un'altra risposta) aiuterà a trovare il genitore di un particolare tipo nell'albero logico (è possibile usare anche l'albero visivo) e restituirlo se trovato.

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

Chiama questo metodo di supporto invece di Window.GetWindow () e continua con la risposta di Jason di iscriverti agli eventi giusti. Nel caso di ScrollViewer, è invece l'evento ScrollChanged.

Ho modificato il codice da Jason, perché il popup è già in primo piano se la finestra non è attivata. C'è qualche opzione nella classe Popup o la mia soluzione è ok?

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

Non puoi farlo. Quando il popup viene visualizzato sullo schermo, non si riposiziona se il padre viene riposizionato. Questo è il comportamento del controllo popup. controlla questo: http://msdn.microsoft .com / it-it / library / system.windows.controls.primitives.popup.aspx

puoi usare una finestra (con WindowStyle = None) invece di Popup che potrebbe risolvere il tuo problema.

Scarica l'esempio di posizione popup popup in:

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

L'esempio di codice utilizza la classe CustomPopupPlacement con un oggetto Rect e si lega agli offset orizzontali e verticali per spostare il 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}"
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top