Etiqueta: C#Sharp

Temas relacionados con C#

  • Cambios radicales en Windows 8.1 Preview (para developers)

     

     

    Nota del 08/Jul/13 17:00:
    Ayer noche publiqué este artículo, y esta mañana he estado haciendo ciertas comprobaciones, y entre ellas he modificado parte del código que te muestro, además de un pequeño truco para seguir usando los elementos VisualState que ya tengamos en el código XAML, aunque ya no existan como "modos de visualización".
    La forma de usar o indicar cuál de los elementos VisualState definidos en el código XAML queremos usar (si hubiera alguno, si no existe ese "aspecto visual" simplemente lo ignora y no produce ningún efecto ni excepción) es haciendo una llamada al método GoToState de la clase VisualStateManager. Esa llamada la haremos en el código del método que intercepta el evento SizeChanged. En el código que te muestro más abajo utilizo esto que te estoy comentando.

     

    Pues eso… que las cosas han cambiado con Windows 8.1 Preview con respecto a la versión anterior y las cosas que antes nos dijeron que eran de una forma, ahora resulta que no es así, y lo peor es que si no las cambias en tus aplicaciones de Windows 8 para Windows Store (Tienda de Windows) no quedarán "cool" y por tanto darán mucho el cante, es decir, que tus aplicaciones van a destacar, pero no por lo buenas o interesantes que puedan ser, si no porque parecerán aplicaciones que ni siquiera te has molestado en testear (probar).

    Bueno, a lo mejor he sido un poco exagerado, ya que esto será así sobre todo si tu aplicación actual para la Tienda de Windows utiliza cosas como acoplarse a la izquierda o derecha (snapped) o tiene en cuenta si tiene otra aplicación acoplada o utiliza actualización del icono de la pantalla de Inicio (live tiles) y algunas cosas más…

    Pero aquí estoy yo para intentar darte un poco de claridad en estas cosas que nos vienen encima, así que… empecemos con algunas cosas.

     

    Las aplicaciones de Windows 8.1 pueden cambiar de tamaño

    Pues eso es así, aunque con la filosofía de Windows 8, es decir, a pantalla completa.
    Sí, sí, no me he equivocado, espera y te lo explico.
    Las aplicaciones de Windows 8.1 (las que se utilizan para la pantalla de inicio, estilo metro o Modern UI) no funcionan en una ventana independiente, esas son las que funcionan en el escritorio, las propias de Windows 8 cuando las inicias, las veremos a pantalla completa. En la versión actual de Windows 8, además de pantalla completa hay otras dos formas de verlas: acoplada a uno de los dos lados (Snapped) o rellenando lo que deja una aplicación que esté en el modo acoplado (Filled).
    Pero esto ha cambiado en Windows 8.1 y ahora los modos Snapped y Filled no existen. La pantalla se puede dividir para que podamos ver dos aplicaciones a la vez (como antes), pero en esta ocasión la división no tiene porqué ser tan evidente como antes, por ejemplo, en la figuras 1 y 2 tenemos cómo se verían las dos mismas aplicaciones usando Windows 8 (fig. 1) o Windows 8.1 Preview (fig. 2).

     

    screenshot_07072013_173613_8
    Figura 1. Windows 8: Aplicaciones en modo Snapped y Filled

     

    screenshot_07072013_173815_8.1
    Figura 2. Windows 8.1 Preview: Aunque parecido a Snapped y Filled, internamente no es así

     

    Si miramos las dos capturas parece que no hay diferencia en las dos versiones de Windows 8 (salvo por la rayita que hay en la separación), pero si miras la siguiente figura (la 3), verás que la aplicación de la izquierda (que no está preparada para Windows 8.1) sigue manteniendo los tamaños de lo que antes era Snapped, pero ocupando más pantalla. Por otro lado, la aplicación para el tiempo si que se adapta al nuevo tamaño.

     

    screenshot_07072013_173841_8.1
    Figura 3. Windows 8.1 Preview: El tamaño ya no es fijo como en la versión anterior

     

    Como detalle, decirte que en modo Snapped el ancho solía ser de 320 pixel y que en Windows 8.1 cuando inicialmente acoplamos la aplicación a uno de los dos lados, ese tamaño suele ser variable, por ejemplo, en una resolución de 1024 (que antes no permitía el modo Snapped) el ancho sería de 501 pixel, y en una resolución de 1366 ese ancho inicial es de 672 pixel.

     

    Saber cuando la aplicación está en los "antiguos" modos Snapped o Filled

    Para nosotros los developers (voy a usar la palabra en inglés, ya que algunas veces hay discrepancia entre el uso de desarrollador y programador) lo que debemos saber es que en Windows 8.1 ya no se podrán usar esos modos de visualización, bueno, no sólo no usarlos, lo peor es que no tenemos una forma fácil de saber si nuestra aplicación está en modo acoplado (Snapped) o no.

    Lo que Microsoft recomienda o dice en la advertencia (ver figura 4) al usar la enumeración ApplicationViewState, o cuando queremos usar ApplicationView.Value (que es el que nos indica si la aplicación está en modo Snapped o Filled), es que tengamos en cuenta el tamaño al que se muestra nuestra aplicación y actuar en consecuencia.

     

    Screenshot (14.1)
    Figura 4. Advertencia (warning) al usar las propiedades y enumeraciones obsoletas

    Resumiendo, el texto de la advertencia es: que la propiedad Value está obsoleta y que comprobemos directamente el tamaño de la ventana (Instead, query for window layout sizes directly).

    Esto está muy bien, particularmente para las nuevas aplicaciones que hagamos, pero el problema es cuando esos tamaños los estamos utilizando en las aplicaciones existentes. La prueba la tienes en la figura 3, que a pesar de tener más espacio disponible, no se adapta al nuevo tamaño.

     

    Nota:
    Estoy usando la aplicación Microsoft Mahjong en estas capturas, pero podría estar usando muchas otras de las aplicaciones que hay para Windows 8, por ejemplo la de mi Reloj para Windows 8, en las que no se comprueban más cosas, simplemente porque en el momento de crear/publicar la aplicación no había más posibilidades de tamaño que pantalla completa, Snapped o Filled.

     

    Como veremos dentro de un momento, me he fabricado unas funciones para saber si estamos o no en modo Snapped (o Filled, etc.) o lo que es lo mismo, cuando la aplicación está acoplada a uno de los dos lados y tiene un tamaño menor del que yo he predeterminado para simular esos valores.

    Aunque tengas esas funciones, el problema real será si hemos usado (como ellos nos recomendaron) que la mayoría de los cambios en el tamaño de los controles, etc. lo hiciéramos dentro del código XAML (como elementos VisualState), ya que en Windows 8.1 Preview esos valores los ignoran completamente.

    Por tanto, si queremos dar la misma funcionalidad a nuestra aplicación que la que ya teníamos, tendremos que convertir esos valores indicados en el diseño de la ventana en código directo y asignarlos en el evento SizeChanged del control o ventana.

    Aunque también podemos indicar cuál de los VisualState que tenemos definido en el código XAML queremos usar. Esta opción es más adecuada, aunque podemos usar una mezcla de los dos, tal como veremos dentro de poco en el ejemplo final.

    Y como también hay dos valores que se tenían en cuenta para manipular los tamaños de los objetos mostrados, como son si la aplicación está en modo horizontal (landscape) o en modo vertical (portrait), pues yo también me he fabricado un par de funciones para esas dos ocasiones.

    Así que, veamos el código de las funciones que he definido y así será más fácil de entender el código que ponga para realizar esos cambios en el evento de cambio de tamaño de la ventana.

     

    Funciones para simular Snapped y Filled o cómo saber si nuestra app está acoplada a uno de los lados

    La definición del código la he hecho en la clase App, pero puedes hacerlo donde quieras, ya que están definidos como métodos compartidos (Shared en Visual Basic, static en C#).

    En total he definido 6 métodos, dos de ellos para saber si está en modo Snapped o Filled. En estos dos utilizo una constante con el valor 750 para indicar que si el tamaño de la ventana (de la aplicación actual) es menor de esos 750 px y además la aplicación está anclada a la derecha o a la izquierda es que está en modo Snapped, por otro lado, considero que está en modo de relleno (Filled) si está ajustada a uno de los lados y el tamaño es igual o superior a esos 750 pixel. Por supuesto , en esta segunda consideración entrarían los modos en que estaría una aplicación cuando el usuario la está redimensionando (y el tamaño que le asigna es 750 o más), pero para ir viendo los cambios, ya nos vale, o, al menos, a mí me vale.

    Nota:
    El código que te mostraré es para Visual Basic, pero a diferencia de lo que han hecho la gente de Microsoft, también te mostraré el código de C#, pero no en el momento en que estoy escribiendo esto, pero lo pondré.

     

    El código de Visual Basic de los métodos IsSnapped y IsFilled sería el siguiente:

     

    Public Const AnchoSnapped As Double = 750
    
    ''' <summary>
    ''' Simular el modo Filled en Windows 8.1
    ''' Comprueba si la aplicación está en uno de los costados
    ''' y el ancho es mayor o igual que <see cref="AnchoSnapped"/> px
    ''' </summary>
    ''' <remarks>07/Jul/13</remarks>
    Public Shared Function IsFilled() As Boolean
        Dim aView = ApplicationView.GetForCurrentView()
        Dim aWidth = Window.Current.Bounds.Width
    
        ' Si está acomplado a ambos lados es que es pantalla completa
        ' (esto es así cuando se cambia el tamaño estando acoplado)
        If (aView.AdjacentToLeftDisplayEdge AndAlso aView.AdjacentToRightDisplayEdge) Then
            Return False
    
        ElseIf (aView.AdjacentToLeftDisplayEdge OrElse aView.AdjacentToRightDisplayEdge) AndAlso
            aWidth >= AnchoSnapped Then
    
            Return True
        Else
            Return False
        End If
    End Function
    
    ''' <summary>
    ''' Simular el modo Snapped en Windows 8.1
    ''' Si el ancho es menor de <see cref="AnchoSnapped"/> pixels
    ''' y está en uno de los lados lo considero Snapped
    ''' </summary>
    ''' <remarks>06/Jul/13</remarks>
    Public Shared Function IsSnapped() As Boolean
        Dim aView = ApplicationView.GetForCurrentView()
        Dim aWidth = Window.Current.Bounds.Width
    
        ' Amplio las posibilidades comprobando solo si el tamaño es menor de AnchoSnapped
        ' ya que esté o no acoplada, el tamaño y posición del contenido debe adaptarse
        '
        'If (aView.AdjacentToLeftDisplayEdge OrElse aView.AdjacentToRightDisplayEdge) AndAlso
        '    aWidth < AnchoSnapped Then
        If aWidth < AnchoSnapped Then
            Return True
        Else
            Return False
        End If
    
    End Function
    
    

     

    Un par de cosas que quiero comentarte sobre el código anterior.

    La primera es que estoy usando Window.Current.Bounds.Width para saber el ancho de la pantalla, y seguramente dirás que podría usar la propiedad ActualWidth de la ventana principal (no se puede desde este código porque la clase App no se deriva de Page), entre otras cosas porque es la forma que seguramente has podido ver en los ejemplos (para C#) que se incluyen con el SDK de Windows 8.1 Preview. Pero, he de decirte que esa propiedad a mí NUNCA me ha servido, siempre que lo he intentado el valor devuelto es 0.0, así que… he optado por esa otra que sé que funciona.

    La segunda es el uso del nuevo método GetForCurrentView de la clase ApplicationView. Ese método devuelve un objeto del tipo ApplicationView que entre otras cosas contiene los dos valores que estoy usando en esas dos funciones: AdjacentToLeftDisplayEdge que nos indica si nuestra aplicación está acoplada a la izquierda de la pantalla, y AdjacentToRightDisplayEdge que como podrás suponer es para saber si la aplicación está acoplada a la derecha.

    Tiempos aquéllos en las las funciones tenían unos pocos caracteres… mejor así, sobre todo si sabes algunas palabras de inglés.

    Creo que el código no necesita mayor explicación, salvo indicar los cambios realizados en esta versión del lunes 8 de julio:

    En el código de la función IsSnapped la he reducido a una simple comprobación de si el ancho de la pantalla es menor del que queremos considerar como "acoplado", en este ejemplo estoy usando el valor de la constante AnchoSnapped que inicialmente la he definido a 750 pixel.

    En la función IsFilled ahora compruebo que no esté acoplado a ambos lados, ya que eso supondría que la aplicación está a pantalla completa, por tanto si se cumple la primera comprobación devuelve un valor falso, y por tanto solamente será verdadero cuando esté acoplado a uno de los dos lados y el ancho sea igual o mayor del indicado en AnchoSnapped.

     

    Saber si la aplicación está en modo apaisado o vertical

    Otras dos funciones que me he fabricado, más que nada para evitar estar repitiendo las definiciones de las llamadas al API de Windows 8.1 Preview (por medio de las clases de .NET Framework), son las que me indican si la aplicación está a pantalla completa y cómo está orientada la tableta o el dispositivo en el que está funcionando.

    En la versión actual de Windows 8 esto lo hacíamos mediante los correspondientes valores de la enumeración ApplicationViewState, pero como ya hemos comprobado, se considera obsoleta.

    Por ejemplo, para saber si nuestra aplicación estaba en modo de pantalla completa y en horizontal, comprobábamos si el valor de la propiedad Value de ApplicationView era FullScreenLandscape.

    Ahora esto se hace comprobando si el valor de IsFullScreen es cierto y además el valor de Orientation es Landscape. Las dos primeras son nuevas propiedades de la clase ApplicationView y el valor de la propiedad Orientation es del tipo enumerado ApplicationViewOrientation.

    Una vez dicho esto, podemos comprobar que las dos funciones que me he fabricado lo único que hacen es reducir un poco el código cuando queramos hacer las comprobaciones pertinentes.

    Este es el código de Visual Basic de esas dos funciones.

     

    ''' <summary>
    ''' Comprobar si la pantalla está en FullScreenPortrait
    ''' (vertical a pantalla completa)
    ''' </summary>
    ''' <remarks>06/Jul/13</remarks>
    Public Shared Function IsFullScreenPortrait() As Boolean
        Dim aView = ApplicationView.GetForCurrentView()
    
        If aView.IsFullScreen AndAlso aView.Orientation = ApplicationViewOrientation.Portrait Then
            Return True
        Else
            Return False
        End If
    End Function
    
    ''' <summary>
    ''' Comprobar si la pantalla está en FullScreenLandscape
    ''' (apaisada a pantalla completa)
    ''' </summary>
    ''' <remarks>06/Jul/13</remarks>
    Public Shared Function IsFullScreenLandscape() As Boolean
        Dim aView = ApplicationView.GetForCurrentView()
    
        If aView.IsFullScreen AndAlso aView.Orientation = ApplicationViewOrientation.Landscape The
            Return True
        Else
            Return False
        End If
    End Function
    

     

    Te comentaba que en total eran 6 funcionen las que me había fabricado, las otras dos simplemente devuelven un objeto del tipo ApplicationView (el valor devuelto por GetForCurrentView); y la otra función indica el ancho de la pantalla. Pero en el ejemplo que pondré ahora no se utilizan, por tanto, mejor las dejo ahí para otra ocasión.

     

    Ejemplo para usar las funciones definidas

    Veamos cómo usar estas funciones para adaptar el tamaño, margen, etc. de los controles según nuestra aplicación esté "acoplada" o no.

    Este ejemplo es muy simple y sólo tiene dos controles TextBlock con distinto texto, uno servirá para el título y el otro para el texto a mostrar.

    El aspecto en modo diseño es el que podemos ver en la figura 5.

     

    Screenshot (15)

    Figura 5. La página principal en modo diseño

     

    Primero definimos los valores de los elementos VisualState, que en el ejemplo que estoy usando serían los indicados en el siguiente código XAML:

     

    <Page
        x:Class="App1.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:App1"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="251*"/>
                <ColumnDefinition Width="640*"/>
                <ColumnDefinition Width="475*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="107*"/>
                <RowDefinition Height="384*"/>
                <RowDefinition Height="277*"/>
            </Grid.RowDefinitions>
            <TextBlock x:Name="txtTitle" Grid.Column="1" Text="El título" FontSize="110" />
            
            <TextBlock x:Name="txtMensaje" Grid.Column="0" Grid.Row="1" 
                       Grid.ColumnSpan="3" Grid.RowSpan="2"
                       VerticalAlignment="Center"
                       FontSize="140" FontWeight="Bold" TextWrapping="WrapWholeWords"
                       Text="El texto que ocupa la pantalla completa con cambio de línea" />
    
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ApplicationViewStates">
                    <VisualState x:Name="FullScreenLandscape" />
                    <VisualState x:Name="FullScreenPortrait">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtTitle" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="90"/>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtMensaje" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="70"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Filled">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtTitle" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="80"/>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtMensaje" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="100"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                    <VisualState x:Name="Snapped">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtTitle" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="80"/>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="txtMensaje" Storyboard.TargetProperty="FontSize">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="70"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
    
            </VisualStateManager.VisualStateGroups>
    
        </Grid>
    </Page>
    
    
    

     

    Todo esto lo haremos en el método del evento SizeChanged del control o página/ventana en el que queramos tener estas cosas en cuenta.

     

    Private Sub MainPage_SizeChanged(sender As Object, e As SizeChangedEventArgs) Handles Me.SizeChanged
        ' valor predeterminado de visualState                       (08/Jul/13)
        Dim visualState As String = "FullScreenLandscape"
    
        If App.IsFilled Then
            visualState = "Filled"
        ElseIf App.IsSnapped Then
            visualState = "Snapped"
        ElseIf App.IsFullScreenPortrait Then
            visualState = "FullScreenPortrait"
        Else
            visualState = "FullScreenLandscape"
    
            ' Si queremos adaptar los tamaños según el ancho
            ' lo podemos hacer aquí\f0
            ' Pero debemos tener en cuenta que el valor asignado en el código XAML
            ' tiene preferencia.
            ' En este ejemplo, esto sólo funciona cuando es FullScreenLandscape
            ' ya que no se asignan valores en el código XAML
    
            Dim ww = Window.Current.Bounds.Width
            If ww <= 800 Then
                txtTitle.FontSize = 70
                txtMensaje.FontSize = 70
            ElseIf ww <= 1024 Then
                txtTitle.FontSize = 80
                txtMensaje.FontSize = 90
            ElseIf ww < 1366 Then
                txtTitle.FontSize = 100
                txtMensaje.FontSize = 110
    
            Else
                ' Los valores predeterminados
                txtTitle.FontSize = 110
                txtMensaje.FontSize = 140
            End If
    
        End If
    
        ' Utilizar los valores del código XAML indicado al visualState indicado
        VisualStateManager.GoToState(Me, visualState, False)
    
        ' Si quisiéramos usar valores distintos a los inidcados en el código XAML
        ' tendremos que asignarlos aquí, después de llamar a VisualStateManager.GoToState
    
    End Sub
    

     

    Por favor lee los comentarios del código para que sepas cuando esas asignaciones al tamaño de la fuente del texto funcionan, ya que siempre tendrá preferencia el valor asignado en el código XAML y si queremos que el asignado manualmente sea el que "mande" debemos usar el truco que ahí indico.

     

    Bueno, creo que con esto ya está bien para esta primera aproximación sobre los cambios que se incluyen en Windows 8.1 Preview y que los que queremos crear aplicaciones para la Tienda de Windows debemos tener en cuenta.

    Hay más cosas, algunas te las contaré y de otras seguramente no te diré ni mú (las que no haya probado).

     

    Espero que te se de utilidad y ya sabes, si quieres invitarme a un café virtual, puedes hacerlo en el botón "Donar" 😉

    Gracias (incluso si no me invitas).

     

    Nos vemos.

    Guillermo

    P.S.

    Cuando tenga listo el código de C# pondré el proyecto de ejemplo en mi sitio de descargas (downloads) para que puedas verlo con más detalle.

    …

  • en el día del libro (23 de abril), te regalo dos

     

    Pues eso… ya que estamos con el día del libro, te recuerdo que yo te regalo dos: uno de Visual Basic y otro de C#.

    En realidad ya te los estuve regalando (o al menos poniéndolo gratuitamente a tu disposición) desde finales de agosto del año pasado, pero… por si no has entrado en mi blog y/o en mi sitio desde hace tiempo… o simplemente has entrado, pero ni te has enterado (que también suele pasar). Así que… como no es extraño que se te pasen las cosas… aquí estoy yo para recordártelo, jejeje

     

    Nota:
    Las descargas se hacen desde la web de SolidQ y debes registrarte para poder descargarlos.

     

    Aquí tienes los enlaces para que te los descargue desde el sitio de SolidQ:
    (recuerda que tienes que estar registrado)

    Visual Basic:

    guille_novedadesVB9

    Novedades de Visual Basic 9.0 (Visual Basic 2008, LINQ, tipos y métodos anónimos, etc.)

    Visual C#:

    guille_aprendeCSharp3

    Aprenda C# 3.0 desde 0.0 – Parte 3: Lo nuevo (Visual Studio 2008, LINQ, tipos y métodos anónimos, etc.)

     

    Espero que te guste el regalo y sobro todo que te sigan siendo de utilidad.

    Nos vemos.
    Guillermo

    …

  • Utilizar el mismo control en dos sitios (Windows Store)

     

    Pues eso… que quería usar el mismo control en dos sitios diferentes (según se muestre o no el AppBar) y como no quería crear otro control para que haga lo mismo que el que ya tenía, pues… a hacer lo que "supuestamente" había que hacer… es decir, cambiarlo de contenedor.

    Lo que pasa con las aplicaciones de la Tienda de Windows (Windows Store) o lo mismo es cosa de XAML, ¡vaya usted a saber!, es que el mismo control (incluso un clon referenciado, vamos que no es una copia) no puede estar en dos sitios a la vez.

    Esto es lógico, pero creo que con los controles de VB6 o de Windows Forms cuando lo "emparentabas" en otro control ya perdía toda la familiaridad con el que antes hacía de padre… pero hace unas semanas que comprobé que en XAML/Windows Store no es así.

    ¿No te estás enterando verdad? No te preocupes, es que cuando no duermo, pues… pasa lo que pasa… la cuestión es que te voy a explicar cómo usar el mismo control para que "parezca" que es una copia ya que unas veces se mostrará en la pantalla de la aplicación y si cuando se muestre el AppBar ese mismo control estará en la barra de la aplicación.

    El código te lo voy a mostrar en Visual Basic y también en C# (para que no te quejes).

    Pero primero voy a mostrarte el código XAML.

     

    <Page.BottomAppBar>
        <AppBar x:Name="mainBottomAppBar" Style="{StaticResource ClockAppBarBackground}" Padding="10,0,10,0"
                Closed="mainBottomAppBar_Closed" Opened="mainBottomAppBar_Opened">
            <Grid>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                    <Button x:Name="buttonAlarmas"  
                            Style="{StaticResource AlarmasAppBarButtonStyle}" Click="buttonAlarmas_Click" />
                </StackPanel>            
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                    <Button x:Name="buttonClock"
                            Style="{StaticResource AboutClockAppBarButtonStyle}" Click="button_Clock" />
                    <StackPanel x:Name="spAppBarSettings" Margin="0,0,-10,0">
                        <Button x:Name="buttonSettings"  
                                Style="{StaticResource SettingsAppBarButtonStyle}" Click="button_Settings" />
                    </StackPanel>
                </StackPanel>
            </Grid>
        </AppBar>
    </Page.BottomAppBar>
    
    

     

    Nota:

    Los estilos usados en el código XAML son modificaciones de los que hay en el fichero StandardStyles.xaml, salvo el estilo de la barra de la aplicación que lo que contiene es la definición del color de fondo y del borde.

    De todas formas, pondré el código de muestra de la página XAML en pastebin.com y así no tendrás problema con los estilos y esas cosas.

     

    utilizar mismo control

     

    Este código es la definición de la AppBar en la que hay tres botones, uno de ellos muestra el panel de configuración (settings charm) y los otros dos hacen lo mismo que si eligiéramos esas dos opciones en el propio panel de configuración, aquí no te muestro el código, pero te lo explico para que sepas porqué el control AppBar tiene esos dos eventos.

    En esos dos eventos es donde hago el cambio de contenedor del control buttonSettings (el de configuración) ya que es el que me interesa que esté "aparentemente" en dos sitios a la vez.

    Este es el código de Visual Basic y el de C# está convertido, así que… aunque supongo que funcionará, comprueba que lo hace… (es que tendría que crear un proyecto para probarlo yo, y… pues eso… )

    VB:

     

    ' Añadir el botón de configuración al panel inferior    (04/Feb/13)
    ' para que siempre esté visible
    ' Pero sólo cuando se cierre la AppBar
    Private Sub mainBottomAppBar_Opened(sender As Object, e As Object)
        spRow2.Children.Remove(buttonSettings)
        spAppBarSettings.Children.Add(buttonSettings)
    End Sub
    
    Private Sub mainBottomAppBar_Closed(sender As Object, e As Object)
        spAppBarSettings.Children.Remove(buttonSettings)
        spRow2.Children.Add(buttonSettings)
    

     

    C#:

     

    // Añadir el botón de configuración al panel inferior    (04/Feb/13)
    // para que siempre esté visible
    // Pero sólo cuando se cierre la AppBar
    private void mainBottomAppBar_Opened(object sender, object e)
    {
        spRow2.Children.Remove(buttonSettings);
        spAppBarSettings.Children.Add(buttonSettings);
    }
    
    private void mainBottomAppBar_Closed(object sender, object e)
    {
        spAppBarSettings.Children.Remove(buttonSettings);
        spRow2.Children.Add(buttonSettings);
    }
    
    

     

    Es importante que el control que queremos mover esté primero en el contenedor spRow2, ya que al abrir la AppBar se intentará quitar de la colección de este panel y si o está… ¡ganaremos un hermoso error!

    Esto lo podemos hacer por código (por ejemplo asignándolo en algunos de los eventos iniciales de la página o mejor aún, a la hora del diseño. Es decir, en vez de ponerlo donde yo lo he puesto (en la AppBar, más que nada para que vieras el código completo de la barra) debería estar en el control spRow2.

     

    Otro problema con el que me he encontrado es que resulta que cuando el usuario pulsa en buttonSettings la barra de la aplicación se oculta, pero cuando se pulsa en cualquiera de los otros dos… pues… no se oculta, así que… lo que he hecho es cerrar por medio de código la barra, pero no he usado la propiedad "Visibility" para ocultarla, ya que eso haría que no se volviera a ver. Lo que he hecho es asignarle un valor falso a la propiedad IsOpen, de forma que se cierra pero sin más problemas. Esa asignación la hago en los dos botones que me daban problema, es decir, cuando el usuario pulsa en uno de esos botones, le asigno el valor falso a esa propiedad (IsOpen) y después muestro el panel de configuración y todo lo que haya que hacer.

    En este caso, el código es muy simple (adivina cuál es el de VB y cuál el de C#):

     

    mainBottomAppBar.IsOpen = False
    
    mainBottomAppBar.IsOpen = false
     ;

     

    Bueno, después de este inciso, sigamos con lo de "mover" el control de sitio.

    Como ya te comenté hace un rato, el control para mostrar el panel de configuración (settings charm) también debe estar en el panel al que he llamado spRow2 y que estará situado al pie de la página (concretamente el control ese parecerá que está en el mismo sitio).

    Y como ese StackPanel sólo tendrá el botón en cuestión hay un problema añadido, y es que cuando lo quitamos de ahí para ponerlo en el AppBar, el resto de la pantalla se baja… así que… ¡hay que arreglarlo! y eso lo he arreglado indicando la altura mínima del panel que lo contiene, de forma que ese tamaño sea el mismo que el que tiene el botón.

    Mir el código y cuando lo pruebes, quita la asignación de MinHeight y verás lo que ocurre.

     

    <!-- el MinHeight es para que al quitar el botón (por código)
         no se mueva lo que haya en medio 
         que se iba para abajo al abrir la appBar -->
    <StackPanel x:Name="spRow2" Grid.Column="2" Orientation="Horizontal" MinHeight="90">
        <!-- poner aquí el botón de configuración, pero por código -->
    </StackPanel>
    
    
    

     

    Y creo que con esto ya está bien…

    Te resumo las cosillas que creo que tiene de interesante este rollo que te he soltado, perdona si no me he sabido explicar bien o lo ves algo poco claro y enredado, pero es que llevo más de 24 horas sin pegar ojo y… eso se nota…  No es una excusa, pero… si no entiendes todo lo que te he dicho, para que sepas que no es culpa tuya, al menos el 100% jejeje (¡qué malo soy!).

     

    En este ejemplo hemos visto:

    – Cómo añadir una AppBar en la parte inferior de nuestra página (se mostrará al pulsar con el botón secundario del ratón).

    – Hacer que se cierre la AppBar en caso de que se quedara abierta al pulsar en uno de los botones.

    – Cuando veas el código XAML completo del ejemplo, sabrás cómo agregar estilos para usarlos sólo en la página en la que se definen.

    – Utilizar los eventos de apertura y cierre de la AppBar para cambiar un botón de un contenedor a otro, concretamente de la colección Children de un control StackPanel.

    – Cómo mantener las cosas en su sitio cuando el tamaño de un panel o contenedor cambia de tamaño (normalmente de la altura).

    – Y si te fijas en el código verás que está el botón de ir hacia atrás, pero como esta página no lo utiliza (y tampoco la página en la que utilizo el código este que estoy mostrándote), lo he ocultado, pero con idea de que el título no se desplace a la izquierda, he puesto que la columna en la que está el botón de ir hacia atrás en lugar de tener un ancho automático (<RowDefinition Height="Auto"/> ) lo he puesto para que tenga un tamaño fijo, que es el que he calculado teniendo en cuenta el ancho del botón más los márgenes, en mi caso (y generalmente si usas ese diseño) es de 110 (<RowDefinition Height="110"/> ).

    – Y seguramente más cosas, pero lo principal es lo que te he relacionado.

     

    Recuerda que en mi cuenta de Pastebin tienes el código completo (o casi) del ejemplo (que al final he hecho), tanto para Visual Basic como para C#.

     

     

    Espero que te haya sido de utilidad.

    Nos vemos.

    Guillermo

    …