Etiqueta: ComoNET

  • Recomendaciones para adaptar los controles (y la fuente) en aplicaciones WPF

    Nota: (02/Ene/2019)
    Ya puedes ver la segunda parte de este artículo, principalmente con el código para C# y un par de cambios en el código XAML, así como un consejo sobre la diferencia en los valores de Padding y Margin entre el código XAML y el de las hojas de estilo CSS.
    He puesto un ZIP (al final de esta página) con el código completo para Visual Basic y C#.

    Pues eso… lo primero, lo primero: ¡FELIZ AÑO NUEVO! 🙂
    Aunque esto lo estoy escribiendo el 31 de diciembre de 2018 (17:21), pero lo pondré para que se publique el 1 de enero de 2019… eso si es que me da tiempo a escribirlo entero hoy… guiño

    Pero te digo lo que contendrá esto que acabas de empezar a leer: Unas indicaciones sobre cómo añadir los controles para que al cambiar el tamaño del formulario o ventana, éstos se adapten al nuevo tamaño, y de paso, veremos cómo cambiar también el tamaño de las fuentes y comprobaremos que como ocurría en las aplicaciones de Windows Forms (o casi) los controles también se adaptan al nuevo tamaño.

    Lo más importante es: NO ASIGNAR FUENTES PERSONALIZADAS a los controles (al menos en el tipo de fuente), no, no te estoy gritando, es que quiero que quede claro, ya que esa ha sido una de mis pesadillas… y después resulta que todo es más fácil si no usaba mis propias fuentes… guiño

    La batallita del Guille o los antecedentes

    Para que no te lleves a engaños, te voy a explicar cómo yo lo estoy haciendo ahora y sobre todo pensando que siempre (o casi) he programado con los formularios usando WinForm de Visual Studio y con Visual Basic en un 99,99% de las aplicaciones que he empezado a hacer, después las convierto a C#, pero las empiezo con Visual Basic.

    Cuando diseño el formulario, suelo poner los controles en sitios específicos, si viene al caso suelo usar controles TabControl y GroupBox y escasamente paneles y casi nunca TableLayoutPanel, que ya me he dado cuenta de que no existe el equivalente en WPF y por tanto, las veces que lo he usado para facilitar el paso de WinForms a WPF, pues… no me ha servido de mucho 🙂

    Nota:
    El TableLayoutPanel tiene como equivalencia el Grid de los controles de WPF.

    Para facilitar la transición de WinForms a WPF/XAML he probado de todo, lo más cercano a lo que quería era usar Canvas y posicionar directamente los controles donde yo los quería, pero esto no vale… ya que lo que siempre he ido buscando es poder adaptar los controles al cambiar el tamaño del formulario.

    Lo mejor que he encontrado es el control Grid, que si se tiene cuidado al añadir los controles dentro y se configuran (más o menos) bien las definiciones de filas y columnas (y el nombre de la fuente o lo que es lo mismo la propiedad FontFamily) se pueden conseguir cosas decentemente aceptables. Y eso es lo que te voy a explicar aquí… 🙂

    Como verás, te estoy poniendo enlaces a las clases que menciono en la documentación de Microsoft y con referencia a .NET 4.7.2 (espero que tarden en romper los enlaces 🙂 )

    Una aplicación WPF de ejemplo (y de paso útil)

    El otro día pensé hacer una aplicación para copiar el contenido (las fotos) de los dos móviles que tengo y pensé en complicarme la vida haciendo la aplicación con WPF guiño

    No voy a entrar en detalles, pero quise acceder al contenido de los móviles, pero no hubo forma, y eso que busqué cómo hacerlo, pero… no di con la tecla, así que… se me ocurrió crear dos carpetas en un disco externo de 5 TB que tengo y desde ahí copiarlas al portátil. El hacer este paso intermedio es porque yo guardo las fotos en una carpeta del año y dentro de esa carpeta las fotos las guardo según el día y el móvil usado. Por ejemplo, para las fotos hechas hoy día 31 de diciembre crearía una carpeta llamada 12 31 1 iP7 para el iPhone y otra llamada 12 31 2 P2XL para el Pixel 2. El 1 y el 2 es porque cuando voy creando carpetas del mismo día las voy enumerando, ya que cuando tenía la GoPro (R.I.P.) también las añadía a una carpeta. Ahora al hacer la aplicación que automatiza la creación de las carpetas he dejado como fijo el 1 para el iPhone de Apple y el 2 para el Pixel de Google.

    El diseño empecé haciéndolo con Grid y creando 2 filas y dos columnas, con idea de en la parte superior izquierda poner un grupo de controles para el iPhone con el directorio donde están las fotos, un botón de seleccionar y un ListView mostrando las fotos a copiar; y en el de la derecha los mismos controles para el Pixel 2. Y en la parte inferior la selección de la fecha desde la que quiero empezar a copiar y el directorio donde almacenarlas, aparte de un ListView mostrando las carpetas creadas y algunas cosillas más, que te mostraré ahora cuando veas el formulario en tiempo de diseño.

    El formulario en tiempo de diseño

    Figura 1. La ventana en tiempo de diseño con WPF.
    Figura 1. La ventana en tiempo de diseño con WPF.

    Como puedes ver en la figura 1, están los controles que te he comentado, aparte el botón de Iniciar Copia, y las cajas de texto para indicar el formato de los nombres de los directorios según sean para el iPhone o para el Pixel 2. Además del habitual botón para Salir o cerrar la aplicación, cosas de la costumbre, ya que en realidad no es necesario, pero bueno…

    Fíjate también que he añadido un menú llamado Tamaño y Fuente, que nos servirá para hacer las pruebas de cambiar el tamaño de la fuente y del formulario o ventana (me acostumbraré a decir ventana, ya que en WPF es una clase Window y no un Form como en WinForms).

    En los tres paneles que hay en esa ventana he usado un control del tipo GroupBox. Cada uno de esos contenedores definen también un Grid. Y los controles están distribuidos en las columnas y filas de cada una de esas rejillas.

    Uno de los problemas que me encontré fue que en WPF no existe el equivalente a FolderBrowserDialog de System.Windows.Forms. y se ve que no hay equivalencia, lo más que he encontrado es un control de usuario que hace las veces (o eso dice la página donde lo encontré, pero no he llegado a probarlo). Buscando más por internet, he leído que en el espacio de nombres Microsoft.Win32 existen clases para acceder a los diálogos comunes, al menos al de selección de ficheros, pero tampoco lo he comprobado (acabo de mirarlo y no, no hay para seleccionar directorios), aunque me lo he apuntado para ver cómo funciona y qué es lo que hay, pero eso será tema de otro artículo.

    Para poder usar el FolderBrowserDialog debes añadir una referencia a System.Windows.Forms y si es necesario añadir una importación del espacio de nombres, yo he optado por añadir esa importación llamándola frm de esa forma estaré seguro que las clases y demás cosas que define no se mezclan con las clases que se recomiendan usar con las aplicaciones de WPF.

    Esta sería la declaración de ese espacio de nombres (por ahora solo en VB ya que aún no tengo convertida la aplicación en C#, pero lo haré guiño)

    ' Para FolderBrowserDialog y OpenFileDialog
    Imports frm = System.Windows.Forms
    

    Y ya que estoy con esto, me vas a permitir que antes de ver todo el tema del diseño te muestre el código para seleccionar el directorio, ya que es interesante, porque como los tres botones Seleccionar hacen lo mismo, solo que interactúan con distintas cajas de texto, pues… ahí tienes un truco, que lo mismo ya lo conoces, pero… guiño

    Private Sub BtnSel_Click(sender As Object, e As RoutedEventArgs) Handles _
                                        btnSel1.Click, btnSel2.Click, btnSelDest.Click
        Dim dir = ""
        If sender Is btnSel1 Then
            dir = txtDir1.Text
        ElseIf sender Is btnSel2 Then
            dir = txtDir2.Text
        Else
            dir = txtDirDest.Text
        End If
    
        Dim desc = If(sender Is btnSel1, " para la unidad 1",
                        If(sender Is btnSel2, " para la unidad 2", "de destino"))
    
        Dim fb As New frm.FolderBrowserDialog
        With fb
            .Description = "Selecciona el directorio" & desc
            .RootFolder = Environment.SpecialFolder.MyComputer
            .SelectedPath = dir
            .ShowNewFolderButton = False
            If .ShowDialog() = frm.DialogResult.OK Then
                dir = .SelectedPath
                If sender Is btnSel1 Then
                    txtDir1.Text = dir
                    My.Settings.dir1 = dir
                    llenar(lvFics1, dir)
                ElseIf sender Is btnSel2 Then
                    txtDir2.Text = dir
                    My.Settings.dir2 = dir
                    llenar(lvFics2, dir)
                Else
                    My.Settings.dir3 = dir
                    txtDirDest.Text = dir
                End If
            End If
        End With
    End Sub
    

    No te voy a explicar nada más, espero que sea fácil de comprender viendo el código.

    Pasemos al tema del diseño.

    Las cosas a tener en cuenta al diseñar usando el control Grid

    Lo importante es no asignar estas cosas a los controles que pongamos dentro del Grid: FontName, Width ni Height. Dejar el valor predeterminado para HorizontalAlignment y VerticalAlignment que es Stretch. Ni al Grid tampoco.

    Con este sencillo consejo conseguirás todo lo que deseas conseguir, ¡seguro! guiño

    Seguramente te preguntarás: ¿Cómo posicionar los controles donde queramos?
    Pues… usando Grid.Column y Grid.Row para indicar en que cuadrícula estará cada control y si viene al caso (por ejemplo para que la caja de textos o el ListView) ocupen más espacio usaremos Grid.ColumnSpan y Grid.RowSpan.

    Además de la propiedad Margin, para que deje un poco de espacio donde lo necesitemos.

    Veamos cómo está definido el Grid del panel superior izquierdo.

    Este es el código XAML del diseñador de la ventana.

    <GroupBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1" 
              Header="Unidad 1" Margin="0">
        <Grid DockPanel.Dock="Top" Margin="0" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition Height="4"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Label Content="Directorio:" Grid.ColumnSpan="1" Margin="0" />
            <TextBox Name="txtDir1" Grid.Row="0" Grid.Column="1" 
                     Grid.ColumnSpan="3" Grid.RowSpan="1"
                     Margin="0,6,0,0" TextWrapping="NoWrap" Text="S:\iPhone"/>
            <Button Name="btnSel1" Content=" Seleccionar... " 
                    Grid.Column="4" Grid.ColumnSpan="1"
                    Margin="8,6,0,0" />
            <ListView Name="lvFics1" Grid.Row="2"
                      Grid.ColumnSpan="5" Grid.RowSpan="1" 
                      Margin="0,8,0,0">
                <ListView.View>
                    <GridView >
                        <GridViewColumn Header="Archivo" Width="220"
                                        DisplayMemberBinding="{Binding Nombre}"/>
                        <GridViewColumn Header="Fecha" Width="Auto"
                                        DisplayMemberBinding="{Binding Fecha}"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>
    
     </GroupBox>
    

    El GruopBox está contenido en el Grid principal, el valor de Grid.Row = «1» es porque en la fila cero está el menú. Aquí le he puesto Grid.Column = «0» pero realmente no es necesario, ya que si no se indica qué fila o columna ocupa, será siempre cero. Lo mismo ocurre con Grid.ColumnSpan = «1», como mínimo espanea (¿existe ese palabro?) una columna o una fila.

    Los controles de este GroupBox están contenidos en un Grid. Yo he definido 5 columnas y 3 filas. El poner 5 columnas, principalmente es para que el TextBox ocupe 3 y el Label de la izquierda y el Button de la derecha ocupen una columna cada uno. El ListView ocupa una fila completa (antes tenía más filas, pero no es necesario). La fila pequeña, con 4 de alto es para dejar un espacio entre los controles de arriba con la lista, pero esto también se podría haber solucionado con los márgenes, pero… bueno… ahí está 🙂

    Con los Margin lo que logro es que tanto el botón como el TextBox no se hagan muy altos y se alineen mejor con la etiqueta.

    Como puedes comprobar, el único Width (ancho) específico que he puesto es el de la primera columna del ListView. Pero más que nada para que al verlo en tiempo de diseño o al iniciar la aplicación y estando vacío se vea más espacioso, ya que al llenarse, se ajustará al texto que contenga.

    En las figuras 2 y 3 tienes la diferencia de asignar Auto al tamaño de la columna (figura 2) y ponerlo como te muestre en este código con 220 de ancho (figura 3). En el panel derecho está en Auto y se ajusta al nombre de la imagen.

    Figura 2. El ancho de las columnas del ListView se ajustan automáticamente al contenido.
    Figura 2. El ancho de las columnas del ListView se ajustan automáticamente al contenido.
    Figura 3. El ancho de la columna izquierda está puesto a 220 y el de la derecha en Auto.
    Figura 3. El ancho de la columna izquierda está puesto a 220 y el de la derecha en Auto.

    La cuestión es que prefiero darle como mínimo unos 200 o 220 pixeles de ancho guiño

    Otro punto a resaltar en ese código es cómo se maneja el contenido del ListView, no voy a entrar en mucho detalle porque ya lo expliqué en mi sitio hace 6 años y 34 días (el 27 de diciembre de 2012) en este artículo: Ejemplo de ListView y equivalencia a subitems, solo decirte que he creado dos clases (dentro de la clase principal) para manejar dicho contenido.

    Este es el código de las dos clases (por ahora en VB):

    Class ItemDir
        Public Property Nombre As String
    End Class
    
    Class ItemFic
        Inherits ItemDir
        Public Property Fecha As Date
    End Class
    

    Y estas clases las uso de esta forma a la hora de asignar el contenido de los ListView ligados con los directorios de mas fotos y con el de las carpetas creadas, por eso hay dos clases, una para los directorios y la segunda para los ficheros. El código para mostrar las fotos está en el método llenar.

    ''' <summary>
    ''' Llenar un listview con los ficheros del directorio
    ''' </summary>
    Private Sub llenar(lvFiles As ListView, dir As String)
        lvFiles.Items.Clear()
        Dim dirI = New DirectoryInfo(dir)
        If dirI.Exists = False Then Exit Sub
    
        For Each fi In dirI.GetFiles()
            Dim lvi = New ItemFic With {.Nombre = fi.Name, .Fecha = fi.LastWriteTime}
            lvFiles.Items.Add(lvi)
        Next
    End Sub
    

    Como ves, solo se añade el objeto creado a cada elemento del ListView y el Binding, concretamente el DisplayMemberBinding, se encarga del resto.

    La parte inferior de la ventana es más de lo mismo. No te la voy a explicar al completo porque ya sería muy pesado, podrás ver el código completo cuando lo publique, aunque no sé si esperaré a que esté el de C# o publicaré primero el de Visual Basic y después el de C#.

    Cambiar el tamaño de la ventana principal y las letras

    En el menú he puesto dos opciones (ahora te enseño el código XAML) una para poder cambiar el tamaño de la ventana principal y el otro para cambiar el tamaño de las letras.

    A dicho menú le he añadido una etiqueta, una caja de textos y un botón (esto también se puede hacer en los menús de WinForms).

    Este es el código XAML de la definición del menú principal y los dos submenús:

    <Menu x:Name="menu" Grid.Row="0" Grid.Column="0" 
          Grid.ColumnSpan="2" 
          Height="24" Margin="0,0,0,4">
        <MenuItem x:Name="mnuCambiarFuente" 
                  Header="_Tamaño y Fuente">
            <MenuItem Header="Tamaño _Ventana">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Label Content="Cambiar tamaño" 
                           ToolTip="Puedes usar un valor negativo para reducir" />
                    <TextBox x:Name="txtTamañoVentana" Text="0,2" 
                             Grid.Column="1" Width="40"
                             Margin="8,6,0,0"/>
                    <Button x:Name="btnAplicar" Content=" Aplicar " 
                            Click="BtnAplicarVentana_Click"
                            Grid.Column="2" Margin="8,6,0,0" />
                </Grid>
            </MenuItem>
            <MenuItem Header="Tamaño _Fuente">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Label Content="Cambiar tamaño" 
                           ToolTip="Puedes usar un valor negativo para reducir" />
                    <TextBox x:Name="txtTamañoFuente" Text="1,0" 
                             Grid.Column="1" Width="40"
                             Margin="8,6,0,0"/>
                    <Button x:Name="btnAplicarFuente" Content=" Aplicar " 
                            Click="BtnAplicarFuente_Click"
                            Grid.Column="2" Margin="8,6,0,0" />
                </Grid>
            </MenuItem>
        </MenuItem>
    </Menu>
    

    Como ves, hay Grid hasta en la sopa 🙂

    El guión bajo en el Header de los menús es para indicar que se resalte esa letra (la que le sigue) al pulsar la tecla Alt, es lo mismo que el ampersand (&) en las aplicaciones de WinForms.

    Veamos el código para Visual Basic para cada uno de los botones:

    Private meHeight As Double
    Private meWidth As Double
    Private meFontSize As Double
    
    Private Sub BtnAplicarVentana_Click(sender As Object, e As RoutedEventArgs)
        ' Para cambiar el tamaño a partir del valor inicial
        If meHeight = 0 Then
            meHeight = Me.Height
        End If
        If meWidth = 0 Then
            meWidth = Me.Width
        End If
    
        Dim d As Double = 0
        Double.TryParse(txtTamañoVentana.Text, d)
        Me.Height = nuevoTamaño(meHeight, d)
        Me.Width = nuevoTamaño(meWidth, d)
    End Sub
    
    Private Sub BtnAplicarFuente_Click(sender As Object, e As RoutedEventArgs)
        If meFontSize = 0 Then
            meFontSize = Me.FontSize
        End If
        Dim d As Double = 0
        Double.TryParse(txtTamañoFuente.Text, d)
        Me.FontSize = meFontSize + d
    End Sub
    

    Como ves ninguno de los dos métodos de evento tienen la cláusula Handles, esto es porque en el código XAML ya hemos indicado qué método se usará para cada uno de los botones.

    En el tamaño de la fuente simplemente añadimos el valor indicado al tamaño que ya tuviera, mientras que el alto y ancho de la ventana usamos un incremento porcentual (o casi).

    Este es el código del método nuevoTamaño:

    Private Function nuevoTamaño(actual As Double, incremento As Double) As Double
        Return actual * incremento + actual
    End Function
    

    En el código hay también unas llamadas al API de Windows para ocultar el botón Maximizar de la ventana, ya que de forma nativa WPF no tiene forma de ocultar ese botón, al menos si la ventana se puede cambiar de tamaño, que es el caso de la que usa este programa, que tiene el valor CanResizeWithGrip asignado a la propiedad ResizeMode.
    Aunque como leí en uno de los foros que estuve consultando: ¿Para qué quieres quitar el botón maximizar si tu formulario puede cambiar de tamaño? Y tiene razón, pero… en fin… costumbres heredadas de los formulari0s de Windows Forms guiño

    Este es el código para Visual Basic de esas API

    '--------------------------------------------------------------------------
    ' Código para quitar el botón de maximizar
    ' Adaptado de la versión de C# publicada en:
    ' https://stackoverrun.com/es/q/5101958
    ' 
    <DllImport("user32.dll")>
    Private Shared Function GetWindowLong(ByVal hWnd As IntPtr,
                                          ByVal nIndex As Integer) As Integer
    End Function
    
    <DllImport("user32.dll")>
    Private Shared Function SetWindowLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer,
                                          ByVal dwNewLong As Integer) As Integer
    End Function
    
    Private Const GWL_STYLE As Integer = -16
    Private Const WS_MAXIMIZEBOX As Integer = &H10000
    
    Private Sub Window_SourceInitialized(ByVal sender As Object,
                                         ByVal e As EventArgs) Handles Me.SourceInitialized
        Dim hwnd = New WindowInteropHelper(CType(sender, Window)).Handle
        Dim value = GetWindowLong(hwnd, GWL_STYLE)
        SetWindowLong(hwnd, GWL_STYLE, CInt((value And Not WS_MAXIMIZEBOX)))
    End Sub
    '
    '--------------------------------------------------------------------------
    

    Y esto es todo.

    Son las 22:55 del lunes 31 de Diciembre de 2018, pero… ya sabes lo pondré en automático para que se publique maña uno de enero de 2019 guiño

    Y mañana o pasado pondré el enlace al código completo para Visual Basic y espero que también el de C#.

    ¡Feliz Año Nuevo!

    Nos vemos.
    Guillermo


    El código completo del ejemplo
    (solución para Visual Studio usando .NET 4.7.2)

    El ZIP con el código completo (una solución de Visual Studio 2017 con los proyectos de Visual Basic y C#

    El zip: Copiar_archivos_de_2_unidades.zip (214 KB)

    MD5 Checksum: 2E3B3F34410F37D051DC5AED8FBBCE1A


  • Cambiar el tamaño de los controles de un formulario automáticamente

    Pues eso… que lo que publiqué anteayer sobre ajustar automáticamente la fuente de un formulario según la configuración del sistema (Cambiar automáticamente las fuentes de nuestro formulario a las de Windows), estaba muy bien, pero no era lo que yo andaba buscando… yo lo que quería era que los controles también cambiaran.

    La batallita del Guille

    Y me puse a ello… como soy un poquillo torpe, ya sabes, no tengo estudios informáticos ni de otros tipos, pues… empecé a escribir el código para cambiar cada uno de los controles del formulario, y por supuesto también el del formulario, ahí si llego ;-). Sí, lo hice, escribí todo el código, con métodos recursivos, etc. En fin… :-/

    La cuestión es que pensé que también estaría guay (cool) poder cambiar las fuentes, así todo estaría más grande… o más pequeño, ya que en el formulario de prueba (ver figura 1) puse la opción de ampliar o reducir.

    Figura 1. La aplicación en modo inicial (usando la configuración del usuario de Windows)
    Figura 1. La aplicación en modo inicial (usando la configuración del usuario de Windows)

    Pero aquello era un caos, cuando ampliaba, lo hacía según el ancho y el alto y claro, si los controles no tienen todos los Anchor necesarios, pues… se te quedaba espacio vacío por aquí y por allá… no tengo capturas (¡gracias a Dios! si no, ya no vendrás más a visitarme 😛 )

    Con decirte que hice hasta una clase para que manejase el tema este del cambio de los controles y así hacerlo más cool… con un evento que indicaba que se estaban haciendo los cambios, etc.; sí, lo que yo te diga, en fin…

    Bueno, vale… otro día hago la captura y así ves cómo se puede uno embrollar con lo que después resultó ser algo muy simple de codificar.

    La solución (muy fácil y sencilla)

    Pues eso, la solución, para esto de que se cambien los controles de tamaño según indiquemos, es muy simple, tanto que… bueno… mejor me callo… ¡ay zeñó!

    Y todo surgió porque los controles quedaban muy mal al cambiar el tamaño, y me dio por probar (esto aún podría estar en la batallita del Guille, pero bueno…) quitando el cambio del tamaño del formulario y de los controles, y dejar solo el cambio del tamaño de la fuente y… ¡voilà, todo va a la perfección!. No sabes la de cabezazos que me hubiera dado contra la pared… menos mal que ahora me he pelado al 2 y no era plan… 🙂

    Te muestro un par de capturas con el 25% de ampliación y el 50% para que te hagas una idea de lo bien que funciona 🙂 y después te muestro el código tanto para Visual Basic como para C#.

    Figura 2. Aspecto de la aplicación en funcionamiento usando el 25% de ampliación
    Figura 2. Aspecto de la aplicación en funcionamiento usando el 25% de ampliación
    Figura 3. Aspecto de la aplicación en funcionamiento usando un 50% de ampliación
    Figura 3. Aspecto de la aplicación en funcionamiento usando un 50% de ampliación

    Nota:
    Según parece, la imagen 1 y 2 son iguales, pero si te fijas en la figura 3, en la que muestro el IDE de Visual Studio, comprobarás que el tamaño del formulario es significativamente más grande que el mostrado en el diseñador de VS.

    Pulsa en las imágenes para verlas en grande.

    El código para cambiar el tamaño de los controles de un formulario

    Comentarte que todo está en cambiar el tamaño de la fuente del formulario, por tanto debes tener asignado el valor Font a la propiedad AutoScaleMode del formulario (es el valor predeterminado al crear un nuevo formulario).

    El código para Visual Basic

    '--------------------------------------------------------------------------
    ' Métodos para cambiar el tamaño del formulario
    ' y de sus controles
    '--------------------------------------------------------------------------
    
    ''' <summary>
    ''' Cambia el tamaño del formulario (o control) indicado,
    ''' ampliando o reduciendo según un porcentaje.
    ''' Recomendado es solo cambiar el formulario.
    ''' </summary>
    Private Sub cambia(ctr As Control, ampliar As Boolean, por As Integer)
        If ampliar = False Then
            por = -por
        End If
    
        '----------------------------------------------------------------------
        ' El ancho y alto es mejor no cambiarlo,
        ' las proporciones de los controles se verán bien
        ' pero si el Anchor no está "pensado" para todos
        ' los controles, quedarán espacios vacíos con
        ' respecto al diseño original.
        ' Al cambiar la fuente del formulario,
        ' el aspecto original no se pierde.
        '----------------------------------------------------------------------
    
    
        '----------------------------------------------------------------------
        ' Al cambiar el tamaño de la fuente del formulario
        ' los controles se adaptan al nuevo tamaño de fuente,
        ' sin perder el aspecto del diseño original.
        '
        ' El formulario debe tener asignada la propiedad
        ' AutoScaleMode = Font
        '----------------------------------------------------------------------
    
        '
        ' Se debería poner un máximo y mínimo a las fuentes
        '
    
        Dim fntSize = ctr.Font.Size
        fntSize = calculaPorcentaje(fntSize, por)
    
        ' No admitir valores menores de uno
        ' ni valores mayores de 3 veces la fuente
        If fntSize < 1 OrElse fntSize > ctr.Font.Size * 3 Then
            fntSize = ctr.Font.Size
        End If
    
        ctr.Font = New Font(ctr.Font.FontFamily,
                            fntSize, ctr.Font.Style,
                            ctr.Font.Unit, ctr.Font.GdiCharSet,
                            ctr.Font.GdiVerticalFont)
    End Sub
    
    ''' <summary>
    ''' Calcula el porcentaje a partir de un valor Single,
    ''' para el tamaño de la fuente.
    ''' </summary>
    Private Function calculaPorcentaje(valor As Single, porcentaje As Integer) As Single
        Return valor + (valor * porcentaje / 100)
    End Function
    

    El código para C#

    // --------------------------------------------------------------------------
    // Métodos para cambiar el tamaño del formulario
    // y de sus controles
    // --------------------------------------------------------------------------
    
    /// <summary>
    /// Cambia el tamaño del formulario (o control) indicado,
    /// ampliando o reduciendo según un porcentaje.
    /// Recomendado es solo cambiar el formulario.
    /// </summary>
    private void cambia(Control ctr, bool ampliar, int por)
    {
        if (ampliar == false)
            por = -por;
    
        // ----------------------------------------------------------------------
        // El ancho y alto es mejor no cambiarlo,
        // las proporciones de los controles se verán bien
        // pero si el Anchor no está "pensado" para todos
        // los controles, quedarán espacios vacíos con
        // respecto al diseño original.
        // Al cambiar la fuente del formulario,
        // el aspecto original no se pierde.
        // ----------------------------------------------------------------------
    
    
        // ----------------------------------------------------------------------
        // Al cambiar el tamaño de la fuente del formulario
        // los controles se adaptan al nuevo tamaño de fuente,
        // sin perder el aspecto del diseño original.
        // 
        // El formulario debe tener asignada la propiedad
        // AutoScaleMode = Font
        // ----------------------------------------------------------------------
    
        // 
        // Se debería poner un máximo y mínimo a las fuentes
        // 
    
        var fntSize = ctr.Font.Size;
        fntSize = calculaPorcentaje(fntSize, por);
    
        // No admitir valores menores de uno
        // ni valores mayores de 3 veces la fuente
        if (fntSize < 1 || fntSize > ctr.Font.Size * 3)
            fntSize = ctr.Font.Size;
    
        ctr.Font = new Font(ctr.Font.FontFamily, fntSize, 
                            ctr.Font.Style, ctr.Font.Unit, 
                            ctr.Font.GdiCharSet, ctr.Font.GdiVerticalFont);
    }
    
    /// <summary>
    /// Calcula el porcentaje a partir de un valor Single,
    /// para el tamaño de la fuente.
    /// </summary>
    private float calculaPorcentaje(float valor, int porcentaje)
    {
        return valor + (valor * porcentaje / 100);
    
    }
    

    Al método cambia le pasamos el control o formulario (recomendable el formulario) en el que queremos hacer el cambio de tamaño, el valor de ampliar es si queremos ampliar (true) o reducir (false) y por es el porcentaje, que es un valor entero y puede ser 0 (cero) para no hacer nada o dejarlo como estaba, o cualquier otro tamaño (no te recomiendo un valor mayor de 175 si no, pues… te faltará pantalla… 🙂

    Como puedes comprobar, he puesto una comprobación para que el tamaño de la fuente no sea menor de 1 ni mayor de 3 veces el tamaño inicial.
    Si quieres cambiarlo, hazlo, pero al menos deja la comprobación de que no sea menor de uno (o cero) ya que so es cero (0) te dará error.

    Y ahora pasemos al código que he usado para cambiar el tamaño del formulario y sus controles.

    El código de ejemplo para cambiar el tamaño según un porcentaje

    En el ejemplo que he hecho (ver cualquiera de las figuras) he puesto la opción de Ampliar o Reducir, un combo con los porcentajes a usar y el botón Cambiar para aplicar el cambio.

    También he puesto un botón para restaurar el tamaño al inicial (Restablecer fuente) pero en realidad no es necesario, ya que al indicar 0% se deja todo como estaba al inicio.

    Lo que si hago es que ese porcentaje sea fijo, es decir, siempre que pulses en, por ejemplo, 50% se cambiará al 50% del valor inicial.

    Te aclaro esto porque inicialmente (ya sabes: la torpeza del Guille) ese porcentaje lo aplicaba al valor que ya hubiese de antes, por tanto, si inicialmente lo habías ampliado al 10% y después seleccionabas el 50% este último se aplicaba sobre la ampliación (o reducción) anterior, y… bueno, que no quedaba nada bien.

    Para conseguir esto, que siempre se aplique el porcentaje según la fuente inicial, he creado una variable llamada miWinFont, esa es la fuente que tiene el sistema, ya que he usado el mismo código que te mostré en el artículo anterior (Cambiar automáticamente las fuentes de nuestro formulario a las de Windows) para utilizar la fuente indicada en Windows.

    La asignación de esa variable se hace al inicio del programa (en el constructor) y después se usa para reiniciar el tamaño de la fuente antes de hacer el cambio, ahora lo verás en el código.

    Como tip te puedo decir que puedes usar otra forma de hacerlo, por ejemplo usando el valor de fntSize a partir del valor de miWinFont en vez de la fuente del formulario, pero no he cambiado el código porque eso se me acaba de ocurrir mientras escribo el artículo, y ya no es plan de cambiar el código 😉

    Vamos a lo que vamos.

    Empecemos con el código de Visual Basic, pero tanto en el de VB como en el de C# lo que hago es lo mismo: hacer el cambio usando el evento Click del botón Cambiar.

    El código para Visual Basic de los métodos de evento

    '
    ' Los métodos de evento para el cambio de la fuente
    '
    Private Sub btnCambiarTamaño_Click(sender As Object, e As EventArgs) Handles _
                                                btnCambiarTamaño.Click
        ' Cambiar solo el tamaño de la fuente del formulario
        txtInfo.Text = "Cambiando el tamaño de los controles..."
        Application.DoEvents()
    
        Me.Hide()
    
        ' restablecer a la fuente inicial
        Me.Font = miWinFont
    
        ' hacer el cambio de tamaño
        cambia(Me, optAmpliar.Checked, CInt(cboTamaños.SelectedItem))
    
        Me.Show()
    
        txtInfo.Text = "Cambiado el tamaño de los controles."
        Application.DoEvents()
    End Sub
    
    Private Sub btnRestablecerFuente_Click(sender As Object, e As EventArgs) Handles _
                                                    btnRestablecerFuente.Click
        ' Restablecer siempre a la fuente del sistema
        ' si no queremos usar la del sistema,
        ' asignar al valor de miFuente (la original al diseñar)
        Me.Font = miWinFont
    
    
    End Sub
    

    Ahora después te muestro dónde declaro miWinFont y dónde la asigno.

    El código para C# de los métodos de evento

    //
    // Los métodos de evento para el cambio de la fuente
    //
    
    private void btnCambiarTamaño_Click(object sender, EventArgs e)
    {
        // Cambiar solo el tamaño de la fuente del formulario
        txtInfo.Text = "Cambiando el tamaño de los controles...";
        Application.DoEvents();
        this.Hide();
        
        // restablecer a la fuente inicial
        this.Font = miWinFont;
        
        // hacer el cambio de tamaño
        cambia(this, optAmpliar.Checked, Convert.ToInt32(cboTamaños.SelectedItem));
    
        this.Show();
        txtInfo.Text = "Cambiado el tamaño de los controles.";
        Application.DoEvents();
    
    }
    
    private void btnRestablecerFuente_Click(object sender, EventArgs e)
    {
        // Restablecer siempre a la fuente del sistema
        // si no queremos usar la del sistema,
        // asignar al valor de miFuente (la original al diseñar)
        this.Font = miWinFont;
    
    }
    

    Este es el código donde se define y se asigna miWinFont.

    Adivina cuál es el de VB y cuál el de C# 😉

    Public Class Form1
        ''' <summary>
        ''' La fuente del sistema
        ''' </summary>
        Private miWinFont As Font
    
    [...]
    
    '
    ' El constructor del formulario
    '
    Sub New()
    
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.
        miFont = Me.Font
    
        Me.Font = SystemFonts.IconTitleFont
        AddHandler SystemEvents.UserPreferenceChanged,
                    AddressOf SystemEvents_UserPreferenceChanged
        miWinFont = Me.Font
    
        infoFuentes()
    End Sub
    
    '
    ' Los métodos de evento para interactuar con la configuración de Windows
    '
    Private Sub SystemEvents_UserPreferenceChanged(ByVal sender As Object,
                                                   ByVal e As UserPreferenceChangedEventArgs)
        If e.Category = UserPreferenceCategory.Window Then
            Me.Font = SystemFonts.IconTitleFont
            miWinFont = Me.Font
        End If
    
        mostrarInfoForm()
        txtInfo.Text = String.Format("UserPreferenceChanged.Category = {0} ({1:HH:mm:ss}){2}{3}",
                                     e.Category.ToString(), Date.Now,
                                     Microsoft.VisualBasic.vbCrLf,
                                     txtInfo.Text)
    
    End Sub
    
    public partial class Form1 : Form
    {
        /// <summary>
        /// La fuente del sistema
        /// </summary>
        private Font miWinFont;
    
    [...]
    
    //
    // El constructor del formulario
    //
    public Form1()
    {
    
        // This call is required by the designer.
        InitializeComponent();
    
        // Add any initialization after the InitializeComponent() call.
        miFont = this.Font;
    
        this.Font = SystemFonts.IconTitleFont;
        SystemEvents.UserPreferenceChanged +=
                                    SystemEvents_UserPreferenceChanged;
        this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
    
        // Para que tenga dos m\f2étodos de evento el evento Load
        this.Load += new System.EventHandler(this.Form1_Load);
    
        miWinFont = this.Font;
    
        infoFuentes();
    }
    
    //
    // Los métodos de evento para interactuar con la configuración de Windows
    //
    
    private void SystemEvents_UserPreferenceChanged(object sender,
                                            UserPreferenceChangedEventArgs e)
    {
        if (e.Category == UserPreferenceCategory.Window) {
            this.Font = SystemFonts.IconTitleFont;
            miWinFont = this.Font;
        }
    
        mostrarInfoForm();
        txtInfo.Text = string.Format("UserPreferenceChanged.Category = {0}"+
                                     "({1:HH:mm:ss}){2}{3}",
                                     e.Category.ToString(), DateTime.Now,
                                     "\r\n", txtInfo.Text);
    
    }
    

    Nota:
    No te preocupes por la definición de los métodos infoFuentes ni mostrarInfoForm, ese código está en el ZIP que te pondré al final con el código completo.

    Y esto es todo… ya ves qué simple… 🙂

    Espero que te sea de utilidad, ¡’esa es siempre la idea!

    Nos vemos.
    Guillermo.

    P.S.
    Comentarte que en el código completo (sí, para VB y C#) la aplicación lo que hace es permitir que selecciones un fichero (archivo) y te muestre las propiedades, nombre completo, directorio, fecha de creación, etc.

    El código completo del ejemplo (solución para Visual Studio usando .NET 4.7.2)

    El ZIP con el código completo (una solución de Visual Studio 2017 con los proyectos de Visual Basic y C#

    ZIP: Cambiar tamaño controles.zip (254 KB)

    MD5 Checksum: 0B5DDD8A525D61647F10070EFC198DCA

    P.S. 2 (31/Dic/18)
    Aquí tienes un ejemplo de cómo hacer que el resto de formularios de nuestra aplicación utilicen el tamaño asignado en el formulario principal:
    Interceptar el cambio del tamaño de los controles de un formulario en otros formularios de la aplicación.

  • Cambiar automáticamente las fuentes de nuestro formulario a las de Windows

    Pues eso… que como ahora «ando liado» con dos monitores, uno, el principal en el portátil (laptop) y el segundo uno de más resolución, pues… resulta que quiero que en el monitor se vean las fuentes más grandes y también el formulario y los controles, y antes de empezar a hacer «manualidades» (adaptar las cosas por la cuenta de la vieja, es decir, a mano) me he decidido a buscar en Internet a ver si había algo y resulta que sí, que lo hay (o casi), seguramente habrá más cosas pero lo que en principio me ha parecido una buena opción es lo que he encontrado en Cómo: Responder a los cambios de las combinaciones de fuentes en una aplicación de Windows Forms, concretamente en la sección: Para usar la fuente del escritorio y responder a los cambios de esquema de fuentes y concretamente el código mostrado en: Para cambiar manualmente la combinación de fuentes en Windows XP.

    Por supuesto el código de ejemplo solo se muestra en C#, pero… no te preocupes aquí estoy yo para mostrarte cómo hacer eso que ahí dice en Visual Basic .NET (sí, y también en C# 😉 )

    El código básicamente es como está en esa página que te he indicado, lo único que yo he añadido al ejemplo que te voy a poner es la opción de mostrar las fuentes originales (iniciales del formulario) o bien usar las que Windows te indique.

    Para que te hagas una idea de lo que el código hace, te muestro dos capturas del formulario (Form1) en ejecución, la figura 1 es con las letras «normales» y la segunda captura (figura 2) es usando el código que hace que se adapte a las fuentes de Windows.

    Nota:
    Este código solo cambia el tamaño de las fuentes del formulario, no la de los controles.
    Al cambiar la fuente del formulario, este cambia también de tamaño y «reubica los controles».

     

    El formulario usando las fuentes normales
    Figura 1. El formulario con las fuentes normales


    El formulario usando las fuentes de Windows
    Figura 2. El formulario usando las fuentes de Windows

    No te asustes porque haya muchos controles 😉

    Es que ese formulario lo tengo para hacer unas pruebas para convertir de Windows Forms a WPF / XAML y tengo que probar con prácticamente todos los controles (al menos los que yo suelo usar), lo que importa es que veas que el formulario de la figura 2 ha cambiado de tamaño y también el contenido de algunos de los controles (salvo los menús y la barra de botones).


    No te voy a explicar en detalle cómo funciona esto, ya que en la página que te indiqué ya lo hacen 🙂 pero si te voy a mostrar el código necesario para que funcione así.


    Si estás usando Visual Basic tendrás que agregar el código del constructor (Sub New) (el IDE de Visual Studio agrega el contenido necesario de forma automática) y añadimos las siguientes líneas de código después de la llamada a InitializeComponent:

    El código de Visual Basic para el constructor (Sub New):

    Sub New()
    
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.
    
        Me.Font = SystemFonts.IconTitleFont
        AddHandler SystemEvents.UserPreferenceChanged, AddressOf SystemEvents_UserPreferenceChanged
    
    End Sub
    

    El código de C# para el constructor:

    public Form1()
    {
        InitializeComponent();
    
        this.Font = SystemFonts.IconTitleFont;
        SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
        this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
    }
    

    Si te fijas un poco, en el código de VB no he puesto el «manenejador de eventos» para el evento Form.Closing, ya que no es necesario, aunque se podría haber hecho como en C#, pero si Visual Basic facilita la creación de eventos, ¿por qué no usarla? (ya sabes, en la declaración del evento se añade la cláusula Handles y a continuación el nombre del evento a «capturar».


    Te explico brevemente lo que hace ese código del constructor:

    Asigna a la fuente del formulario la que hay definida en Windows, esto funciona desde Windows XP hasta el actual Windows 10 (actual en las fechas que estoy escribiendo esto, es decir el 27 de diciembre de 2018).


    Después añade el manejador de eventos para la clase UserPreferenceChanged de SystemEvents, que es una clase definida en el espacio de nombres Microsoft.Win32, por tanto en el formulario hay que importar ese espacio de nombres, ya sabes:
    Imports Microsoft.Win32 para Visual Basic o using Microsoft.Win32; para C#.


    En C# también añade el controlador de eventos para el evento Form.Closing.


    En realidad en el evento Form.Closing solo se desliga el manejador de eventos para UserPreferenceChanged.

    Aquí tienes el código para VB y C#.

    El código para Visual Basic:

    Private Sub SystemEvents_UserPreferenceChanged(ByVal sender As Object,
                                                   ByVal e As UserPreferenceChangedEventArgs)
        If e.Category = UserPreferenceCategory.Window Then
            Me.Font = SystemFonts.IconTitleFont
        End If
    End Sub
    
    Private Sub Form1_FormClosing(ByVal sender As Object,
                                  ByVal e As FormClosingEventArgs) Handles Me.FormClosing
        RemoveHandler SystemEvents.UserPreferenceChanged,
                AddressOf SystemEvents_UserPreferenceChanged
    End Sub
    

     

    El código para C#:

    void SystemEvents_UserPreferenceChanged(object sender, 
                                            UserPreferenceChangedEventArgs e)
    {
        if (e.Category == UserPreferenceCategory.Window)
        {
            this.Font = SystemFonts.IconTitleFont;
        }
    }
    
    void Form1_FormClosing(object sender, 
                           FormClosingEventArgs e)
    {
        SystemEvents.UserPreferenceChanged -= 
            new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged);
    }
    

    Y esto es todo amigo 🙂


    Si me decido a poner algún ejemplo completo, actualizaré esta «entrada» (o post) poniendo el enlace para la descarga.



    Espero que te sirva 😉

    Nos vemos.
    Guillermo

    P.S.
    Si quieres que los controles de tu formulario cambien
    al tamaño que tu indiques (un porcentaje), mira esta entrada:
    Cambiar el tamaño de los controles de un formulario automáticamente