Restauration De La Taille/Position De La Fenêtre Avec Plusieurs Moniteurs

de nombreux messages sur la restauration D'une position et d'une taille WinForm.

exemples:

mais je n'ai pas encore trouvé de code pour le faire avec plusieurs moniteurs.

C'est-à-dire, si je ferme mon application .net Winform avec la fenêtre sur le moniteur 2, je veux qu'il sauve la taille de windows, l'emplacement, et l'état des paramètres de l'application, de sorte qu'il pourrait plus tard restaurer à monitor 2 lorsque je redémarre l'application. Ce serait bien si, comme dans le codeproject exemple ci-dessus, il comprend quelques vérifications, comme si l'emplacement d'enregistrement est pour la plupart hors de l'écran, il "corrige". Ou si l'emplacement sauvegardé est sur un moniteur qui n'est plus là (par exemple, mon ordinateur portable est maintenant tout seul sans mon second moniteur) alors il le déplace correctement au Moniteur 1.

une idée?

mon environnement: C#,. Net 3.5 ou inférieur, VS2008

31
demandé sur VVS 2009-06-02 03:28:04

6 réponses

essayez ce code. Points d'intérêt:

  • vérifie si la fenêtre est (partiellement) visible sur la zone de travail d'un écran. Par exemple: le faire glisser derrière la barre de tâches ou le déplacer complètement hors de l'écran permet de rétablir la position par défaut de windows.
  • enregistre les limites correctes même si la forme est minimisée ou maximisée (erreur commune)
  • sauve le WindowState correctement. Enregistrement FormWindowState.Minimisé est désactivé par la conception.

les limites et l'état sont stockés dans les applications avec leur type correspondant de sorte qu'il n'y a pas besoin de faire d'analyse de chaîne. Laissez le framework faire sa magie de sérialisation.

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            this.Size = Settings.Default.WindowPosition.Size;
        }
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        this.Visible = false;
        this.WindowState = FormWindowState.Normal;

        Settings.Default.WindowPosition = this.DesktopBounds;
        Settings.Default.Save();
    }
}

le fichier de paramètres de référence:

<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ScreenTest" GeneratedClassName="Settings">
    <Profiles />
    <Settings>
        <Setting Name="WindowPosition" Type="System.Drawing.Rectangle" Scope="User">
            <Value Profile="(Default)">0, 0, 0, 0</Value>
        </Setting>
        <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
            <Value Profile="(Default)">Normal</Value>
        </Setting>
    </Settings>
</SettingsFile>
32
répondu VVS 2009-06-02 15:44:04

la réponse fournie par VVS a été d'une grande aide! J'ai trouvé deux petits problèmes avec elle, alors je suis reposter la majeure partie de son code avec ces révisions:

(1) la première fois que la demande est lancée, le formulaire est ouvert à l'état Normal, mais sa taille est telle qu'il apparaît comme une barre de titre. J'ai ajouté un conditionnel dans le constructeur pour corriger cela.

(2) Si l'application est fermée pendant qu'elle est minimisée ou maximisée, le code dans L'Onclosage échoue pour rappeler les dimensions de la fenêtre dans son état Normal. (Les 3 lignes de code--que j'ai maintenant commenté--semblent raisonnables mais pour une raison quelconque ne fonctionne tout simplement pas. Heureusement, j'avais déjà résolu ce problème et j'ai inclus ce code dans une nouvelle région à la fin du code pour suivre l'état de la fenêtre au fur et à mesure qu'il se produit plutôt que d'attendre la fermeture.


avec ces deux fixations en place, j'ai testé:

A. fermeture l'état normal--restaure à la même taille/position et de l'état

B. fermeture à l'état minimisé--rétablissement à l'état normal avec la dernière Position / taille normale

C. fermeture à l'état maximisé -- rétablit à l'état maximisé et se souvient de sa dernière taille/position quand on s'ajuste plus tard à l'état normal.

D. fermeture de l'écran 2 -- rétablissement de l'écran 2.

E. fermeture de l'écran 2 puis Déconnexion de l'écran 2--rétablissement à la même position sur le moniteur 1

David: votre code m'a permis d'atteindre les points D et E presque sans effort--non seulement vous avez fourni une solution pour ma question, vous l'avez fourni dans un programme complet donc je l'ai eu en marche presque quelques secondes après l'avoir collé dans Visual Studio. Donc un grand merci pour ça!

public partial class MainForm : Form
{
    bool windowInitialized;

    public MainForm()
    {
        InitializeComponent();

        // this is the default
        this.WindowState = FormWindowState.Normal;
        this.StartPosition = FormStartPosition.WindowsDefaultBounds;

        // check if the saved bounds are nonzero and visible on any screen
        if (Settings.Default.WindowPosition != Rectangle.Empty &&
            IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
        {
            // first set the bounds
            this.StartPosition = FormStartPosition.Manual;
            this.DesktopBounds = Settings.Default.WindowPosition;

            // afterwards set the window state to the saved value (which could be Maximized)
            this.WindowState = Settings.Default.WindowState;
        }
        else
        {
            // this resets the upper left corner of the window to windows standards
            this.StartPosition = FormStartPosition.WindowsDefaultLocation;

            // we can still apply the saved size
            // msorens: added gatekeeper, otherwise first time appears as just a title bar!
            if (Settings.Default.WindowPosition != Rectangle.Empty)
            {
                this.Size = Settings.Default.WindowPosition.Size;
            }
        }
        windowInitialized = true;
    }

    private bool IsVisibleOnAnyScreen(Rectangle rect)
    {
        foreach (Screen screen in Screen.AllScreens)
        {
            if (screen.WorkingArea.IntersectsWith(rect))
            {
                return true;
            }
        }

        return false;
    }

    protected override void OnClosed(EventArgs e)
    {
        base.OnClosed(e);

        // only save the WindowState if Normal or Maximized
        switch (this.WindowState)
        {
            case FormWindowState.Normal:
            case FormWindowState.Maximized:
                Settings.Default.WindowState = this.WindowState;
                break;

            default:
                Settings.Default.WindowState = FormWindowState.Normal;
                break;
        }

        # region msorens: this code does *not* handle minimized/maximized window.

        // reset window state to normal to get the correct bounds
        // also make the form invisible to prevent distracting the user
        //this.Visible = false;
        //this.WindowState = FormWindowState.Normal;
        //Settings.Default.WindowPosition = this.DesktopBounds;

        # endregion

        Settings.Default.Save();
    }

    # region window size/position
    // msorens: Added region to handle closing when window is minimized or maximized.

    protected override void OnResize(EventArgs e)
    {
        base.OnResize(e);
        TrackWindowState();
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        TrackWindowState();
    }

    // On a move or resize in Normal state, record the new values as they occur.
    // This solves the problem of closing the app when minimized or maximized.
    private void TrackWindowState()
    {
        // Don't record the window setup, otherwise we lose the persistent values!
        if (!windowInitialized) { return; }

        if (WindowState == FormWindowState.Normal)
        {
            Settings.Default.WindowPosition = this.DesktopBounds;
        }
    }

    # endregion window size/position
}
25
répondu Michael Sorens 2009-07-01 19:55:57

la plupart des autres solutions ici reposent sur la détermination manuelle du positionnement actuel de chaque moniteur. Les cas limites sont extrêmement difficile à comprendre, et très peu d'applications peuvent obtenir le droit de rouler leur propre.

la fonction SetWindowPlacement à L'intérieur de Windows elle - même gère correctement tous les cas de bordures-si la fenêtre est positionnée hors d'un écran visible, elle l'ajuste en conséquence.

le meilleur exemple que j'ai vu en C # est sur le blog de David Rickard. Non seulement il montre comment utiliser SetWindowPlacement, mais il montre aussi comment sérialiser le résultat entier. http://blogs.msdn.com/b/davidrickard/archive/2010/03/09/saving-window-size-and-location-in-wpf-and-winforms.aspx

8
répondu ShadowChaser 2011-05-19 17:09:37

celui-ci est le parfait je pense basé sur vos réponses et commentaires.


cette solution consiste à sauvegarder/restaurer la taille et la position du formulaire avec moniteurs multiples + document multiple , formulaire multiple ou formulaire principal multiple support. Il est pas MDI formulaire mais Microsoft Word comme multi document avec instance principale différente.

grâce à VVS, msorens et Ian Goldby. Je fusionne la solution de VVS, msorens et MSDN Application.Exécuter la méthode (ApplicationContext) exemple pour faire la multi-forme principale mais pas MDI.

ce correctif inclut le commentaire de Ian Goldby qui utilise Form.RestoreBounds pour éliminer OnResize() , OnMove() et TrackWindowState() .

je fixe aussi pour se souvenir du moniteur quand le formulaire se déplacer à L'autre moniteur et obtenir maximisé avant la sortie parce que je ne traque pas L'OnResize, OnMove. Par cette correction, cette solution prend en charge Windows 7 fonction Snap que vous pouvez faire glisser la barre de titre ou la touche Win+flèche pour snap form window dans n'importe quel bord de moniteur ou de le faire maximisé/normal ainsi que minimisé.

cette solution mise en œuvre dans le programme mais pas dans la forme principale pour soutenir la forme principale multi. Cependant vous pouvez utiliser pour unique principale La forme aussi.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SimpleTestForm.Properties;
using System.Drawing;

namespace SimpleTestForm
{
    static class Program
    {
        static MultiMainFormAppContext appContext;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            appContext = new MultiMainFormAppContext();
            Application.Run(appContext);
        }

        /// <summary>
        /// Create a new MainForm and restore the form size and position if necessary. This method can be called like from Menu File > New click event.
        /// </summary>
        /// <returns></returns>
        public static MainForm createNewMainForm()
        {
            return appContext.createNewMainForm();
        }

        /// <summary>
        /// Get the current active MainForm event if a dialog is opened. Useful to create Dictionary (MainForm, T) to store Form/document dependent field. Please set the Owner of child form to prevent null reference exception.
        /// </summary>
        /// <returns></returns>
        public static MainForm GetCurrentMainFormInstance()
        {
            Form mainForm = Form.ActiveForm;
            while (!(mainForm is MainForm) && mainForm.Owner != null)
                mainForm = mainForm.Owner;
            return mainForm as MainForm;
        }
    }

    class MultiMainFormAppContext : ApplicationContext
    {
        List<MainForm> mainForms = new List<MainForm>();
        Point newRestoredLocation = Point.Empty;

        internal MultiMainFormAppContext()
        {
            createNewMainForm();
        }

        internal MainForm createNewMainForm()
        {
            MainForm mainForm = new MainForm();
            mainForm.FormClosed += new FormClosedEventHandler(mainForm_FormClosed);
            mainForm.LocationChanged += new EventHandler(mainForm_LocationChanged);
            RestoreFormSizeNPosition(mainForm);
            PreventSameLocation(mainForm);
            mainForms.Add(mainForm);
            mainForm.Show();
            return mainForm;
        }

        private void PreventSameLocation(MainForm mainForm)
        {
            const int distance = 20;
            foreach (MainForm otherMainForm in mainForms)
            {
                if (Math.Abs(otherMainForm.Location.X - mainForm.Location.X) < distance &&
                    Math.Abs(otherMainForm.Location.Y - mainForm.Location.Y) < distance)
                    mainForm.Location = new Point(mainForm.Location.X + distance, mainForm.Location.Y + distance);
            }
        }

        /// <summary>
        /// Restore the form size and position with multi monitor support.
        /// </summary>
        private void RestoreFormSizeNPosition(MainForm mainForm)
        {
            // this is the default
            mainForm.WindowState = FormWindowState.Normal;
            mainForm.StartPosition = FormStartPosition.WindowsDefaultBounds;

            // check if the saved bounds are nonzero and visible on any screen
            if (Settings.Default.WindowPosition != Rectangle.Empty &&
                IsVisibleOnAnyScreen(Settings.Default.WindowPosition))
            {
                // first set the bounds
                mainForm.StartPosition = FormStartPosition.Manual;
                mainForm.DesktopBounds = Settings.Default.WindowPosition;

                // afterwards set the window state to the saved value (which could be Maximized)
                mainForm.WindowState = Settings.Default.WindowState;
            }
            else
            {
                // this resets the upper left corner of the window to windows standards
                mainForm.StartPosition = FormStartPosition.WindowsDefaultLocation;

                // we can still apply the saved size if not empty
                if (Settings.Default.WindowPosition != Rectangle.Empty)
                {
                    mainForm.Size = Settings.Default.WindowPosition.Size;
                }
            }
        }

        private void SaveFormSizeNPosition(MainForm mainForm)
        {
            // only save the WindowState as Normal or Maximized
            Settings.Default.WindowState = FormWindowState.Normal;
            if (mainForm.WindowState == FormWindowState.Normal || mainForm.WindowState == FormWindowState.Maximized)
                Settings.Default.WindowState = mainForm.WindowState;

            if (mainForm.WindowState == FormWindowState.Normal)
            {
                Settings.Default.WindowPosition = mainForm.DesktopBounds;
            }
            else
            {
                if (newRestoredLocation == Point.Empty)
                    Settings.Default.WindowPosition = mainForm.RestoreBounds;
                else
                    Settings.Default.WindowPosition = new Rectangle(newRestoredLocation, mainForm.RestoreBounds.Size);
            }

            Settings.Default.Save();
        }

        private bool IsVisibleOnAnyScreen(Rectangle rect)
        {
            foreach (Screen screen in Screen.AllScreens)
            {
                if (screen.WorkingArea.IntersectsWith(rect))
                    return true;
            }
            return false;
        }

        void mainForm_LocationChanged(object sender, EventArgs e)
        {
            MainForm mainForm = sender as MainForm;
            if (mainForm.WindowState == FormWindowState.Maximized)
            {
                // get the center location of the form incase like RibbonForm will be bigger and maximized Location wll be negative value that Screen.FromPoint(mainForm.Location) will going to the other monitor resides on the left or top of primary monitor.
                // Another thing, you might consider the form is in the monitor even if the location (top left corner) is on another monitor because majority area is on the monitor, so center point is the best way.
                Point centerFormMaximized = new Point (mainForm.DesktopBounds.Left + mainForm.DesktopBounds.Width/2, mainForm.DesktopBounds.Top + mainForm.DesktopBounds.Height/2);
                Point centerFormRestored = new Point(mainForm.RestoreBounds.Left + mainForm.RestoreBounds.Width / 2, mainForm.RestoreBounds.Top + mainForm.RestoreBounds.Height / 2);
                Screen screenMaximized = Screen.FromPoint(centerFormMaximized);
                Screen screenRestored = Screen.FromPoint(centerFormRestored);
                // we need to change the Location of mainForm.RestoreBounds to the new screen where the form currently maximized.
                // RestoreBounds does not update the Location if you change the screen but never restore to FormWindowState.Normal
                if (screenMaximized.DeviceName != screenRestored.DeviceName)
                {
                    newRestoredLocation = mainForm.RestoreBounds.Location;
                    int screenOffsetX = screenMaximized.Bounds.Location.X - screenRestored.Bounds.Location.X;
                    int screenOffsetY = screenMaximized.Bounds.Location.Y - screenRestored.Bounds.Location.Y;
                    newRestoredLocation.Offset(screenOffsetX, screenOffsetY);
                    return;
                }
            }
            newRestoredLocation = Point.Empty;
        }

        void mainForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            MainForm mainForm = sender as MainForm;
            SaveFormSizeNPosition(mainForm);
            mainForm.FormClosed -= new FormClosedEventHandler(mainForm_FormClosed);
            mainForm.LocationChanged -= new EventHandler(mainForm_LocationChanged);
            mainForm.Dispose();
            mainForms.Remove(mainForm);
            if (mainForms.Count == 0) ExitThread();
        }
    }
}

modifier: Préventsamelocation méthode ajoutée pour s'assurer que le 2ème formulaire ouvert pas exactement sur le premier formulaire et l'utilisateur remarquera le formulaire nouvellement ouvert.

2
répondu CallMeLaNN 2011-02-25 08:08:11

si vous avez plusieurs moniteurs, je crois que les dimensions de L'interface sont simplement plus grandes. Ainsi, l'approche normale "1 Moniteur" de stockage et de restauration de l'emplacement ne fera que fonctionner. Je n'ai pas essayé cela parce que je suis loin de mon second moniteur mais cela ne devrait pas être difficile à tester. La façon dont vous avez posé la Question Est comme vous ne l'avez pas testé.

votre deuxième exigence signifie que vous devrez vérifier les dimensions max sceen lors de la restauration de l'application, et puis repositionner en tant que de besoin. Pour faire ce dernier bit, j'utilise ce code:

    private System.Drawing.Rectangle ConstrainToScreen(System.Drawing.Rectangle bounds)
    {
        Screen screen = Screen.FromRectangle(bounds);
        System.Drawing.Rectangle workingArea = screen.WorkingArea;
        int width = Math.Min(bounds.Width, workingArea.Width);
        int height = Math.Min(bounds.Height, workingArea.Height);
        // mmm....minimax            
        int left = Math.Min(workingArea.Right - width, Math.Max(bounds.Left, workingArea.Left));
        int top = Math.Min(workingArea.Bottom - height, Math.Max(bounds.Top, workingArea.Top));
        return new System.Drawing.Rectangle(left, top, width, height);
    }

j'appelle cette méthode lors de la restauration de la forme. Je stocke la géométrie de l'écran dans le Registre sur le formulaire Fermer, puis lire la géométrie sur le formulaire ouvert. J'obtiens les limites, mais puis je contraint les limites restaurées à l'écran courant actuel, en utilisant la méthode ci-dessus.

enregistrer sur Fermer:

      // store the size of the form
      int w = 0, h = 0, left = 0, top = 0;
      if (this.Bounds.Width < this.MinimumSize.Width || this.Bounds.Height < this.MinimumSize.Height)
      {
          // The form is currently minimized.  
          // RestoreBounds is the size of the window prior to last minimize action.
          w = this.RestoreBounds.Width;
          h = this.RestoreBounds.Height;
          left = this.RestoreBounds.Location.X;
          top = this.RestoreBounds.Location.Y;
      }
      else
      {
          w = this.Bounds.Width;
          h = this.Bounds.Height;
          left = this.Location.X;
          top = this.Location.Y;
      }
      AppCuKey.SetValue(_rvn_Geometry,
        String.Format("{0},{1},{2},{3},{4}",
              left, top, w, h, (int)this.WindowState));

Restaurer sur le formulaire d'ouverture:

    // restore the geometry of the form
    string s = (string)AppCuKey.GetValue(_rvn_Geometry);
    if (!String.IsNullOrEmpty(s))
    {
        int[] p = Array.ConvertAll<string, int>(s.Split(','),
                         new Converter<string, int>((t) => { return Int32.Parse(t); }));
        if (p != null && p.Length == 5)
            this.Bounds = ConstrainToScreen(new System.Drawing.Rectangle(p[0], p[1], p[2], p[3]));
    }
1
répondu Cheeso 2009-06-02 00:39:05

c'est une vieille question, Mais voici une version VB basée sur les réponses précédentes.

un problème avec les réponses suggérées par VVS et Michael Sorens est qu'une position sauvegardée qui n'affiche que quelques pixels sur un écran compte comme visible. Cette solution nécessite au moins 50x50 pixels à l'intersection avant de restaurer l'emplacement précédent.

paramètres:

<Settings>
  <Setting Name="WindowState" Type="System.Windows.Forms.FormWindowState" Scope="User">
    <Value Profile="(Default)">Normal</Value>
  </Setting>
  <Setting Name="WindowBounds" Type="System.Drawing.Rectangle" Scope="User">
    <Value Profile="(Default)">10, 10, 800, 600</Value>
  </Setting>
</Settings>

forme:

Partial Public Class MainForm

    Private loadingComplete As Boolean = False

    Public Sub New()

        InitializeComponent()
        RestoreWindowLocation()

    End Sub

    Private Sub MainForm_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        loadingComplete = True

    End Sub

    Private Sub MainForm_Resize(sender As System.Object, e As System.EventArgs) Handles MyBase.Resize

         TrackWindowLocation()

     End Sub

     Private Sub MainForm_Move(sender As System.Object, e As System.EventArgs) Handles MyBase.Move

         TrackWindowLocation()

     End Sub

    Private Sub MainForm_FormClosing(sender As System.Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing

        SaveWindowLocation()

     End Sub


    Private Sub RestoreWindowLocation()

        If IsRectangleVisible(My.Settings.WindowBounds) Then
            Me.StartPosition = FormStartPosition.Manual
            Me.DesktopBounds = My.Settings.WindowBounds
        End If

        If Not My.Settings.WindowState = FormWindowState.Minimized Then
            Me.WindowState = My.Settings.WindowState
        End If

    End Sub

    Private Sub TrackWindowLocation()

        If loadingComplete Then
            If Me.WindowState = FormWindowState.Normal Then
                My.Settings.WindowBounds = Me.DesktopBounds
                My.Settings.WindowState = Me.WindowState
            End If
        End If

    End Sub

    Private Sub SaveWindowLocation()

        If Not Me.WindowState = FormWindowState.Minimized Then
            My.Settings.WindowState = Me.WindowState
        End If

        If Me.WindowState = FormWindowState.Normal Then
            My.Settings.WindowBounds = Me.DesktopBounds
        End If

        My.Settings.Save()

    End Sub

    Private Function IsRectangleVisible(Rectangle As Rectangle) As Boolean

        For Each screen As Screen In screen.AllScreens
            Dim r As Rectangle = Rectangle.Intersect(Rectangle, screen.WorkingArea)
            If Not r.IsEmpty Then
                If r.Width > 50 And r.Height > 50 Then Return True
            End If
        Next

        Return False

    End Function

End Class
0
répondu Judah Sali 2014-07-24 01:56:58