2012-03-21 8 views
12

Próbuję wyśrodkować okno do okna właściciela. Potrzebuję również okna podrzędnego, aby poruszać się wzdłuż okna właściciela. Cross-post na forum MSDN WPF można znaleźć here.Górne i lewe wartości okna nie są poprawnie aktualizowane podczas maksymalizowania okna w .NET 4

Aby to osiągnąć, subskrybuję zdarzenia LocationChanged i SizeChanged (a także zdarzenie StateChanged) właściciela mojego okna podrzędnego. Po uruchomieniu tych zdarzeń ponownie obliczam położenie okna potomnego. Robię to w kodzie z tyłu okna podrzędnego.

Kod jest bardzo prosta:

Top = Owner.Top + ((Owner.ActualHeight - ActualHeight)/2); 
Left = Owner.Left + ((Owner.ActualWidth - ActualWidth)/2); 

Jeśli skompilować i uruchomić program próbki I pod warunkiem widać, że to działa, gdy główne okno jest jak jest i przemieszczać. Ta część działa.

Problem pojawia się, gdy okno właściciela jest zmaksymalizowane. (Po zmaksymalizowaniu, przywróć normalne.) Ponieważ subskrybuję trzy zdarzenia, trzy razy wchodzę w funkcję przenoszenia. Po wydrukowaniu danych właściciela otrzymuję inne wyniki. Najbardziej irytujące wartości Top i Left okna właściciela są wyłączone. Wydaje się, że otrzymuje poprawne wartości górny i lewy, gdy stan się zmienia, ale wartości ActualWidth i ActualHeight są nieprawidłowe. Po wywołaniu zdarzeń LocationChanged lub wartości są w porządku, ale wartości Góra i Lewica są niepoprawne. Wygląda na to, że są to poprzednie wartości. Jak to może być? Co to powoduje? I czy jest na to poprawna poprawka?

Ponieważ ten sam kod wydawał się działać w .net 3.5. Mam wrażenie, że coś zmieniło się w .net 4. (Albo miałem dziwny problem z timingiem, który spowodował, że problem się nie pojawił.) Ale nie mogę znaleźć żadnego udokumentowana zmiana w tej części.

.NET 3.5:

OnOwnerLocationChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerStateChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerSizeChanged 
T: -8; L: -8; W: 1936; H: 1066 

.NET 4.0:

OnOwnerLocationChanged 
T: -8; L: -8; W: 640; H: 480 
OnOwnerStateChanged 
T: 494; L: 33; W: 640; H: 480 
OnOwnerSizeChanged 
T: 494; L: 33; W: 1936; H: 1066 

Tak więc głównym pozostaje pytanie: dlaczego to szczyt i wartości lewej właściciela błędna?

+0

znalazłem wątku na SO, który wspomina, jak złapać maximiaze/przywracaj zdarzenia przez przesłonięcie WndProc: http://stackoverflow.com/questions/1295999/event-when-a-window-gets-maximized-un-maximized Wypróbuj kod w odpowiedzi http://stackoverflow.com/a/3382336/137972 – Christoffer

+1

Od czasu T i L od zdarzenia LocationChanged jest poprawny Mogę je przechowywać w pamięci podręcznej i używać W i H z zdarzenia SizeChanged, ponieważ zdarzenie to przychodzi nawet POZ LocationChanged, ale tak jak twoja sugestia, jest to obejście tego problemu. Wolałbym teraz, dlaczego to zachowanie jest inne. – Jensen

+0

Wykonuję te obliczenia w dwóch oknach, na dyspozytorach różnic i jestem bardzo wdzięczny, że natknąłem się na ten post! W końcu użyłem również metody buforowania. – RichardOD

Odpowiedz

12

comment z Mataniko dotyczące kwestii migracji w .NET 4.0 był prawidłowy. Ponieważ mój kod działał, gdy WindowState ustawiono na Normal, mogłem to zatrzymać. Po prostu musiałem coś przewidzieć, kiedy WindowState było.

Zaimplementowałem natywną funkcję GetWindowRect(), aby to osiągnąć, ponieważ może to dać mi właściwe wymiary.

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 
} 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 

// Make sure RECT is actually OUR defined struct, not the windows rect. 
public static RECT GetWindowRectangle(Window window) 
{ 
    RECT rect; 
    GetWindowRect((new WindowInteropHelper(window)).Handle, out rect); 

    return rect; 
} 

Następnie, gdy jest MaximizedOwner.WindowState, używamy funkcji GetWindowRectangle uzyskać rzeczywiste wymiary. W tej chwili nie przejmujemy się granicą, ale w razie potrzeby można ją włączyć za pomocą funkcji GetSystemMetrics.

if (Owner.WindowState == WindowState.Maximized) 
{ 
    var rect = GetWindowRectangle(Owner); 

    Top = rect.Top + ((rect.Bottom - ActualHeight)/2); 
    Left = rect.Left + ((rect.Right - ActualWidth)/2); 
} 
2

Nie wiem, czy to błąd w .NET 4.0 lub zamierzonego zachowania. Podejrzewam, że pomiędzy zarejestrowanymi zdarzeniami i strzelaniem istnieje warunek wyścigu. Mimo że LocationChanged jest uruchamiany jako pierwszy, SizeChanged jest rejestrowany jako pierwszy z wciąż niepoprawnymi wartościami Lokalizacji. Można łatwo obejść ten problem, tworząc zmienną lokalną w oknie podrzędnym, która rejestruje pozycję Właściciel u góry i lewo w zdarzeniu LocationChanged.

Przykład:

private Point _ownerLocation; 

private void OnOwnerLocationChanged(object sender, EventArgs e) 
    { 
     Console.WriteLine("OnOwnerLocationChanged"); 
     _ownerLocation = new Point(Owner.Top, Owner.Left); 
     SetLocationToOwner(); 
    } 

private void SetLocationToOwner() 
    { 
     if (IsVisible && (Owner != null)) 
     { 
      Console.WriteLine("T: {0}; L: {1}; W: {2}; H: {3}", Owner.Top, Owner.Left, Owner.ActualWidth, Owner.ActualHeight); 

      Top = _ownerLocation.X + ((Owner.ActualHeight - ActualHeight)/2); 
      Left = _ownerLocation.Y + ((Owner.ActualWidth - ActualWidth)/2); 
     } 
    } 
+1

jeszcze jedno: LocationChanged jest również zwolniony, gdy zmienia WindowState, nie trzeba zarejestruj się w zdarzeniu StateChanged i używaj tylko wartości z góry z lewej od wartości LocationChanged i Width/Height z SizeChanged. Źródło: [MSDN] (http://msdn.microsoft.com/en-us/library/system.windows.window.locationchanged.aspx) – Mataniko

+0

To nadal stanowi problem. Jeśli okno właściciela zostało zmaksymalizowane PRZED otworzeniem okna podrzędnego, okno potomne nie może uzyskać przyzwoitej wartości Górny i Lewy, a okno potomne jest ponownie ustawione niepoprawnie. – Jensen

+0

W takim przypadku można zarejestrować zdarzenia SizeChanged/LocationChanged w głównym formularzu i otworzyć formularz podrzędny z tymi wartościami. To błąd, ale wygląda jak regresja w .NET 4.0. Może warto otworzyć raport ze stwardnieniem rozsianym, ponieważ tak się dzieje z .NET 4.5 – Mataniko

0

Czy próbowałeś ..

int left, top, width, height; 
bool maximised; 

if (WindowState == FormWindowState.Maximized) 
{ 
    maximised = true; 
    left = RestoreBounds.X; 
    top = RestoreBounds.Y; 
    width = RestoreBounds.Width; 
    height = RestoreBounds.Height; 
} 
else 
{ 
    maximised = false; 
    left = Left; 
    top = Top; 
    width = Width; 
    height = Height; 
} 

I powrócić (kolejność jest ważna) ...

StartPosition = FormStartPosition.Manual; 
Location = new Point(left, top); 
Size = new Size(width, height); 

if (maximised) 
    WindowState = FormWindowState.Maximized; 
+0

Zgodnie z jego specyfikacją (https://msdn.microsoft.com/en-us/library/system.windows.window.restorebounds(v=vs.110).aspx) RestoreBounds twierdzi, że jest oknem, tuż przed tym, zanim Maksymalizowany lub zminimalizowany. Więc nie byłoby to rozwiązanie problemu. Niestety, RestoreBounds nawet nie daje ci tego, co twierdzi, że przynajmniej nie w moim teście na Windows 7 .NET 4.5 –

+0

Wszystko, co mogę powiedzieć, to to, że działa dla mnie, ale stwierdzenie "jeśli" jest ważne - musisz sprawdź stan okna przed uzyskaniem parametrów. Przypisanie parametrów jest oczywiście odwrotnością. – damichab

+0

Zaktualizowałem odpowiedź w przypadku, gdy ktoś inny miał z nią problemy. Późno wiem! – damichab

Powiązane problemy