2008-09-19 20 views
23

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 antes. Aquí está mi lista de deseos:Cómo registrar la posición de la ventana en la configuración de la aplicación Windows Forms

  • Posición de la ventana misma que
    • A menos que la pantalla ha cambiado el tamaño y la posición de edad es ahora fuera de la pantalla.
  • divisores deben conservar su posición
  • contenedores lengüeta debe conservar su selección
  • Algunos menús desplegables deben conservar su selección
  • estado de la ventana (maximizar, minimizar, normal) es el mismo como lo era.
    • Tal vez nunca deberías comenzar a minimizar, no lo he decidido.

voy a añadir mis soluciones actuales como respuesta, junto con las limitaciones.

Respuesta

18

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

inconvenientes:

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

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

Publiqué the source code, incluyendo la clase WindowSettings y algunos formularios que lo usan. Las instrucciones para 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. Elige Browse ... en el menú desplegable de tipo, y luego ingresa manualmente el espacio de nombre y el nombre de clase. Los tipos de su proyecto no aparecen en la lista.

Actualización: Agregué algunos métodos estáticos para simplificar el código repetitivo que agrega 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); 
    } 
+0

¡Gran código de muestra! –

+1

Desafortunadamente, la licencia del proyecto donkirkby bajo el cual está limitada la muestra puede no permitir un simple uso libre del código. Considere volver a publicarlo aquí. –

+0

Ahora he cambiado a la licencia de MIT; No quise restringir el uso del código. Por supuesto, se agradece la atribución. El código es un poco largo para publicar aquí. –

5

La solución más simple que he encontrado es utilizar el enlace de datos con la configuración de la aplicación. Ato las propiedades location y clientSize en la ventana junto con el splitterDistance en el splitter.

inconvenientes:

  • Si cierra la ventana mientras se minimiza, se abre oculta la próxima vez. Es realmente difícil recuperar la ventana.
  • Si cierra la ventana mientras está maximizado, se abrirá llenando toda la pantalla, pero no se maximizará (problema menor).
  • Cambiar el tamaño de la ventana usando la esquina superior derecha o la esquina inferior izquierda es feo. Supongo que las dos propiedades de datos se están peleando entre sí.

Si desea experimentar con el comportamiento extraño, he publicado un sample solution con esta técnica.

+0

Hay una propiedad WindowState a la que puede hacer referencia para verificar ventanas minimizadas o maximizadas. Sin embargo, no puedo comentar cuál es el tamaño de la ventana cuando está en uno de esos dos estados. –

+0

Sí, experimenté con la propiedad WindowState. Se comportó de manera muy extraña. Quizás un problema similar al cambio de tamaño, donde cambiar más de una propiedad de datos al mismo tiempo causa contención y parpadeo. –

2

Un truco puede usar Configuraciones para almacenar esa información. Todo lo que tiene que hacer es vincular la propiedad deseada (por ejemplo, formulario.Tamaño y forma.Ubicación) a una configuración específica y se guardará y actualizará automáticamente.

2

Puede utilizar los parámetros de la aplicación para establecer cuales controlan las propiedades se persistió, en caso Form_closed usted tiene que utilizar el método Save en la configuración de la aplicación para escribir estos en el disco:

Properties.Settings.Default.Save(); 
2

Aquí hay un ejemplo de algunos que uso yo mismo. Solo toma en consideración el monitor principal, por lo que podría ser mejor manejarlo de forma diferente si se usa en múltiples 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(); 
+0

Sí, ese es el tipo de código personalizado del que estoy hablando en mi segunda respuesta. Como dije, es difícil hacer las cosas bien. Espero que haya una manera más fácil. Gracias, sin embargo, no sabía sobre las propiedades AsSize y AsPoint. Los verificare. –

5

que realizar un ajuste para cada valor quiero salvar, y use el código como esto:

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 recompilar borra el archivo de configuración donde se almacenan los ajustes, por lo que probarlo sin hacer cambios de código entre un guardado y una restauración.

6

El ejemplo siguiente muestra cómo lo hago

  • SavePreferences se llama cuando se cierra el formulario y guarda el tamaño de la forma, y ​​una bandera que indica si está maximizada (en esta versión no la salvo si se trata de minimizado - volverá restaurado o maximizado la próxima vez).

  • LoadPreferences se llama desde OnLoad.

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

  • A continuación, restaure el tamaño de la configuración persistente.

  • Ahora asegúrese de que la forma quepa en su pantalla (llame a FitToScreen). La resolución de la pantalla puede haber cambiado desde la última vez que ejecutó la aplicación.

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

Esto obviamente se puede adaptar para persistir en la posición de inicio y si la forma se minimizó cuando se cerró - No tuve que hacer eso. Otras configuraciones para controles en 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

+0

Intenté esto y tuve problemas con la referencia de UserPreferencesManager. Google indica que esta es una clase de Java, no C#! –

+0

No estaba muy claro, era I. En este ejemplo, UserPreferencesManager es una clase que escribí, que hace el trabajo de cargar y guardar configuraciones en un medio persistente. Esto fue para .NET 1.1, estos días usaría la arquitectura de configuración de .NET 2.0 para persistencia. Tenga en cuenta que el foco de esta muestra fue el orden en que las propiedades se configuran al cargar la configuración, en lugar de los detalles de cómo se guardan/restauran. – Joe

+0

El comentario del código dice "Ajustar al tamaño de pantalla actual ..." pero el código actual usa Screen.PrimaryScreen: ¡la pantalla actual puede no ser PrimaryScreen en una configuración de múltiples monitores! – onedaywhen

3

Sobre la base de la respuesta aceptada por Don Kirkby y la clase WindowSettings escribió, se podría derivar un CustomForm de la estándar para reducir la cantidad de código idénticos escrito para cada uno y cada forma, tal como esto:

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, pasar a la clase de configuración de la aplicación y el nombre de la configuración en el constructor:

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

Esto usa Reflection para obtener/establecer la configuración anterior de/a la clase de configuración. Puede que no sea óptimo colocar la llamada Guardar en la rutina Form_Closing, se podría eliminar 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.

+0

Esa es una idea interesante para reducir el código repetitivo. Podría adaptarlo a un método estático en la clase WindowSettings que toma el objeto WindowSettings como un parámetro ref. Creo que eso podría evitar el uso de la reflexión. –

+0

Gracias por el comentario. Sin embargo, no estoy seguro de cómo funcionaría eso para usar el reflejo. Debe pasar algún tipo que represente un objeto de configuración. Como el objeto que contiene la configuración varía de un proyecto a otro, no sabía cómo configurar y extraer la configuración sin usar el reflejo, ya que los nombres de las propiedades son diferentes para cada caso. Para simplificar también intenté usar _this.Name_ para construir un nombre de configuración más genérico, pero eso no funcionó ya que el nombre del formulario se establece dentro de la llamada InitializeComponent, que ocurre después de la llamada al constructor base. – takrl

+0

OK, agregué los métodos estáticos al código de muestra: http://code.google.com/p/donkirkby/source/browse/trunk/WindowSettings/WindowSettings.cs Puede ver cómo se llaman en el fragmento en la respuesta aceptada arriba. Gracias por la idea –

Cuestiones relacionadas