Este código esta diseñado para ser copiado y pegado en todos los formularios de una aplicación.
Cuando se cierra el formulario se guardan en un fichero INI. los valores de situación, tamaño, si esta minimizado, oculto, y el valor TopMost
Cuando el formulario vuelve a usarse, se cargan esos valores del fichero INI. y se muestra con el mismo tamaño y en la misma posición que estaba cuando se cerró.
Imagen de la grabación de este código en el fichero INI.

Aspecto del fichero INI generado
Código VB.Net (V 7.0)
#Region "Eventos Load / Closing [Versión - 2023-10-12]" Private Sub MainWindow_Initialized(sender As Object, e As System.EventArgs) Handles Me.Initialized '--------------------------------------------------------- ' Otras inicializaciones Call InicializacionCamposFormulario() '--------------------------------------------------------- ' Leer datos del fichero Ini Call LeerDatosVentanaDeIni() End Sub Private Sub MainWindow_Closing(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles Me.Closing '--------------------------------------------------------- ' grabar datos en el fichero Ini Call GuardarDatosVentanaEnIni() '--------------------------------------------------------- ' destruir los objetos internos empleados en este form ' FALTA POR ESCRIBIR '----------------------------------------------------- ' End Sub ''' <summary> ''' Carga los cuadro de texto con la información que muestra este formulario ''' Se tiene que llamar desde el evento [Initialized] ''' No es obligatorio ''' </summary> Private Sub InicializacionCamposFormulario() ' en este form no se hace nada End Sub #End Region #Region "Funciones para Guardar / leer datos en fichero Ini [Versión - 2023-10-12]" '------------------------------------------------ ' Apunte táctico '----------------------------------------------- ' el nombre del ensamblado --> System.Reflection.MethodBase.GetCurrentMethod.Module.Name ' el nombre de la clase -----> System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name ' el nombre de la función ---> System.Reflection.MethodBase.GetCurrentMethod.Name Private Const CADENA_WIN_LEFT As String = "Left" Private Const CADENA_WIN_TOP As String = "Top" Private Const CADENA_WIN_WIDTH As String = "Width" Private Const CADENA_WIN_HEIGHT As String = "Height" Private Const CADENA_WIN_TOP_MOST As String = "TopMost" Private Const CADENA_WIN_WINDOW_STATE As String = "WindowState" Private Const CADENA_WIN_WINDOW_VISIBILITY As String = "WindowsVisibility" ' Valores por defecto de la posición y tamaño de la ventana Private Const VALOR_WIN_LEFT As Integer = 20 Private Const VALOR_WIN_TOP As Integer = 20 Private Const VALOR_WIN_WIDTH As Integer = 460 Private Const VALOR_WIN_HEIGHT As Integer = 460 '---------------------------------------------------------- ''' <summary> ''' Lee (de un fichero tipo Ini) los valores (left, top, width, Height) de la ventana ''' </summary> Private Sub LeerDatosVentanaDeIni() Try ' Obtener el nombre del ensamblado (sin extension) como nombre de fichero INI ' Dim EnsambladoConExtension As String = System.Reflection.MethodBase.GetCurrentMethod.Module.Name Dim SoloNombreEnsambladoSinExtension As String = System.IO.Path.GetFileNameWithoutExtension(System.Reflection.MethodBase.GetCurrentMethod.Module.Name) ' Obtener el nombre de la clase que sera el nombre de la sección. ' La Sección de los valores INI que en este caso es el nombre de la clase Dim SeccionIni As String = System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name ' --------------------------------------- ' Instanciar el objeto que maneja el fichero Ini ' Observaciones: ' - Si el fichero no existe se crea ' - El fichero Ini se crea el directorio [ruta del ensamblado /DatosPrograma/ version 4 cifras] ' - El nombre es: [Nombre del ensamblado (sin extension) + extension INI] ' --------------------------------------- Using objIni As New Util.Ini.IniFile(SoloNombreEnsambladoSinExtension) '-------------------------------------- ' Posición del Form If objIni.GetDouble(SeccionIni, CADENA_WIN_LEFT, 0D) > 0D Then If Me.WindowStartupLocation <> WindowStartupLocation.CenterOwner Then ' si el formulario se lanza [CenterOwner], me interesa que se centre, pero ' también me interesa que recuerde el tamaño que tenia la ultima vez que se cerro ' de esta forma, evito que la posición guardada del formulario lo mueva a otro sitio Me.Left = objIni.GetDouble(SeccionIni, CADENA_WIN_LEFT, 0D) Me.Top = objIni.GetDouble(SeccionIni, CADENA_WIN_TOP, 0D) End If End If Me.Width = objIni.GetDouble(SeccionIni, CADENA_WIN_WIDTH, 450D) Me.Height = objIni.GetDouble(SeccionIni, CADENA_WIN_HEIGHT, 450D) ' ---------------- ' Estado TopMost Me.Topmost = objIni.GetBoolean(SeccionIni, CADENA_WIN_TOP_MOST, False) 'Me.MenuVentanaSiempreVisible.IsChecked = My.Settings.MainWindow_TopMost ' ---------------- ' WindowState ' ¡¡ Bug 2017/01/30 !! Problemas al leer. ' CType, espera recibir un numero para hacer la conversion de la enumeración System.Windows.WindowStat Me.WindowState = CType(objIni.GetInt32(SeccionIni, CADENA_WIN_WINDOW_STATE, 0), System.Windows.WindowState) ' ---------------- 'Visibility Enumeración 'Collapsed 2 'No mostrar el elemento y no reservar espacio para él en el diseño. 'Hidden 1 'No mostrar el elemento, pero reservar espacio para el elemento en el diseño. 'Visible 0 'Mostrar el elemento. Me.Visibility = CType(objIni.GetInt32(SeccionIni, CADENA_WIN_WINDOW_VISIBILITY, 0), System.Windows.Visibility) ' ------------------------------------------- ' Otras variables del form 'ControlDatosMailFicheros.LoadMemento() End Using Catch ex As System.Exception Dim mensajeError As String Using sw As New System.IO.StringWriter(System.Globalization.CultureInfo.CurrentCulture) sw.WriteLine("----") sw.WriteLine("{0,-16} [{1}]", "Fecha local ", DateTime.Now.ToString("F", System.Globalization.CultureInfo.CurrentCulture)) sw.WriteLine("ERROR {0} > {1} > {2}", System.Reflection.MethodBase.GetCurrentMethod.Module.Name, System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name, System.Reflection.MethodBase.GetCurrentMethod.Name) sw.WriteLine("Problemas durante el proceso de Lectura de datos de 'INI' ") sw.WriteLine("----") sw.WriteLine("Mensaje de error del sistema:") If ex Is Nothing Then sw.WriteLine("Problema, el valor de la excepción recibida es Null") Else If ex.GetType Is Nothing Then sw.WriteLine("Problema, el valor de la excepción [ex.GetType] recibida es Null") Else sw.WriteLine("GetType: [{0}]", ex.GetType.ToString) End If ' sw.WriteLine("Message: [{0}]", ex.Message) End If sw.WriteLine("/ Eof") sw.Flush() mensajeError = sw.ToString End Using ' Disparar el error Throw New Exception(mensajeError, ex) ' /FIN del error End Try ' / FIN Leer los datos de la configuración ' --------------------------------------------------- End Sub '---------------------------------------------------------- ''' <summary> ''' Guarda (en un fichero tipo Ini) los valores (left, top, width, Height) de la ventana ''' </summary> Private Sub GuardarDatosVentanaEnIni() ' --------------------------------------------------- Try ' Obtener el nombre del ensamblado (sin extension) para usarlo como nombre de fichero INI ' Dim EnsambladoConExtension As String = System.Reflection.MethodBase.GetCurrentMethod.Module.Name Dim SoloNombreEnsambladoSinExtension As String = System.IO.Path.GetFileNameWithoutExtension(System.Reflection.MethodBase.GetCurrentMethod.Module.Name) ' Obtener el nombre de la clase que sera el nombre de la sección. ' La Sección de los valores INI que en este caso es el nombre de la clase Dim SeccionIni As String = System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name ' --------------------------------------- ' Instanciar el objeto que maneja el fichero Ini ' Observaciones: ' - Si el fichero no existe se crea ' - El fichero Ini se crea el directorio [ruta del ensamblado /DatosPrograma/ version 4 cifras] ' - El nombre es: [Nombre del ensamblado (sin extension) + extension INI] ' --------------------------------------- Using objIni As New Util.Ini.IniFile(SoloNombreEnsambladoSinExtension) '--------------------------------------- ' Posición del Form '--------------------------------------- '--------------------------------------- If Me.WindowState = WindowState.Normal Then ' El form está en posición normal, guardar todos los datos objIni.WriteValue(SeccionIni, CADENA_WIN_LEFT, Me.Left) objIni.WriteValue(SeccionIni, CADENA_WIN_TOP, Me.Top) objIni.WriteValue(SeccionIni, CADENA_WIN_WIDTH, Me.Width) objIni.WriteValue(SeccionIni, CADENA_WIN_HEIGHT, Me.Height) Else '---------------------------------------------------------------- ' Si el formulario no está en modo normal ' es decir, esta minimizado, o maximizado, ' guardar los datos de la posición que tenía ' antes de minimizar o maximizar '---------------------------------------------------------------- '---------------------------------------------------------------- ' Bug 2023/10/12 (DIA DEL PILAR) ' Si la ventana esta minimizada y no ha estado nunca visible ' por ejemplo oculta con un NotifyIcon, [RestoreBounds] devuelve un valor infinito ' positivo o negativo (aun no se cuando ocurre cada valor) ' pero lo que hago es interceptarlo y grabar un valor coherente '---------------------------------------------------------------- ' Código refactorizado 'objIni.WriteValue(SeccionIni, CADENA_WIN_LEFT, Me.RestoreBounds.Left) 'objIni.WriteValue(SeccionIni, CADENA_WIN_TOP, Me.RestoreBounds.Top) 'objIni.WriteValue(SeccionIni, CADENA_WIN_WIDTH, Me.RestoreBounds.Width) 'objIni.WriteValue(SeccionIni, CADENA_WIN_HEIGHT, Me.RestoreBounds.Height) ' ------------------------------------------------------------ If Double.IsPositiveInfinity(Me.RestoreBounds.Left) = True OrElse Double.IsNegativeInfinity(Me.RestoreBounds.Left) = True Then objIni.WriteValue(SeccionIni, CADENA_WIN_LEFT, VALOR_WIN_LEFT) Else objIni.WriteValue(SeccionIni, CADENA_WIN_LEFT, Me.RestoreBounds.Left) End If If Double.IsPositiveInfinity(Me.RestoreBounds.Top) = True OrElse Double.IsNegativeInfinity(Me.RestoreBounds.Top) = True Then objIni.WriteValue(SeccionIni, CADENA_WIN_TOP, VALOR_WIN_TOP) Else objIni.WriteValue(SeccionIni, CADENA_WIN_TOP, Me.RestoreBounds.Top) End If If Double.IsPositiveInfinity(Me.RestoreBounds.Width) = True OrElse Double.IsNegativeInfinity(Me.RestoreBounds.Width) = True Then objIni.WriteValue(SeccionIni, CADENA_WIN_WIDTH, VALOR_WIN_WIDTH) Else objIni.WriteValue(SeccionIni, CADENA_WIN_WIDTH, Me.RestoreBounds.Width) End If If Double.IsPositiveInfinity(Me.RestoreBounds.Height) = True OrElse Double.IsNegativeInfinity(Me.RestoreBounds.Height) = True Then objIni.WriteValue(SeccionIni, CADENA_WIN_HEIGHT, VALOR_WIN_HEIGHT) Else objIni.WriteValue(SeccionIni, CADENA_WIN_HEIGHT, Me.RestoreBounds.Height) End If End If ' ------------------------------------------- ' Estado TopMost objIni.WriteValue(SeccionIni, CADENA_WIN_TOP_MOST, Me.Topmost) 'Me.MenuVentanaSiempreVisible.IsChecked = My.Settings.MainWindow_TopMost ' --------------- ' WindowState objIni.WriteValue(SeccionIni, CADENA_WIN_WINDOW_STATE, Me.WindowState.ToString) ' --------------- ' WindowState objIni.WriteValue(SeccionIni, CADENA_WIN_WINDOW_VISIBILITY, Me.Visibility.ToString) ' ------------------------------------------- ' Otras variables del form ' 'ControlDatosMailFicheros.SaveMemento() ' ------------------------------------------- objIni.Flush() End Using Catch ex As System.Exception Dim mensajeError As String Using sw As New System.IO.StringWriter(System.Globalization.CultureInfo.CurrentCulture) sw.WriteLine("----") sw.WriteLine("{0,-16} [{1}]", "Fecha local ", DateTime.Now.ToString("F", System.Globalization.CultureInfo.CurrentCulture)) sw.WriteLine("ERROR {0} > {1} > {2}", System.Reflection.MethodBase.GetCurrentMethod.Module.Name, System.Reflection.MethodBase.GetCurrentMethod.DeclaringType.Name, System.Reflection.MethodBase.GetCurrentMethod.Name) sw.WriteLine("Problemas durante el proceso de escritura de datos de 'INI' ") sw.WriteLine("----") sw.WriteLine("Mensaje de error del sistema:") If ex Is Nothing Then sw.WriteLine("Problema, el valor de la excepción recibida es Null") Else If ex.GetType Is Nothing Then sw.WriteLine("Problema, el valor de la excepción [ex.GetType] recibida es Null") Else sw.WriteLine("GetType: [{0}]", ex.GetType.ToString) End If ' sw.WriteLine("Message: [{0}]", ex.Message) End If sw.WriteLine("/ Eof") sw.Flush() mensajeError = sw.ToString End Using ' Disparar el error Throw New Exception(mensajeError, ex) ' /FIN del error End Try ' / FIN guardar los datos en la configuración ' --------------------------------------------------- End Sub #End Region