2012-05-03 10 views
6

C'è un modo per determinare se una finestra WPF aperta è attualmente visibile in qualsiasi monitor connesso al desktop? Con visibile intendo che il rettangolo dei limiti della finestra si interseca con il rettangolo del desktop di uno qualsiasi dei monitor.Determina se una finestra WPF aperta è visibile su qualsiasi monitor

Ho bisogno di questa funzionalità per determinare se una finestra deve essere riposizionata perché la configurazione del monitor (limiti delle aree di lavoro, conteggio monitor) è cambiata tra riavvii della mia applicazione (che salva le posizioni della finestra).

sono venuto su con il codice qui sotto e sembra funzionare, ma ha diversi problemi:

  1. Ho bisogno di fare riferimento a forme finestre.
  2. Ho bisogno delle impostazioni DPI del desktop e trasformo i pixel reali dei moduli Windows in pixel virtuali WPF.
  3. Ho bisogno di un'istanza Visual acutal che è già stata sottoposta a rendering per eseguire la trasformazione.

Sei a conoscenza di una soluzione che risolve alcuni o tutti i 3 problemi sopra riportati?

using System.Collections.Generic; 
using System.Linq; 
using System.Windows; 
using System.Windows.Forms; 
using System.Windows.Media; 

internal static class Desktop 
{ 
    private static Size dpiFactor = new Size(1.0, 1.0); 
    private static bool isInitialized; 

    public static IEnumerable<Rect> WorkingAreas 
    { 
     get 
     { 
      return 
       Screen.AllScreens.Select(
        screen => 
        new Rect(
         screen.WorkingArea.Left * dpiFactor.Width, 
         screen.WorkingArea.Top * dpiFactor.Height, 
         screen.WorkingArea.Width * dpiFactor.Width, 
         screen.WorkingArea.Height * dpiFactor.Height)); 
     } 
    } 

    public static void TryInitialize(Visual visual) 
    { 
     if (isInitialized) 
     { 
      return; 
     } 

     var ps = PresentationSource.FromVisual(visual); 
     if (ps == null) 
     { 
      return; 
     } 

     var ct = ps.CompositionTarget; 
     if (ct == null) 
     { 
      return; 
     } 

     var m = ct.TransformToDevice; 
     dpiFactor = new Size(m.M11, m.M22); 
     isInitialized = true; 
    } 
} 

Uso del (inizializzato) Desktop classe:

private bool IsLocationValid(Rect windowRectangle) 
    { 
     foreach (var workingArea in Desktop.WorkingAreas) 
     { 
      var intersection = Rect.Intersect(windowRectangle, workingArea); 
      var minVisible = new Size(10.0, 10.0); 
      if (intersection.Width >= minVisible.Width && 
       intersection.Height >= minVisible.Height) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

Aggiornamento

Uso dello schermo virtuale (SystemParameters.VirtualScreen*) non funziona perché quando si utilizzano più monitor il "desktop "non è un semplice rettangolo. Potrebbe essere un poligono. Ci saranno punti ciechi nella schermata virtuale perché

  1. gli schermi collegati possono avere diverse risoluzioni
  2. è possibile configurare la posizione di ogni schermata.
+0

http: // StackOverflow.it/questions/2465646/how-do-i-know-monitor-a-wpf-window-is-in – Flot2011

+0

Sebbene la domanda da quel collegamento sia abbastanza simile, non risponde alla mia domanda. Ho già proposto una soluzione e mi piacerebbe trovare un modo per eliminare esplicitamente i tre problemi che ho menzionato. – bitbonk

+0

Quindi le librerie WPF e C# non possono aiutarti. Potresti provare PInvoke, ma probabilmente è più sporco. Vedi questa domanda per ulteriori informazioni al riguardo: http://stackoverflow.com/questions/1927540/how-to-get-the-size-of-the-current-screen-in-wpf – AkselK

risposta

7

Il codice che utilizziamo per fare qualcosa di simile utilizza informazioni provenienti da SystemParameters, in particolare SystemParameter.VirtualScreenLeft, Top, larghezza e altezza.

Se abbiamo una posizione e dimensione salvato poi determinare se la finestra è fuori dai limiti di questo tipo:

bool outOfBounds = 
    (location.X <= SystemParameters.VirtualScreenLeft - size.Width) || 
    (location.Y <= SystemParameters.VirtualScreenTop - size.Height) || 
    (SystemParameters.VirtualScreenLeft + 
     SystemParameters.VirtualScreenWidth <= location.X) || 
    (SystemParameters.VirtualScreenTop + 
     SystemParameters.VirtualScreenHeight <= location.Y); 
+2

L'uso dello schermo virtuale non funziona perché quando si utilizzano più monitor il "desktop" non è un semplice rettangolo. Ci saranno punti ciechi nello schermo virtuale perché a) gli schermi collegati possono avere risoluzioni diverse eb) puoi configurare la posizione di ogni schermo. – bitbonk

0

controlla questo codice se un top finestra a sinistra angolo è all'interno della scatola schermo virtuale (il rettangolo che include tutte le schermate disponibili). Si occupa anche delle impostazioni multimonitor in cui le coordinate possono essere negative, ad esempio il monitor principale a destra o in basso.

bool valid_position = 
SystemParameters.VirtualScreenLeft <= saved_location.X && 
(SystemParameters.VirtualScreenLeft + SystemParameters.VirtualScreenWidth) >= saved_location.X && 
SystemParameters.VirtualScreenTop <= saved_location.Y && 
(SystemParameters.VirtualScreenTop + SystemParameters.VirtualScreenHeight) >= saved_location.Y; 
+2

L'uso dello schermo virtuale non funziona perché quando si utilizzano più monitor il "desktop" non è un semplice rettangolo. Ci saranno punti ciechi nello schermo virtuale perché a) gli schermi collegati possono avere risoluzioni diverse eb) puoi configurare la posizione di ogni schermo. – bitbonk

0

Io uso questo codice tagliato nel mio progetto WPF all'avvio; è scritto in vb.net:

If My.Settings.RememberWindowPositionAndSize Then 
    If My.Settings.winMainWinState > 2 Then My.Settings.winMainWinState = WindowState.Normal 
    If My.Settings.winMainWinState = WindowState.Minimized Then My.Settings.winMainWinState = WindowState.Normal 
    Me.WindowState = My.Settings.winMainWinState 
    If My.Settings.winMainWinState = WindowState.Normal Then 
     Dim winBounds As New System.Drawing.Rectangle(CInt(My.Settings.winMainPosX), CInt(My.Settings.winMainPosY), 
                 CInt(My.Settings.winMainSizeB), CInt(My.Settings.winMainSizeH)) 
     For Each scr As System.Windows.Forms.Screen In System.Windows.Forms.Screen.AllScreens 
      If winBounds.IntersectsWith(scr.Bounds) Then 
       Me.Width = My.Settings.winMainSizeB 
       Me.Height = My.Settings.winMainSizeH 
       Me.Top = My.Settings.winMainPosY 
       Me.Left = My.Settings.winMainPosX 
       Exit For 
      End If 
     Next 
    End If 
End If 

e qui è il medesimo codice (convertito) in C#

if (My.Settings.RememberWindowPositionAndSize) { 
    if (My.Settings.winMainWinState > 2) 
     My.Settings.winMainWinState = WindowState.Normal; 
    if (My.Settings.winMainWinState == WindowState.Minimized) 
     My.Settings.winMainWinState = WindowState.Normal; 
    this.WindowState = My.Settings.winMainWinState; 

    if (My.Settings.winMainWinState == WindowState.Normal) { 
     System.Drawing.Rectangle winBounds = new System.Drawing.Rectangle(Convert.ToInt32(My.Settings.winMainPosX), Convert.ToInt32(My.Settings.winMainPosY), Convert.ToInt32(My.Settings.winMainSizeB), Convert.ToInt32(My.Settings.winMainSizeH)); 

     foreach (System.Windows.Forms.Screen scr in System.Windows.Forms.Screen.AllScreens) { 
      if (winBounds.IntersectsWith(scr.Bounds)) { 
       this.Width = My.Settings.winMainSizeB; 
       this.Height = My.Settings.winMainSizeH; 
       this.Top = My.Settings.winMainPosY; 
       this.Left = My.Settings.winMainPosX; 
       break; 
      } 
     } 
    } 
}