Cómo registrar la posición de la ventana en la configuración de la aplicación Windows Forms

StackOverflow https://stackoverflow.com/questions/105932

  •  01-07-2019
  •  | 
  •  

Pregunta

Parece un requisito estándar:La próxima vez que el usuario inicie la aplicación, abra la ventana en la misma posición y estado que estaba antes.Aquí está mi lista de deseos:

  • Posición de la ventana igual que antes.
    • A menos que la pantalla haya cambiado de tamaño y la posición anterior ahora esté fuera de la pantalla.
  • Los divisores deben conservar su posición
  • Los contenedores de pestañas deben conservar su selección.
  • Algunos menús desplegables deberían conservar su selección.
  • El estado de la ventana (maximizar, minimizar, normal) es el mismo que antes.
    • Quizás nunca deberías empezar minimizado, no lo he decidido.

Agregaré mis soluciones actuales como respuesta junto con las limitaciones.

¿Fue útil?

Solución

Mi otra opción es escribir más código personalizado en torno a la configuración de la aplicación y ejecutarlo en formLoad y formClosed.Esto no utiliza enlace de datos.

Desventajas:

  • Más código para escribir.
  • Muy complicado.El orden en que establece las propiedades en formLoad es confuso.Por ejemplo, debe asegurarse de haber configurado el tamaño de la ventana antes de configurar la distancia del divisor.

En este momento, esta es mi solución preferida, pero parece demasiado trabajo.Para reducir el trabajo, creé una clase WindowSettings que serializa la ubicación, el tamaño, el estado y cualquier posición del divisor de la ventana en una única configuración de aplicación.Luego puedo crear una configuración de ese tipo para cada formulario en mi aplicación, guardarla al cerrar y restaurarla al cargar.

publiqué el código fuente, incluida la clase WindowSettings y algunos formularios que la utilizan.Las instrucciones sobre cómo agregarlo a un proyecto se incluyen en el archivo WindowSettings.cs.La parte más complicada fue descubrir cómo agregar una configuración de aplicación con un tipo personalizado.Tu eliges Explorar...en el menú desplegable de tipos y luego ingrese manualmente el espacio de nombres y el nombre de la clase.Los tipos de su proyecto no aparecen en la lista.

Actualizar: Agregué algunos métodos estáticos para simplificar el código repetitivo que agregas a cada formulario.Una vez que haya seguido las instrucciones para agregar la clase WindowSettings a su proyecto y crear una configuración de aplicación, aquí hay un ejemplo del código que debe agregarse a cada formulario cuya posición desea registrar y restaurar.

    private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        Settings.Default.CustomWindowSettings = WindowSettings.Record(
            Settings.Default.CustomWindowSettings,
            this, 
            splitContainer1);
    }

    private void MyForm_Load(object sender, EventArgs e)
    {
        WindowSettings.Restore(
            Settings.Default.CustomWindowSettings, 
            this, 
            splitContainer1);
    }

Otros consejos

El siguiente ejemplo muestra cómo lo hago.

  • Se llama a SavePreferences al cerrar el formulario y guarda el tamaño del formulario y una bandera que indica si está maximizado (en esta versión no guardo si está minimizado; volverá a aparecer restaurado o maximizado la próxima vez).

  • LoadPreferences se llama desde OnLoad.

  • Primero guarde el WindowState en tiempo de diseño y configúrelo en Normal.Solo puede configurar correctamente el tamaño del formulario si WindowState es Normal.

  • Luego restaure el Tamaño desde su configuración persistente.

  • Ahora asegúrese de que el formulario se ajuste a su pantalla (llame a FitToScreen).Es posible que la resolución de la pantalla haya cambiado desde la última vez que ejecutó la aplicación.

  • Finalmente, establezca WindowState nuevamente en Maximizado (si persiste como tal), o en el valor de tiempo de diseño guardado anteriormente.

Obviamente, esto podría adaptarse para conservar la posición inicial y si el formulario se minimizó cuando se cerró; no necesitaba hacer eso.Otras configuraciones para los controles de su formulario, como la posición del divisor y el contenedor de pestañas, son sencillas.

private void FitToScreen()
{
    if (this.Width > Screen.PrimaryScreen.WorkingArea.Width)
    {
        this.Width = Screen.PrimaryScreen.WorkingArea.Width;
    }
    if (this.Height > Screen.PrimaryScreen.WorkingArea.Height)
    {
        this.Height = Screen.PrimaryScreen.WorkingArea.Height;
    }
}   
private void LoadPreferences()
{
    // Called from Form.OnLoad

    // Remember the initial window state and set it to Normal before sizing the form
    FormWindowState initialWindowState = this.WindowState;
    this.WindowState = FormWindowState.Normal;
    this.Size = UserPreferencesManager.LoadSetting("_Size", this.Size);
    _currentFormSize = Size;
    // Fit to the current screen size in case the screen resolution
    // has changed since the size was last persisted.
    FitToScreen();
    bool isMaximized = UserPreferencesManager.LoadSetting("_Max", initialWindowState == FormWindowState.Maximized);
    WindowState = isMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
private void SavePreferences()
{
    // Called from Form.OnClosed
    UserPreferencesManager.SaveSetting("_Size", _currentFormSize);
    UserPreferencesManager.SaveSetting("_Max", this.WindowState == FormWindowState.Maximized);
    ... save other settings
}

X

La solución más sencilla que he encontrado es utilizar el enlace de datos con la configuración de la aplicación.Vinculo las propiedades de ubicación y tamaño del cliente en la ventana junto con splitterDistance en el divisor.

Desventajas:

  • Si cierra la ventana mientras está minimizada, se abrirá oculta la próxima vez.Es muy difícil recuperar la ventana.
  • Si cierra la ventana mientras está maximizada, se abre ocupando toda la pantalla, pero no maximizada (problema menor).
  • Cambiar el tamaño de la ventana usando la esquina superior derecha o la esquina inferior izquierda es simplemente feo.Supongo que las dos propiedades vinculadas a datos están luchando entre sí.

Si desea experimentar con el comportamiento extraño, publiqué un solución de muestra utilizando esta técnica.

Hago una configuración para cada valor que quiero guardar y uso un código como este:

private void MainForm_Load(object sender, EventArgs e) {
  RestoreState();
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
  SaveState();
}

private void SaveState() {
  if (WindowState == FormWindowState.Normal) {
    Properties.Settings.Default.MainFormLocation = Location;
    Properties.Settings.Default.MainFormSize = Size;
  } else {
    Properties.Settings.Default.MainFormLocation = RestoreBounds.Location;
    Properties.Settings.Default.MainFormSize = RestoreBounds.Size;
  }
  Properties.Settings.Default.MainFormState = WindowState;
  Properties.Settings.Default.SplitterDistance = splitContainer1.SplitterDistance;
  Properties.Settings.Default.Save();
}

private void RestoreState() {
  if (Properties.Settings.Default.MainFormSize == new Size(0, 0)) {
    return; // state has never been saved
  }
  StartPosition = FormStartPosition.Manual;
  Location = Properties.Settings.Default.MainFormLocation;
  Size = Properties.Settings.Default.MainFormSize;
  // I don't like an app to be restored minimized, even if I closed it that way
  WindowState = Properties.Settings.Default.MainFormState == 
    FormWindowState.Minimized ? FormWindowState.Normal : Properties.Settings.Default.MainFormState;
  splitContainer1.SplitterDistance = Properties.Settings.Default.SplitterDistance;
}

Tenga en cuenta que la recompilación borra el archivo de configuración donde se almacenan las configuraciones, así que pruébelo sin realizar cambios en el código entre guardar y restaurar.

Según la respuesta aceptada por Don Kirkby y la clase WindowSettings que escribió, podría derivar un CustomForm del estándar para reducir la cantidad de código idéntico escrito para todos y cada uno de los formularios, tal vez así:

using System;
using System.Configuration;
using System.Reflection;
using System.Windows.Forms;

namespace CustomForm
{
  public class MyCustomForm : Form
  {
    private ApplicationSettingsBase _appSettings = null;
    private string _settingName = "";

    public Form() : base() { }

    public Form(ApplicationSettingsBase settings, string settingName)
      : base()
    {
      _appSettings = settings;
      _settingName = settingName;

      this.Load += new EventHandler(Form_Load);
      this.FormClosing += new FormClosingEventHandler(Form_FormClosing);
    }

    private void Form_Load(object sender, EventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null) return;

      previousSettings.Restore(this);
    }

    private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null)
        previousSettings = new WindowSettings();

      previousSettings.Record(this);

      settingProperty.SetValue(_appSettings, previousSettings, null);

      _appSettings.Save();
    }
  }
}

Para usar esto, pase la clase de configuración de su aplicación y el nombre de la configuración en el constructor:

CustomForm.MyCustomForm f = new CustomForm.MyCustomForm(Properties.Settings.Default, "formSettings");

Esto utiliza Reflection para obtener/establecer la configuración anterior desde/hacia la clase de configuración.Puede que no sea óptimo colocar la llamada Guardar en la rutina Form_Closing; se podría eliminar eso y guardar el archivo de configuración cada vez que se cierre la aplicación principal.

Para usarlo como una forma regular, simplemente use el constructor sin parámetros.

Puede usar la configuración de la aplicación para establecer qué propiedades de control se conservarán; en el evento Form_closed debe usar el método guardar en la configuración de la aplicación para escribirlas en el disco:

Properties.Settings.Default.Save();

Aquí hay un ejemplo de algunos que uso yo mismo.Solo tiene en cuenta el monitor principal, por lo que podría ser mejor manejarlo de manera diferente si se usa en varios monitores.

Size size;
int x;
int y;
if (WindowState.Equals(FormWindowState.Normal))
{
    size = Size;
    if (Location.X + size.Width > Screen.PrimaryScreen.Bounds.Width)
        x = Screen.PrimaryScreen.Bounds.Width - size.Width;
    else
        x = Location.X;
    if (Location.Y + Size.Height > Screen.PrimaryScreen.Bounds.Height)
        y = Screen.PrimaryScreen.Bounds.Height - size.Height;
    else
        y = Location.Y;
}
else
{
size = RestoreBounds.Size;
x = (Screen.PrimaryScreen.Bounds.Width - size.Width)/2;
y = (Screen.PrimaryScreen.Bounds.Height - size.Height)/2;
}
Properties.Settings.Position.AsPoint = new Point(x, y); // Property setting is type of Point
Properties.Settings.Size.AsSize = size;                 // Property setting is type of Size
Properties.Settings.SplitterDistance.Value = splitContainer1.SplitterDistance; // Property setting is type of int
Properties.Settings.IsMaximized = WindowState == FormWindowState.Maximized;    // Property setting is type of bool
Properties.Settings.DropDownSelection = DropDown1.SelectedValue;
Properties.Settings.Save();

Un truco que puedes usar en Configuración para almacenar esa información.Todo lo que tienes que hacer es vincular la propiedad deseada (ej.form.Size y form.Location) a una configuración específica y se guarda y actualiza automáticamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top