Jak mogę przenieść wyskakujące okienko WPF, gdy jego element kotwicy się porusza?
Mam wyskakujące okienko zdefiniowane tak:
<Popup
Name="myPopup"
StaysOpen="True"
Placement="Bottom"
PlacementRectangle="0,20,0,20"
PlacementTarget="{Binding ElementName=myPopupAnchor}">
<TextBlock ... />
</Popup>
Dodałem obsługę zdarzeń do elementu myPopupAnchor
dla zdarzeń MouseEnter
i MouseLeave
. Dwie procedury obsługi zdarzeń przełączają widoczność wyskakującego okienka.
Mój problem polega na tym, że pozycja myPopupAnchor jest odczytywana tylko wtedy, gdy wyskakujące okienko jest najpierw pokazane, lub ukryte, a następnie pokazane ponownie. Jeśli Kotwica się porusza, wyskakujące okienko nie.
Szukam sposobu, aby obejść to, chcę ruchome Popup. Czy Mogę zawiadomić WPF o zmianie wiązania PlacementTarget
i powinno się czytać jeszcze raz? Czy mogę ręcznie ustawić pozycję wyskakującego okienka?
Obecnie mam bardzo prymitywne obejście, które obejmuje zamknięcie, a następnie ponowne otwarcie wyskakującego okienka, co powoduje pewne problemy z przemalowaniem.
8 answers
Spojrzałem na kilka opcji i próbek tam. Rzeczą, która wydaje się działać najlepiej dla mnie jest "podbić" jedną z właściwości, które powodują, że wyskakujące okienko samo się zmienia. Właściwość, której użyłem to HorizontalOffset.
Ustawiam go na (sam + 1), a następnie ustawiam z powrotem oryginalną wartość. Robię to w programie obsługi zdarzeń, który działa, gdy okno jest zmieniane.
// 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;
};
}
Gdy okno zostanie przesunięte, wyskakujące okienko zmieni położenie. Subtelna zmiana w HorizontalOffset nie jest zauważany, ponieważ okno i wyskakujące okienko i tak już się poruszają.
Nadal oceniam, czy kontrola popup jest najlepszą opcją w przypadkach, gdy kontrola pozostaje otwarta podczas innych interakcji. Myślę, że sugestia Raya Burnsa, aby umieścić to w warstwie Adorner wydaje się dobrym podejściem do niektórych scenariuszy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:37
Żeby dodać do tego świetne rozwiązanie powyżej, pomyślałem, że wskażę jakiś kontekst , taki jak gdzie aby w tym przypadku umieścić kod C#. Wciąż jestem całkiem nowy w WPF, więc na początku starałem się wymyślić, gdzie umieścić kod NathanAW. Kiedy próbowałem umieścić ten kod w konstruktorze dla kontrolera UserControl, który hostował moje Popup, Window.GetWindow()
Zawsze zwracał Null
(więc kod "bump" nigdy nie został wykonany). Pomyślałem więc, że inni nowicjusze mogą skorzystać z postrzeganie rzeczy w kontekście.
Przed pokazaniem C# w kontekście, oto przykładowy kontekst XAML pokazujący niektóre istotne elementy i ich nazwy:
<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>
Następnie w kodzie-behind, aby uniknąć Window.GetWindow()
return Null
, Podłącz obsługę do załadowanego zdarzenia, aby umieścić kod Nathanawa(zobacz komentarz Petera Walke na przykład w podobnej dyskusji stackoverflow). Oto dokładnie jak to wszystko wyglądało w moim kodzie UserControl-behind:
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;
};
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41
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);
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-23 14:42:29
Jeśli chcesz przesunąć wyskakujące okienko, istnieje prosta sztuczka: zmień jego pozycję, a następnie Ustaw :
IsOpen = false;
IsOpen = true;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-09 07:16:22
Nie możesz tego zrobić. Gdy wyskakujące okienko jest wyświetlane na ekranie, nie zmienia pozycji, jeśli jego rodzic jest zmieniany. To zachowanie kontroli Popup. sprawdź to: http://msdn.microsoft.com/en-us/library/system.windows.controls.primitives.popup.aspx
Możesz użyć okna (z WindowStyle = None) zamiast Popup, które może rozwiązać twój problem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-05 13:28:03
Zmodyfikowałem kod z Jasona, ponieważ wyskakujące okienko jest już na pierwszym planie, jeśli okno nie jest aktywowane. Czy jest jakaś opcja w klasie Popup, czy moje rozwiązanie jest w porządku?
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-11 11:32:09
Aby dodać do odpowiedzi Jasona Franka, podejście Window.GetWindow()
nie zadziała, jeśli WPF UserControl zostanie ostatecznie hostowany w Elementhostie WinForms. To, co musiałem znaleźć, to ScrollViewer, w którym umieszczono mój Kontroler UserControl, ponieważ był to element pokazujący paski przewijania.
Ta ogólna metoda rekurencyjna (zmodyfikowana z innej odpowiedzi) pomoże znaleźć rodzica określonego typu w drzewie logicznym (możliwe jest również użycie drzewa wizualnego) i zwróci go, jeśli zostanie znaleziony.
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 ta metoda pomocnicza zamiast Window.GetWindow()
i kontynuuje odpowiedź Jasona na odpowiednie zdarzenia. W przypadku ScrollViewer jest to Zdarzenie ScrollChanged.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-10 23:05:40
Pobierz przykładową pozycję wyskakującego okienka pod adresem:
Http://msdn.microsoft.com/en-us/library/ms771558 (v=VS.90).aspx
Próbka kodu używa klasy CustomPopupPlacement z obiektem Rect i wiąże się z poziomymi i pionowymi przesunięciami, aby przesunąć wyskakujące okienko.
<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}"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 14:38:10