Archivo de la etiqueta: ComoNET

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

El código de VB y C# de: Mostrar todos los formularios de una aplicación

Pues eso… lo prometido es deuda y aquí tienes el código completo de cómo mostrar todos los formularios de una aplicación (ensamblado) y cómo mostrar un formulario en el segundo monitor.

Al final de todo te pongo el enlace para descargar los proyectos tanto de Visual Basic como de C#.

¡Que te aproveche! Smile

Estas son las capturas (en funcionamiento) de Visual Basic y C#:

mostrar_formularios_01
Figura
1, la aplicación de Visual Basic

mostrar_formularios_02
Figura 2: La aplicación de C#

El código completo de Visual Basic:

'------------------------------------------------------------------------------
' Mostrar todos los formularios de una aplicación (ensamblado)      (16/Dic/18)
' Estén abiertos o no
' Usando reflection
'
' Este código está basado en el artículo que publiqué el 13/Sep/2004 en:
' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
' 
'
' (c) Guillermo (elGuille) Som, 2018
'------------------------------------------------------------------------------
Option Strict On
Option Infer On

Imports Microsoft.VisualBasic
Imports vb = Microsoft.VisualBasic
Imports System
Imports System.Text
Imports System.Collections.Generic
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Diagnostics
Imports System.Linq

Public Class Form1
    ''' <summary>
    ''' Para recorrer todos los formularios de un ensamblado
    ''' estén abiertos o no
    ''' Basado en el código del elGuille.info:
    ''' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
    ''' </summary>
    Private ass As System.Reflection.Assembly

    ''' <summary>
    ''' Mostrar todos los formularios de la aplicación actual,
    ''' estén o no en memoria, usando reflection.
    ''' Además de los formularios, mostrará:
    ''' My.Application (solo en VB)
    ''' Y todos los nombres esmpiezan con el espacio de nombres
    ''' ESPACIO_DE_NOMBRES.NombreForm
    ''' </summary>
    Private Sub mostrarForms()
        ' llena una colección con los formularios de esta aplicación
        ' estén o no en memoria.
        ' Muestra el resultado en un listbox

        lbForms.Items.Clear()

        For Each t As Type In ass.GetTypes()
            Dim nombreTipo = t.BaseType.Name
            ' También tendrá My.Application: (solo en VB)
            ' <espacio de nombres>.My.MyApplication
            If nombreTipo.ToLower().Contains("form") Then
                lbForms.Items.Add(t.FullName)
            End If
        Next
    End Sub

    ''' <summary>
    ''' Muestra el formulario indicado en el argumento,
    ''' este debe ser con el espacio de nombres completo
    ''' 
    ''' Si es el actual no lo muestra.
    ''' Si da error o no es un formulario se avisa.
    ''' </summary>
    Private Sub mostrarFormulario(s As String)
        ' creamos un tipo a partir del nombre
        Dim t As Type = ass.GetType(s)
        ' instanciamos un nuevo objeto en la memoria
        Dim o As Object

        ' por si hemos seleccionado algo que no es una clase
        Try
            o = Activator.CreateInstance(t)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Mostrar formularios")
            Exit Sub
        End Try

        ' si no es un formulario, mostramos un aviso y salimos
        If Not (TypeOf o Is Form) Then
            MessageBox.Show(s & ", no es un formulario", "Mostrar formularios")
            Exit Sub
        End If

        ' convertimos el objeto en un formulario
        ' como sabemos que si llega aquí es un formulario,
        ' usamos TryCast que hace menos trabajo que CType o DirectCast.
        Dim f As Form = TryCast(o, Form)
        If f Is Nothing Then
            MessageBox.Show(s & ", parece que no es un formulario",
                            "Mostrar formularios")
            Exit Sub
        End If

        ' si el nombre es el de este formulario,
        ' lo cerramos y salimos.
        If f.Name = Me.Name Then
            ' no volver a crear este formulario
            'f.Close()
            Me.BringToFront()

            MessageBox.Show(s & " es el formulario de inicio.",
                            "Mostrar formularios")

            Return
        End If

        ' mostramos el formulario.
        f.Show()

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Mostrar esta ventana en el monitor secundario
        MostrarEnVentana2(Me)

        ' mostrar los formularios de esta aplicación
        ass = System.Reflection.Assembly.GetExecutingAssembly()
        mostrarForms()
    End Sub

    Private Sub btnAbrir_Click(sender As Object, e As EventArgs) Handles btnAbrir.Click
        ' Por si no hay un nombre indicado
        If String.IsNullOrWhiteSpace(txtForm.Text) Then Return

        ' Abrir el formulario indicado en el textbox
        mostrarFormulario(txtForm.Text)
    End Sub

    Private Sub btnSalir_Click(sender As Object, e As EventArgs) Handles btnSalir.Click
        Me.Close()
    End Sub

    Private Sub lbForms_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lbForms.SelectedIndexChanged
        txtForm.Text = lbForms.SelectedItem.ToString
    End Sub

    ''' <summary>
    ''' Mostrar el formulario indicado en la segunda pantalla
    ''' Código adaptado de la respuesta 42 de:
    ''' https://stackoverflow.com/questions/2561104/how-do-i-ensure-a-form-displays-on-the-additional-monitor-in-a-dual-monitor-sc
    ''' </summary>
    Public Shared Sub MostrarEnVentana2(frm As Form)
        Dim myScreen = Screen.PrimaryScreen
        Dim otherScreen = If(Screen.AllScreens.FirstOrDefault(
                                Function(s) Not s.Equals(myScreen)), myScreen)

        ' Si queremos indicar dónde mostrarlo
        ' podemos cambiar los valores de Left y Top
        'frm.Left = otherScreen.WorkingArea.Left + 12
        'frm.Top = otherScreen.WorkingArea.Top + 12

    End Sub

    Private Sub btnForm2_Click(sender As Object, e As EventArgs) Handles btnForm2.Click
        Dim f As New Form2
        f.Show()
    End Sub

    Private Sub btnForm3_Click(sender As Object, e As EventArgs) Handles btnForm3.Click
        Dim f As New Form3
        f.Show()
    End Sub
End Class

El código completo de C#:

// ----------------------------------------------------------------------------
// Mostrar todos los formularios de una aplicación (ensamblado)     (16/Dic/18)
// Estén abiertos o no
// Usando reflection
// 
// Este código está basado en el artículo que publiqué el 13/Sep/2004 en:
// http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
// 
// 
// (c) Guillermo (elGuille) Som, 2018
// ----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace Mostrar_Nombres_Formularios_cs
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Para recorrer todos los formularios de un ensamblado
        /// estén abiertos o no
        /// Basado en el código del elGuille.info:
        /// http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
        /// </summary>
        private System.Reflection.Assembly ass;

        /// <summary>
        /// Mostrar todos los formularios de la aplicación actual,
        /// estén o no en memoria, usando reflection.
        /// Además de los formularios, mostrará:
        /// My.Application (solo en VB)
        /// Y todos los nombres esmpiezan con el espacio de nombres
        /// ESPACIO_DE_NOMBRES.NombreForm
        /// </summary>
        private void mostrarForms()
        {
            // llena una colección con los formularios de esta aplicación
            // estén o no en memoria.
            // Muestra el resultado en un listbox

            lbForms.Items.Clear();

            foreach (Type t in ass.GetTypes())
            {
                var nombreTipo = t.BaseType.Name;
                // También tendrá My.Application: (solo en VB)
                // <espacio de nombres>.My.MyApplication
                if (nombreTipo.ToLower().Contains("form"))
                    lbForms.Items.Add(t.FullName);
            }
        }

        /// <summary>
        /// Muestra el formulario indicado en el argumento,
        /// este debe ser con el espacio de nombres completo
        /// 
        /// Si es el actual no lo muestra.
        /// Si da error o no es un formulario se avisa.
        /// </summary>
        private void mostrarFormulario(string s)
        {
            // creamos un tipo a partir del nombre
            Type t = ass.GetType(s);
            // instanciamos un nuevo objeto en la memoria
            object o;

            // por si hemos seleccionado algo que no es una clase
            try
            {
                o = Activator.CreateInstance(t);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, 
                    "Mostrar formularios");
                return;
            }

            // si no es un formulario, mostramos un aviso y salimos
            if (!(o is Form))
            {
                MessageBox.Show(s + ", no es un formulario", 
                    "Mostrar formularios");
                return;
            }

            // convertimos el objeto en un formulario
            Form f = o as Form;
            if (f == null)
            {
                MessageBox.Show(s + ", parece que no es un formulario", 
                    "Mostrar formularios");
                return;
            }

            // si el nombre es el de este formulario,
            // lo cerramos y salimos.
            if (f.Name == this.Name)
            {
                // no volver a crear este formulario
                // f.Close()
                this.BringToFront();

                MessageBox.Show(s + " es el formulario de inicio.", 
                    "Mostrar formularios");

                return;
            }

            // mostramos el formulario.
            f.Show();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Mostrar esta ventana en el monitor secundario
            MostrarEnVentana2(this);

            // mostrar los formularios de esta aplicación
            ass = System.Reflection.Assembly.GetExecutingAssembly();
            mostrarForms();
        }

        private void btnAbrir_Click(object sender, EventArgs e)
        {
            //' Por si no hay un nombre indicado
            if (String.IsNullOrWhiteSpace(txtForm.Text))
                return;

            //' Abrir el formulario indicado en el textbox
            mostrarFormulario(txtForm.Text);

        }

        private void btnSalir_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void lbForms_SelectedIndexChanged(object sender, EventArgs e)
        {
            txtForm.Text = lbForms.SelectedItem.ToString();
        }

        /// <summary>
        /// Mostrar el formulario indicado en la segunda pantalla
        /// Código adaptado de la respuesta 42 de:
        /// https://stackoverflow.com/questions/2561104/how-do-i-ensure-a-form-displays-on-the-additional-monitor-in-a-dual-monitor-sc
        /// </summary>
        public static void MostrarEnVentana2(Form frm)
        {
            var myScreen = Screen.PrimaryScreen;
            var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen)) 
                                    ?? myScreen;

            //' Si queremos indicar dónde mostrarlo
            //' podemos cambiar los valores de Left y Top
            //'frm.Left = otherScreen.WorkingArea.Left + 12
            //'frm.Top = otherScreen.WorkingArea.Top + 12
        }
    }
}

El enlace para descargar el código completo (tanto de Visual Basic como de C#):

Archivo: Mostar_Nombres_Formularios_20181216_2346.zip (204 KB)
MD5 Checksum: DA004D15933BD82355684AC0F20680B1

Nos vemos.
Guillermo

Mostrar los formularios de nuestra aplicación (aunque no estén abiertos)

Pues eso… que estoy haciendo una aplicación para convertir los Windows Forms en WPF (Xaml) y tengo que recorrer todos los formularios de la aplicación para ir convirtiéndolos y he buscado en Internet cómo hacer eso y… ¡no lo he encontrado!

Mostrar todos los formularios de un ensamblado (aplicación) estén o no abiertos

Así que… me dije que yo tenía algún código haciendo Reflection para mostrar una clase y va y resulta que en ese ejemplo tenía la respuesta que buscaba… ¡lo que no esté en el sitio del Guille! Winking smile

Concretamente en esta página:
Las clases de un ensamblado usando Reflection (por ejemplo para saber los formularios de nuestra aplicación)

Y para que no me vuelva a ocurrir lo mismo en un futuro (ese artículo lo escribí el 13 de septiembre de 2004… ¡casi ná! Smile with tongue out) me he decidido a escribir este nuevo…

No te voy a explicar mucho (eso ya lo hice en ese artículo) solo te mostraré el código de ejemplo (y te pondré el proyecto para que lo puedas descargar).
Y como siempre el código tanto para Visual Basic como para C#.

Primero te muestro el código que recorre los formularios del ensamblado o aplicación (que es lo que realmente te puede interesar) y después te mostraré el resto.

El código para Visual Basic:

    ''' <summary>
    ''' Para recorrer todos los formularios de un ensamblado
    ''' estén abiertos o no
    ''' Basado en el código del elGuille.info:
    ''' http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
    ''' </summary>
    Private ass As System.Reflection.Assembly

    ''' <summary>
    ''' Mostrar todos los formularios de la aplicación actual,
    ''' estén o no en memoria, usando reflection.
    ''' Además de los formularios, mostrará:
    ''' My.Application (solo en VB)
    ''' Y todos los nombres esmpiezan con el espacio de nombres
    ''' ESPACIO_DE_NOMBRES.NombreForm
    ''' </summary>
    Private Sub mostrarForms()
        ' llena una colección con los formularios de esta aplicación
        ' estén o no en memoria.
        ' Muestra el resultado en un listbox

        lbForms.Items.Clear()

        For Each t As Type In ass.GetTypes()
            Dim nombreTipo = t.BaseType.Name
            ' También tendrá My.Application: (solo en VB)
            ' <espacio de nombres>.My.MyApplication
            If nombreTipo.ToLower().Contains("form") Then
                lbForms.Items.Add(t.FullName)
            End If
        Next
    End Sub

El código para C#:

        /// <summary>
        /// Para recorrer todos los formularios de un ensamblado
        /// estén abiertos o no
        /// Basado en el código del elGuille.info:
        /// http://www.elguille.info/NET/dotnet/reflectionTiposdeunensamblado.htm
        /// </summary>
        private System.Reflection.Assembly ass;

        /// <summary>
        /// Mostrar todos los formularios de la aplicación actual,
        /// estén o no en memoria, usando reflection.
        /// Además de los formularios, mostrará:
        /// My.Application (solo en VB)
        /// Y todos los nombres esmpiezan con el espacio de nombres
        /// ESPACIO_DE_NOMBRES.NombreForm
        /// </summary>
        private void mostrarForms()
        {
            // llena una colección con los formularios de esta aplicación
            // estén o no en memoria.
            // Muestra el resultado en un listbox
            lbForms.Items.Clear();

            foreach (Type t in ass.GetTypes())
            {
                var nombreTipo = t.BaseType.Name;
                // También tendrá My.Application: (solo en VB)
                // <espacio de nombres>.My.MyApplication
                if (nombreTipo.ToLower().Contains("form"))
                    lbForms.Items.Add(t.FullName);
            }
        }

Solo comentarte que en el código de Visual Basic he añadido 2 botones extras para mostrar los formularios Form2 y Form3 respectivamente. Esto solo es para probar que se pueden mostrar de las dos formas Smile
Pero en cualquier caso, siempre se muestra un nuevo formulario, esté o no abierto.

En la figura 1 puedes ver la aplicación en funcionamiento, mostrando una lista de los formularios de la aplicación.

mostrar_formularios_01.pg
Figura 1. La aplicación en ejecución mostrando los formularios del ensamblado (aplicación)

Si te fijas en la figura 1, se muestra también “My.MyApplication” que no es un formulario, pero el tipo “base” es Form, y por eso se muestra. Esto no ocurre en C#, ya que C# no tiene la clase My.

Nota:
El código de Visual Basic lo he convertido con una utilidad que hay en la web:
Telerik Code Converter

Un extra: Mostrar un formulario en el segundo monitor

Esto ya lo publicaré otro día (realmente era lo que iba a publicar hoy junto con una explicación de los problemas de nuestro código de Windows Forms y Visual Studio y los monitores HDPI), y es que en el código incluyo cómo hacer que un formulario se muestre en el segundo monitor.

Actualmente estoy usando un portátil con la resolución 1920×1080 y usando el tamaño del texto (etc.) al 100% y un segundo monitor con 3840×2160 y el tamaño del texto, etc. al 150%.

Como te explicaré en otra ocasión, yo uso el Visual Studio abriéndolo desde la pantalla principal (la del portátil al 100%), pero después muevo el IDE de Visual Studio al segundo monitor, por tanto cuando ejecuto el código, lo que se muestra lo hace siempre en la pantalla del portátil, por eso busqué cómo hacer que se muestre un formulario en el segundo monitor, y ¡lo encontré!, eso sí, en código para C#, concretamente en una respuesta en el sitio de StackOverflow en un hilo con la siguiente pregunta: How do I ensure a form displays on the “additional” monitor in a dual monitor scenario?
La respuesta que me sirvió fue la 42, con este código:

var myScreen = Screen.FromControl(originalForm);
var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen)) 
               ?? myScreen;
otherForm.Left = otherScreen.WorkingArea.Left + 120;
otherForm.Top = otherScreen.WorkingArea.Top + 120;

Que traducido a Visual Basic sería algo así:

Dim myScreen = Screen.PrimaryScreen
Dim otherScreen = If(Screen.AllScreens.FirstOrDefault(
                        Function(s) Not s.Equals(myScreen)), myScreen)

Y ¡esto es todo amigo!
Espero , como siempre, que te sea de utilidad y si no… al menos a mí dentro de 14 años me puede ser útil… Winking smile

Nos vemos.
Guillermo

P.S.
El código de ejemplo completo y el enlace para la descarga de los proyectos (hechos con Visual Studio 2017 y .NET 4.7.2, ninguno de estos requisitos son necesarios para el código) te los muestro en otra página.

El código de VB y C# de: Mostrar todos los formularios de una aplicación

Winking smile

ejemplo de ExecuteScalar para leer una columna de una tabla

 

Pues eso… que esto seguro que lo tengo mil veces publicado en mi sitio, pero hoy lo he ido a buscar, y no lo he encontrado de forma concreta, así que… aquí está para que "yo" lo encuentre… jejeje

¿Por qué buscaba esto?

El caso es el siguiente: necesitaba saber el ID (u otro campo) de una tabla de una base de datos de SQL Server Express 2014 (el tipo de base de datos es indiferente siempre que tenga el método que te comento) haciendo una consulta, concretamente usando LIKE en la propia cadena de selección o consulta.

La consulta es la siguiente:

String.Format( "SELECT ID FROM Clientes WHERE Nombre LIKE '%{0}%' AND Apellidos LIKE '%{1}%'",

nombre, apellidos)

No indico la forma de asignar esa cadena para que uses VB o C#, en el primer caso tendrías que indicar Dim sel = y en el segundo var sel =

A continuación creamos un objeto del tipo Connection y otro del tipo Command y hacemos una llamada al método ExecuteScalar y el valor que devuelva será (en este caso concreto) el ID que estamos buscando o CERO si no lo ha encontrado.

Simplificando (sin tratamiento de error y esas cosas), si el acceso a la base de datos es de SQL Server (en este ejemplo en una instancia de SQLEXPRESS) y usamos las clases definidas en System.Data.SqlClient, el código sería algo así:

Visual Basic:

Public Shared Function BuscarCliente2(nombre As String, apellidos As String) As Integer
    Dim elID As Integer = 0

    Dim sel =
        String.Format(
            "SELECT ID FROM Clientes WHERE Nombre LIKE '%{0}%' AND Apellidos LIKE '%{1}%'",
            nombre, apellidos)

    Dim sCon = ".\sqlexpress;Initial Catalog=LaBaseDeDatos;Integrated Security=True"

    Using con As New SqlConnection(sCon)
        Dim cmd As New SqlCommand(sel, con)

        con.Open()

        elID = CInt(cmd.ExecuteScalar())

        con.Close()
    End Using

    Return elID
End Function

 

C#:

public static int BuscarCliente2(string nombre, string apellidos)
{
    int elID = 0;

    var sel =
        String.Format(
            "SELECT ID FROM Clientes WHERE Nombre LIKE '%{0}%' AND Apellidos LIKE '%{1}%'",
            nombre, apellidos);

    var sCon = @".\sqlexpress;Initial Catalog=LaBaseDeDatos;Integrated Security=True";

    using (SqlConnection con = new SqlConnection(sCon))
    {
        SqlCommand cmd = new SqlCommand(sel, con);

        con.Open();

        elID = Convert.ToInt32(cmd.ExecuteScalar());

        con.Close();
    }

    return elID;
}

 

Y esto es todo amigos… 🙂

 

Nos vemos.

Guillermo

Error al usar una DLL de COM (ActiveX) en Windows 8 (x64)

 

Pues eso… que estaba yo actualizando al Visual Studio 2012 una aplicación que hace un par de años me funcionaba en Windows 7 con Visual Studio 2010 y en el que utilizo un componente ActiveX (COM) creado con Visual Basic 6.0. Pero a la hora de usarlo me da este error:
Retrieving the COM class factory for component with CLSID {6366501A-4960-4E2D-8E09-00F022ACD30E} failed due to the following error: 80040154.

Y después de varias búsquedas en google y desechar las que indican que hay que registrar el OCX/DLL con permisos de administrador (eso ya lo sabía yo desde Windows Vista, jeje) y ver que lo único "medio válido" es un comentario de que compile la DLL a 64 bits, me digo yo a mi mismo (algunas veces hablo con mi otro yo) ¿y si es cosa de que debe estar en modo x86?

Así que, me voy al Visual Studio 2012 (en realidad no es que me vaya, está algo complicado de que me pueda meter yo por la pantalla… si no que centre mi atención en el VS) y cambio en la ficha de Compilación (Compile) que en vez de AnyCPU sea x86, el cambio lo hago en los tres proyectos que utiliza lo que estoy mirando (aunque creo que sólo sería necesario en el ejecutable no en las 2 dll, pero por si las flais, lo cambio en todos).

¡Y funciona!

 

Así que… aquí estoy contándotelo por si alguna vez te pasa (o me vuelve a pasar a mí y no me acuerdo del tema…)

 

Espero que te sea de utilidad y acuérdate de "donar" algo… que si no… ¡que malamente está la vida! ¡ay! en fin…

 

Nos vemos.
Guillermo

Algunas diferencias entre WPF para escritorio y la Tienda de Windows

 

Pues eso… aunque el título más bien tendría que ser: algunas propiedades que están en las aplicaciones WPF para escritorio y que no está o son diferentes en las aplicaciones WPF para la Tienda de Windows.

 

Aquí te enumero algunas que me he ido encontrando:

1.- En las aplicaciones WPF para escritorio algunos controles tienen la propiedad Tooltip, en las aplicaciones para Windows Store hay que usar: ToolTipService.ToolTip.

2.- En WPF/desktop el control TextBlock tiene la propiedad IsEnabled, en las aplicaciones de Windows Store podemos usar: IsTextSelectionEnabled.

En este ejemplo se asigna a esa propiedad el valor que tenga la propiedad IsEnabled de un control llamado cboTablas:

IsTextSelectionEnabled="{Binding Source=cobTablas, Path=IsEnabled}"

 

3.- El control TextBlock no tiene la propiedad Background en las aplicaciones para la Tienda de Windows. En este caso, lo que yo he hecho es poner dicho control dentro de un objeto Border, ya que lo que yo hacía con el color ese de fondo era darle otro aspecto (tipo información):

<Border Background="LightGoldenrodYellow" 
        Grid.Row="6" Grid.Column="1" Grid.ColumnSpan="3" 
        Margin="5" VerticalAlignment="Top">
    <TextBlock x:Name="lblInfo" Text=""  
               TextWrapping="Wrap" FontSize="10" 
               IsTextSelectionEnabled="{Binding Source=cobTablas, Path=IsEnabled}"/>
</Border>

 

 

Y ya no me he encontrado con más problemas en esta aplicación (simple) que estoy migrando… el problema seguro que lo tendré con el código… #enfin…

 

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

Crear un SplashScreen personalizado para la Windows Store

 

Pues eso… que en las aplicaciones para la Tienda de Windows te "exigen" que tengas una imagen de al menos 620×300 para usar como pantalla de presentación (splash screen) mientras la aplicación se carga.

Si quieres poner algún texto en esa imagen, dicho texto debe ser estático, es decir, cada vez que lo cambies en realidad estarías cambiando la imagen, y lo que te voy a explicar aquí es cómo añadir textos (o cualquier otra cosa) a esa imagen de inicio, pero de forma que sólo se añada (o sea visible) mientras está cargando el programa.

 

Nota:
Este ejemplo está basado, y creo que mejorado, pero sobre todo simplificado, en los ejemplos de la MSDN / SDK:

Cómo extender la pantalla de presentación y Directrices y lista de comprobación para pantallas de presentación, en concreto el ejemplo de C# para Evitar un parpadeo durante la transición a la pantalla de presentación extendida.

 

En este ejemplo he procurado que, en modo de diseño, la imagen esté centrada y dentro de un Grid con idea de que podamos situar los textos donde queramos que estén.

Hay que tener en cuenta que aquí estoy usando la imagen predeterminada de 620×300 pero si utilizas alguna de las otras dos, debes tenerlo en cuenta a la hora de posicionar los textos.

He dejado la imagen predeterminada, es decir, la que utiliza el Visual Studio al crear un nuevo proyecto, por tanto, los valores de las columnas y filas del Grid puede que no sean los adecuados si tienes otra imagen.

Lo que si es conveniente saber es que el ancho y alto de esas columnas y filas deben coincidir con el tamaño de la imagen, ya que así tendremos una visión exacta de dónde estarán posicionados las cosas que le añadamos a esa imagen de inicio.
En este ejemplo he usado el logo pequeño de la aplicación (SmallLogo.png) y un par de cajas de textos, una de ellas la modifico (si es necesario) en tiempo de ejecución.

En la siguiente captura puedes ver cómo quedaría con el código de ejemplo usado en este artículo.

 

ExtendedSplash

 

 

Nota:
El color de fondo de la pantalla de inicio y de la aplicación deberían coincidir con el indicado en el manifiesto de la aplicación (Package.appxmanifest), ya que si no… pues… lo mismo no queda bien.
Fíjate que en el manifiesto de la aplicación hay dos definiciones de colores, uno para las imágenes "normales" (los logos) y otro para la pantalla de inicio (Splash screen). Yo los he puesto todos (también el de MainPage) con el mismo color verde: #185F18.

 

Vamos a ver el código XAML de la página/control que hará de pantalla de inicio en este ejemplo.

 

Código Xaml El código Xaml
<Grid
    x:Class="Splash_Screen.ExtendedSplash"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="using:Splash_Screen"
    mc:Ignorable="d"
    Background="#185F18">

    <!-- la imagen tiene 620 x 300 -->
    <Canvas MaxHeight="300" MaxWidth="620">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="35" />
                <RowDefinition Height="225" />
                <RowDefinition Height="40" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="60"/>
                <ColumnDefinition Width="140"/>
                <ColumnDefinition Width="420"/>
            </Grid.ColumnDefinitions>

            <Image x:Name="extendedSplashImage" Grid.Row="0" Grid.Column="0"
                   Grid.RowSpan="4" Grid.ColumnSpan="4" 
                   Margin="0" Source="///Assets/SplashScreen.png" 
                   ImageOpened="extendedSplashImage_ImageOpened"/>
            <TextBlock x:Name="txtTitulo" x:Uid="txtTitle" 
                       Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
                       Foreground="White" FontSize="18" FontWeight="Bold" 
                       HorizontalAlignment="Center"
                       Text="Prueba de Splash Screen personalizada" />
            <TextBlock x:Name="txtCopyr" Grid.Row="2" Grid.Column="2" 
                       Foreground="White" FontSize="15" FontWeight="Bold" 
                       Margin="0,10,0,0"
                       Text="©Guillermo Som (elGuille), 2013" />
            <Image Grid.Row="2" Grid.Column="0" Grid.RowSpan="2"
                   Height="30" Width="30" Stretch="Uniform" 
                   Source="///Assets/SmallLogo.png" Margin="0,0,0,10"  Opacity="100" />
        </Grid>
    </Canvas>
</Grid> 

 

Nota sobre las tres /// que hay delante de la carpeta en la que están las imágenes:

Esas tres barras indican que es desde el directorio base de la aplicación, de esa forma, si mueves este fichero de la Splash screen a otra carpeta no tendrás que hacer cambios para indicar la ruta de las imágenes.

 

Ahora veamos el código de esa página, como verás hay dos constructores, pero nosotros solo usaremos el que recibe dos parámetros.

 

Código para Visual Basic.NET (VB.NET) El código para Visual Basic .NET
'------------------------------------------------------------------------------
' ExtendedSplash                                                    (19/Ene/13)
' Usar un SplashScreen personalizado
'
' Basado en un ejemplo de la MSDN (y creo que mejorado y simplificado)
' Sólo hay que modificar App.OnLaunched
'
' Cómo extender la pantalla de presentación
' http://msdn.microsoft.com/es-es/library/windows/apps/xaml/hh868191.aspx
' y Directrices y lista de comprobación para pantallas de presentación para
' Evitar un parpadeo durante la transición a la pantalla de presentación extendida
' http://msdn.microsoft.com/es-es/library/windows/apps/hh465338.aspx#ts_flicker_cs
'
'
' ©Guillermo 'guille' Som, 2013
'------------------------------------------------------------------------------

Imports System
Imports Windows.ApplicationModel.Activation
Imports Windows.Foundation
Imports Windows.UI.Core
Imports Windows.UI.Xaml
Imports Windows.UI.Xaml.Controls

Partial Class ExtendedSplash
    ' Rect to store splash screen image coordinates. 
    Friend splashImageRect As Rect
    ' Variable to track splash screen dismissal status. 
    Friend dismissed As Boolean = False
    ' Variable to hold the splash screen object. 
    Private splash As SplashScreen

    Friend rootFrame As Frame


    Private showWindowTimer As DispatcherTimer
    Private showWindowTimerNum As Integer = 0

    Private Sub OnShowWindowTimer(sender As Object, e As Object)
        showWindowTimerNum += 1

        If showWindowTimerNum = 1 Then
            ' Activate/show the window, now that the splash image has rendered
            Window.Current.Activate()

            ' aquí hacemos un pequeño descanso antes de mostrar
            ' la página principal
        ElseIf showWindowTimerNum >= 50 Then
            showWindowTimer.Stop()
            cargarMainPage()
        End If

    End Sub

    Private Sub extendedSplashImage_ImageOpened(sender As Object, e As RoutedEventArgs)
        ' ImageOpened means the file has been read, but the image hasn't been painted yet.
        ' Start a short timer to give the image a chance to render, before showing the window
        ' and starting the animation.
        showWindowTimer = New DispatcherTimer()
        showWindowTimer.Interval = TimeSpan.FromMilliseconds(50)
        AddHandler showWindowTimer.Tick, AddressOf OnShowWindowTimer
        showWindowTimer.Start()

    End Sub

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub

    ''' <summary> 
    ''' Constructor with splash screen information 
    ''' </summary> 
    Public Sub New(splashscreen As SplashScreen, loadState As Boolean)
        InitializeComponent()

        If DateTime.Now.Year > 2013 Then
            txtCopyr.Text = "©Guillermo Som (elGuille), 2013-" & DateTime.Now.Year.ToString
        End If

        ' Listen for window resize events to reposition the extended splash screen image accordingly. 
        ' This is important to ensure that the extended splash screen is formatted properly in response to
        ' snapping, unsnapping, rotation, etc... 
        AddHandler Window.Current.SizeChanged, AddressOf ExtendedSplash_OnResize

        splash = splashscreen

        If splash IsNot Nothing Then
            ' Register an event handler to be executed when the splash screen has been dismissed. 
            AddHandler splash.Dismissed, AddressOf DismissedEventHandler

            ' Retrieve the window coordinates of the splash screen image. 
            splashImageRect = splash.ImageLocation
            PositionImage()
        End If

        ' Create a Frame to act as the navigation context  
        rootFrame = New Frame()

        '' Restore the saved session state if necessary 
        'RestoreStateAsync(loadState)


    End Sub

    'Public Async Sub RestoreStateAsync(loadState As Boolean)
    '    If loadState Then
    '        Await SuspensionManager.RestoreAsync()
    '    End If
    '    ' Normally you should start the time consuming task asynchronously here and  
    '    ' dismiss the extended splash screen in the completed handler of that task 
    '    ' This sample dismisses extended splash screen in the handler for "Learn More" button for demonstration 
    'End Sub

    ' Position the extended splash screen image in the same location as the system splash screen image. 
    Private Sub PositionImage()
        extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X)
        extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y)

        extendedSplashImage.Height = splashImageRect.Height
        extendedSplashImage.Width = splashImageRect.Width
    End Sub

    Private Sub ExtendedSplash_OnResize(sender As Object, e As WindowSizeChangedEventArgs)
        ' Safely update the extended splash screen image coordinates.
        ' This function will be fired in response to snapping, unsnapping, rotation, etc... 
        If splash IsNot Nothing Then
            ' Update the coordinates of the splash screen image. 
            splashImageRect = splash.ImageLocation
            PositionImage()
        End If
    End Sub

    Private Sub cargarMainPage()
        ' Navigate to MainPage 
        rootFrame.Navigate(GetType(MainPage))

        '' Set extended splash info on Main Page 
        'DirectCast(rootFrame.Content, MainPage).SetExtendedSplashInfo(splashImageRect, dismissed)

        ' Place the frame in the currrent window 
        Window.Current.Content = rootFrame

    End Sub

    ' Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view). 
    Private Sub DismissedEventHandler(sender As SplashScreen, e As Object)
        dismissed = True

        ' Navigate away from the app's extended splash screen after completing setup operations here... 
        ' This sample navigates away from the extended splash screen when the "Learn More" button is clicked. 
    End Sub
End Class

 

 

Código para C Sharp (C#) El código para C#
//-----------------------------------------------------------------------------
// ExtendedSplash                                                   (19/Ene/13)
// Usar un SplashScreen personalizado
//
// Basado en un ejemplo de la MSDN (y creo que mejorado y simplificado)
// Sólo hay que modificar App.OnLaunched
//
// Cómo extender la pantalla de presentación
// http://msdn.microsoft.com/es-es/library/windows/apps/xaml/hh868191.aspx
// y Directrices y lista de comprobación para pantallas de presentación para
// Evitar un parpadeo durante la transición a la pantalla de presentación extendida
// http://msdn.microsoft.com/es-es/library/windows/apps/hh465338.aspx#ts_flicker_cs
//
//
// ©Guillermo 'guille' Som, 2013
//------------------------------------------------------------------------------

using System;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Splash_Screen
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class ExtendedSplash : Grid
    {
        public ExtendedSplash()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Constructor with splash screen information
        /// </summary>
        public ExtendedSplash(SplashScreen splashscreen, bool loadState)
        {
            InitializeComponent();

            //
            // Aquí pondremos lo que haya que actualizar en los textos
            //
            if (DateTime.Now.Year > 2013)
            {
                txtCopyr.Text = "©Guillermo Som (elGuille), 2013-" + DateTime.Now.Year.ToString();
            }

            // Listen for window resize events to reposition the extended splash screen image accordingly.
            // This is important to ensure that the extended splash screen is formatted properly in response to
            // snapping, unsnapping, rotation, etc...
            Window.Current.SizeChanged += ExtendedSplash_OnResize;

            splash = splashscreen;

            if (splash != null)
            {
                // Register an event handler to be executed when the splash screen has been dismissed.
                splash.Dismissed += DismissedEventHandler;

                // Retrieve the window coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();
            }

            // Create a Frame to act as the navigation context
            rootFrame = new Frame();

            // Restore the saved session state if necessary
            //RestoreStateAsync(loadState);


        }
        
        //async void RestoreStateAsync(bool loadState)
        //{
        //    if (loadState)
        //        await SuspensionManager.RestoreAsync();

        //    // Normally you should start the time consuming task asynchronously here and  
        //    // dismiss the extended splash screen in the completed handler of that task 
        //    // This sample dismisses extended splash screen  in the handler for "Learn More" button for demonstration 
        //} 


        // Rect to store splash screen image coordinates.
        internal Rect splashImageRect;

        // Variable to track splash screen dismissal status.
        internal bool dismissed = false;

        // Variable to hold the splash screen object.
        private SplashScreen splash;

        internal Frame rootFrame;


        private DispatcherTimer showWindowTimer;
        private int showWindowTimerNum = 0;

        private void OnShowWindowTimer(object sender, object e)
        {
            showWindowTimerNum += 1;

            if (showWindowTimerNum == 1)
            {
                // Activate/show the window, now that the splash image has rendered
                Window.Current.Activate();
            }
            // aquí hacemos un pequeño descanso antes de mostrar
            // la página principal
            else if (showWindowTimerNum >= 50)
            {
                showWindowTimer.Stop();
                cargarMainPage();
            }
        }

        private void extendedSplashImage_ImageOpened(object sender, RoutedEventArgs e)
        {
            // ImageOpened means the file has been read, but the image hasn't been painted yet.
            // Start a short timer to give the image a chance to render, before showing the window
            // and starting the animation.
            showWindowTimer = new DispatcherTimer();
            showWindowTimer.Interval = TimeSpan.FromMilliseconds(50);
            showWindowTimer.Tick += OnShowWindowTimer;
            showWindowTimer.Start();
        }

        // Position the extended splash screen image in the same location as the system splash screen image.
        private void PositionImage()
        {
            extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
            extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);

            extendedSplashImage.Height = splashImageRect.Height;
            extendedSplashImage.Width = splashImageRect.Width;
        }

        private void ExtendedSplash_OnResize(object sender, WindowSizeChangedEventArgs e)
        {
            // Safely update the extended splash screen image coordinates.
            // This function will be fired in response to snapping, unsnapping, rotation, etc...
            if (splash != null)
            {
                // Update the coordinates of the splash screen image.
                splashImageRect = splash.ImageLocation;
                PositionImage();
            }
        }

        private void cargarMainPage()
        {
            // Navigate to MainPage
            rootFrame.Navigate(typeof(MainPage));

            // Set extended splash info on Main Page
            //(rootFrame.Content as MainPage).SetExtendedSplashInfo(splashImageRect, dismissed);

            // Place the frame in the currrent window
            Window.Current.Content = rootFrame;
        }

        // Include code to be executed when the system has transitioned 
        // from the splash screen to the extended splash screen (application's first view).
        private void DismissedEventHandler(SplashScreen sender, object e)
        {
            dismissed = true;

            // Navigate away from the app's extended splash screen after completing setup operations here...
            // This sample navigates away from the extended splash screen when the "Learn More" button is clicked.
        }  
    }
}

 

Ahora sólo falta modificar el código de App.xaml, concretamente el método OnLaunched para que muestre nuestra página de inicio en lugar de MainPage (desde el código de ExtendedSplash nos encargamos de mostrar esa página cuando se ha terminado de mostrar.

 

Aquí tienes el código de VB y el de C# del método OnLaunched de la clase App:

Visual Basic:

Protected Overrides Sub OnLaunched(args As Windows.ApplicationModel.Activation.LaunchActivatedEventArgs)

    ' Para usar el SplashScreen personalizado                   (07/Ene/13)
    If args.PreviousExecutionState <> ApplicationExecutionState.Running Then
        Dim loadState As Boolean = (args.PreviousExecutionState = ApplicationExecutionState.Terminated)
        Dim extendedSplash As ExtendedSplash = New ExtendedSplash(args.SplashScreen, loadState)
        Window.Current.Content = extendedSplash

        ' ExtendedSplash will activate the window when its initial content has been painted.

        ' Salir
        Exit Sub
    End If


    Dim rootFrame As Frame = TryCast(Window.Current.Content, Frame)

    ' Do not repeat app initialization when the Window already has content,
    ' just ensure that the window is active

    If rootFrame Is Nothing Then
        ' Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = New Frame()
        If args.PreviousExecutionState = ApplicationExecutionState.Terminated Then
            ' TODO: Load state from previously suspended application
        End If
        ' Place the frame in the current Window
        Window.Current.Content = rootFrame
    End If
    If rootFrame.Content Is Nothing Then
        ' When the navigation stack isn't restored navigate to the first page,
        ' configuring the new page by passing required information as a navigation
        ' parameter
        If Not rootFrame.Navigate(GetType(MainPage), args.Arguments) Then
            Throw New Exception("Failed to create initial page")
        End If
    End If

    ' Ensure the current window is active
    Window.Current.Activate()
End Sub

 

 

C#:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    // Para usar el SplashScreen personalizado              (07/Ene/13)
    if (args.PreviousExecutionState != ApplicationExecutionState.Running)
    {
        bool loadState = (args.PreviousExecutionState == ApplicationExecutionState.Terminated);
        ExtendedSplash extendedSplash = new ExtendedSplash(args.SplashScreen, loadState);
        Window.Current.Content = extendedSplash;

        // ExtendedSplash will activate the window when its initial content has been painted.

        // Salir
        return;
    }
    

    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        if (!rootFrame.Navigate(typeof(MainPage), args.Arguments))
        {
            throw new Exception("Failed to create initial page");
        }
    }
    // Ensure the current window is active
    Window.Current.Activate();
}

 

Ya solo quedaría hacer algo en MainPage, pero ahí haz lo que quieras, ya que no hace falta añadir ningún código para esto de mostrar la SplashScreen. Es decir, en esa página añade las cosas que tu aplicación tendrá que hacer.

Y si en lugar de usar MainPage utilizas cualquiera de las otras páginas usadas en los ejemplos de Visual Studio, acuérdate de cambiar el nombre de esa página por la que corresponda en el método que te acabo de mostrar de la clase App y también en el método cargarMainPage de ExtendedSplash.

 

Espero que te sirva para personalizar mejor tus aplicaciones para la Tienda de Windows 😉

 

Nos vemos.

Guillermo

Ejemplo sencillo de notificaciones (toast) para Windows Store

 

Pues eso… que aunque aún no son horas de toastar nada, pero te voy a poner un par de ejemplos (o uno solo) de cómo mostrar las notificaciones del sistema (toast notifications), que no son otra cosa que los mensajes (o notificaciones) que aparecen en los laterales de la parte superior de la pantalla de Windows (ver la figura 1).

 

toast01
Figura 1.

 

En principio este tipo de notificaciones están pensadas para eso, notificar al usuario de algo en un momento concreto (ahora te explico esto), pero sobre todo, que el usuario se entere de que eso está ocurriendo (lo están avisando), así que… este tipo de notificaciones se mostrarán siempre encima de lo que haya en ese momento en la pantalla… y cuando digo pantalla me refiero a la pantalla tanto de inicio de Windows 8 como a la "pantalla del escritorio".

 

Lo del par de ejemplos que te comentaba antes es porque hay dos formas de notificar (o mostrar las notificaciones). Una es de forma inmediata, es decir: ¡ya!. La otra es para que se muestre en el momento que indiquemos, ya sean unos segundos después o más tiempo… no sé exactamente cuanto tiempo, pero incluso días y meses después. La tercera (sí, ya se que te dije que había dos, pero…) es una especie de variación de la segunda, ya que también se puede indicar la periodicidad de dicha notificación (si se repite, etc.)

Es decir, que hay muchas posibilidades de usar las notificaciones del sistema (o toast notifications), así que, si además de lo que te explique aquí quieres saber más: Introducción a las notificaciones del sistema (Toast notification overview)

 

Lo primero que debes saber es que esto es para usarlo en las aplicaciones de Windows Store y que en el manifiesto de la aplicación tienes que indicar que quieres usar este tipo de notificaciones.

Abre el fichero Package.appxmanifest y en la primera ficha (Application UI) en la lista de las imágenes busca Badge Logo y selecciónala, en la parte de la derecha tendrás las opciones de notificaciones (notifications) en la lista Toast capable selecciona Yes tal como puedes ver en la figura 2.
De no hacerlo, las notificaciones simplemente ¡¡¡ NO APARECERÁN !!!

 

toast03
Figura 2

 

Una vez que tenemos esto, vamos a ver el código (en principio de Visual Basic, ya también el de C# o lo pongo más tarde o te pongo un enlace a Pastebin para cuando esté listo), de todas formas, los que usáis C# no os podéis quejar mucho, ya que la mayoría de los ejemplos de la documentación y otros sitios web están en C#, algo que no es tan habitual para los que prefieren usar Visual Basic, en fin…

 

Los pasos a seguir:

Crea un proyecto para la tienda de Windows (yo he creado el m´s básico).

Abre el fichero del manifiesto de la aplicación (Package.appxmanifest) y selecciona Yes en Toast capable (ver figura 2).

En MainPage.xaml vamos a crear una barra de aplicación (AppBar) con tres botones.
El diseño de esos botones los tomaremos del fichero StandardStyles.xaml (en la carpeta Common), pero como quiero cambiarles el texto para que me lo muestre en castellano, los he copiado y pegado en los recursos de la página (cambiándoles el nombre para que no haya conflictos, aunque en esta caso no los habría ya que los botones del fichero StandarStyles suelen estar comentados, al menos en la plantilla de las aplicaciones básicas.

Esos tres botones usarán distintos tipos de notificaciones, con idea de que sepas manejarte con varias de las posibilidades que tiene esto de los "toast notifications".

El primer botón envía una notificación inmediata usando un periodo largo, es decir, la notificación tardará más de lo habitual en quitarse, también está desactivado el audio (es una notificación silenciosa y de larga duración, jeje).

Al pulsar en el segundo botón mostrará una notificación a los 3 segundos y esta si que será con sonido y de duración normal.

El tercer botón se notificará de forma inmediata y otra más a los 10 segundos, ambas serán de larga duración y con sonido.

 

Aquí tienes una captura en pleno funcionamiento:

 

toast05
Figura 3. La aplicación en funcionamiento

 

Este es el código XAML completo, con la definición de los estilos de los tres botones.

 

<Page
    x:Class="Toast_notifications.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Toast_notifications"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <Style x:Key="PlayEsAppBarButtonStyle" TargetType="ButtonBase" 
               BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" Value="PlayAppBarButton"/>
            <Setter Property="AutomationProperties.Name" Value="Inmediato"/>
            <Setter Property="Content" Value="&#xE102;"/>
        </Style>

        <Style x:Key="HelpEsAppBarButtonStyle" TargetType="ButtonBase" 
               BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" Value="HelpAppBarButton"/>
            <Setter Property="AutomationProperties.Name" Value="Ayuda"/>
            <Setter Property="Content" Value="&#xE11B;"/>
        </Style>
        <Style x:Key="ClockEsAppBarButtonStyle" TargetType="ButtonBase" 
               BasedOn="{StaticResource AppBarButtonStyle}">
            <Setter Property="AutomationProperties.AutomationId" Value="ClockAppBarButton"/>
            <Setter Property="AutomationProperties.Name" Value="Programado"/>
            <Setter Property="Content" Value="&#xE121;"/>
        </Style>
    </Page.Resources>
    
    <Page.BottomAppBar>
        <AppBar x:Name="bottomAppBar1" Padding="10,0,10,0">
            <Grid>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
                    <Button Style="{StaticResource PlayEsAppBarButtonStyle}" 
                            Click="ButtonPlay_Click" />
                    <Button Style="{StaticResource ClockEsAppBarButtonStyle}" 
                            Click="ButtonClock_Click" />
                </StackPanel>
                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                    <Button Style="{StaticResource HelpEsAppBarButtonStyle}" 
                            Click="ButtonHelp_Click" />
                </StackPanel>
                
            </Grid>
        </AppBar>
    </Page.BottomAppBar>
    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="80" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <!-- El título de la página -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="120"/>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock x:Name="pageTitle" Grid.Column="1" Text="Prueba de toast notifications" 
                       IsHitTestVisible="false" 
                       Style="{StaticResource PageHeaderTextStyle}" />
            <Image x:Name="imgLogo" Grid.Column="2" Margin="10" 
                   Source="///Assets/SmallLogo.png" Stretch="Uniform" />
        </Grid>

    </Grid>
</Page>

 

Y este es el código de los tres botones Visual Basic:

Primero te pongo las dos importaciones que hay que añadir:

 

Imports Windows.UI.Notifications
Imports Windows.Data.Xml.Dom

 

Y este es el código de los tres botones, las explicaciones están en los comentarios.

 

Private Sub ButtonPlay_Click(sender As Object, e As RoutedEventArgs)

    ' La plantilla a usar, esta Text03 es:
    ' un texto de cabecera que puede ocupar dos líneas y una línea de texto normal
    Dim toastTemplate As ToastTemplateType = ToastTemplateType.ToastText03
    ' Asignamos el template a un documento Xml
    Dim toastXml As XmlDocument = ToastNotificationManager.GetTemplateContent(toastTemplate)

    ' El texto para el primero elemento de la pantilla
    Dim toastTextElements As XmlNodeList = toastXml.GetElementsByTagName("text")
    toastTextElements(0).AppendChild(toastXml.CreateTextNode("Cargando ..."))

    ' Si queremos que la duración sea larga
    ' Puede ser corta o larga, corta es la predeterminada
    Dim toastNode As IXmlNode = toastXml.SelectSingleNode("/toast")
    TryCast(toastNode, XmlElement).SetAttribute("duration", "long")

    ' Si queremos quitar el sonido
    ' (o indicar alguno en particular)
    ' tenemos que usar el elemento "audio"
    'Dim toastNode As IXmlNode = toastXml.SelectSingleNode("/toast")
    Dim audio As XmlElement = toastXml.CreateElement("audio")
    audio.SetAttribute("silent", "true")
    toastNode.AppendChild(audio)


    Dim toast As New ToastNotification(toastXml)
    ToastNotificationManager.CreateToastNotifier().Show(toast)
End Sub

Private Sub ButtonClock_Click(sender As Object, e As RoutedEventArgs)

    ' La plantilla a usar, esta Text03 es:
    ' un texto de cabecera que puede ocupar dos líneas y una línea de texto normal
    Dim toastTemplate As ToastTemplateType = ToastTemplateType.ToastText03
    ' Asignamos el template a un documento Xml
    Dim toastXml As XmlDocument = ToastNotificationManager.GetTemplateContent(toastTemplate)

    ' El texto para el primero elemento de la pantilla
    Dim toastTextElements As XmlNodeList = toastXml.GetElementsByTagName("text")
    toastTextElements(0).AppendChild(
        toastXml.CreateTextNode("A los 3 segundos después de haber pulsado en el botón."))
    toastTextElements(1).AppendChild(
        toastXml.CreateTextNode("Segundo texto."))

    ' Esto es para indicar que esta notificación se hará en el momento indicado
    Dim dueTime As DateTime = DateTime.Now.AddSeconds(3)
    Dim scheduledToast As New ScheduledToastNotification(toastXml, dueTime)

    ToastNotificationManager.CreateToastNotifier().AddToSchedule(scheduledToast)

End Sub

Private Sub ButtonHelp_Click(sender As Object, e As RoutedEventArgs)
    ' La plantilla a usar, esta Text02 es:
    ' un texto de cabecera y un texto normal que puede ocupar dos líneas
    Dim toastTemplate As ToastTemplateType = ToastTemplateType.ToastText02
    ' Asignamos el template a un documento Xml
    Dim toastXml As XmlDocument = ToastNotificationManager.GetTemplateContent(toastTemplate)

    ' El texto para el primero elemento de la pantilla
    Dim toastTextElements As XmlNodeList = toastXml.GetElementsByTagName("text")
    toastTextElements(0).AppendChild(
        toastXml.CreateTextNode("Esto se mostrará durante más tiempo ..."))
    toastTextElements(1).AppendChild(
        toastXml.CreateTextNode("Siempre puedes cerrar las notificaciones en la X superior."))

    ' Si queremos que la duración sea larga
    ' Puede ser corta o larga, corta es la predeterminada
    Dim toastNode As IXmlNode = toastXml.SelectSingleNode("/toast")
    TryCast(toastNode, XmlElement).SetAttribute("duration", "long")

    ' Esto es para indicar que esta notificación se hará en el momento indicado
    Dim dueTime As DateTime = DateTime.Now.AddSeconds(10)
    Dim scheduledToast As New ScheduledToastNotification(toastXml, dueTime)

    ToastNotificationManager.CreateToastNotifier().AddToSchedule(scheduledToast)

    Dim toast As New ToastNotification(toastXml)
    ToastNotificationManager.CreateToastNotifier().Show(toast)

 

Ese es el código para C# (no he tardado tanto, ¿verdad? son las 06.35 y lo publiqué a eso de las 6.05)

 

Añade estas dos importaciones:

 

using Windows.UI.Notifications;
using Windows.Data.Xml.Dom;

 

private void ButtonPlay_Click(object sender, RoutedEventArgs e)
{
    // La plantilla a usar, esta Text03 es:
    // un texto de cabecera que puede ocupar dos líneas y una línea de texto normal
    ToastTemplateType toastTemplate = ToastTemplateType.ToastText03;

    // Asignamos el template a un documento Xml
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);

    // El texto para el primer elemento de la pantilla
    XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
    toastTextElements[0].AppendChild(toastXml.CreateTextNode("Cargando ..."));

    // Si queremos que la duración sea larga
    // Puede ser corta o larga, corta es la predeterminada
    IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
    ((XmlElement)toastNode).SetAttribute("duration", "long");

    // Si queremos quitar el sonido
    // (o indicar alguno en particular)
    // tenemos que usar el elemento "audio"
    // IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
    XmlElement audio = toastXml.CreateElement("audio");
    audio.SetAttribute("silent", "true");
    toastNode.AppendChild(audio);

    ToastNotification toast = new ToastNotification(toastXml);
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

private void ButtonClock_Click(object sender, RoutedEventArgs e)
{
    // La plantilla a usar, esta Text03 es:
    // un texto de cabecera que puede ocupar dos líneas y una línea de texto normal
    ToastTemplateType toastTemplate = ToastTemplateType.ToastText03;

    // Asignamos el template a un documento Xml
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);

    // El texto para el primer elemento de la pantilla
    XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
    toastTextElements[0].AppendChild(
        toastXml.CreateTextNode("A los 3 segundos después de haber pulsado en el botón."));
    toastTextElements[1].AppendChild(
        toastXml.CreateTextNode("Segundo texto."));

    // Esto es para indicar que esta notificación se hará en el momento indicado
    DateTime dueTime = DateTime.Now.AddSeconds(3);
    ScheduledToastNotification scheduledToast = new ScheduledToastNotification(toastXml, dueTime);

    ToastNotificationManager.CreateToastNotifier().AddToSchedule(scheduledToast);
}

private void ButtonHelp_Click(object sender, RoutedEventArgs e)
{
    // La plantilla a usar, esta Text02 es:
    // un texto de cabecera y un texto normal que puede ocupar dos líneas
    ToastTemplateType toastTemplate = ToastTemplateType.ToastText02;
    // Asignamos el template a un documento Xml
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(toastTemplate);

    // El texto para el primero elemento de la pantilla
    XmlNodeList toastTextElements = toastXml.GetElementsByTagName("text");
    toastTextElements[0].AppendChild(
        toastXml.CreateTextNode("Esto se mostrará durante más tiempo ..."));
    toastTextElements[1].AppendChild(
        toastXml.CreateTextNode("Siempre puedes cerrar las notificaciones en la X superior."));

    // Si queremos que la duración sea larga
    // Puede ser corta o larga, corta es la predeterminada
    IXmlNode toastNode = toastXml.SelectSingleNode("/toast");
    (toastNode as XmlElement).SetAttribute("duration", "long");

    // Esto es para indicar que esta notificación se hará en el momento indicado
    DateTime dueTime = DateTime.Now.AddSeconds(10);
    ScheduledToastNotification scheduledToast = new ScheduledToastNotification(toastXml, dueTime);

    ToastNotificationManager.CreateToastNotifier().AddToSchedule(scheduledToast);

    ToastNotification toast = new ToastNotification(toastXml);
    ToastNotificationManager.CreateToastNotifier().Show(toast);
}

 

 

Como comentario adicional, decirte que podemos "detener" una notificación si tenemos a nuestra disposición el objeto con la que se creó, ya que para detenerla tenemos que usar el método Hide de la función compartida CreateToastNotifier. A ese método Hide le tenemos que pasar una referencia al objeto que queremos detener. Yo esto lo he usado en la aplicación que he mandado a la tienda de Windows, en la que muestro la notificación mientras se carga una página web en el control WebView y la oculto cuando dicha página se ha terminado de cargar o lo que es lo mismo, intercepto el evento LoadComplete del control WebView, y ahí es donde quito la notificación.

Como es natural, el objeto "toast" usado para esa notificación no puede estar definido dentro de un sub, ya que así no sería accesible desde otra parte del código. Por tanto, en ese caso, el objeto toast usado para poder ocultarlo cuando queramos, lo tendríamos que definir fuera de cualquier método.

 

Espero que todo esto te sea de utilidad y para los que prefieren el C#, dadme unos minutos a que cree el proyecto en ese lenguaje y convierta el código, lo tacho porque ya está publicado.

 

Nos vemos.

Guillermo

Como mostrar paneles en el panel de configuración (Windows Store)

 

Pues eso… que ahora necesito crear un panel de configuración y como quiero hacerlo "al estilo metro" pues… he ido a mirar por aquí y resulta que no lo tenía publicado… así que… ahora te voy a explicar lo que yo sé sobre cómo mostrar un panel personalizado en el panel de configuración de las aplicaciones para Windows 8 o Windows Store.

Hace un mes o así ya te expliqué cómo o, mejor dicho, dónde se deben poner las opciones de configuración de nuestras aplicaciones para la tienda de Windows.

Ahora lo que toca es saber hacerlo nosotros, que eso de que otros lo hagan está muy bien… pero… si lo necesitamos en nuestra aplicación, pues…

Cuando hice mi panel de configuración me basé en este ejemplo: Quickstart: Add app settings (Windows Store apps using C#/VB/C++ and XAML) (Windows).

Y a partir de ahí lo fui adaptando… y aquí te dejo una forma simplificada de eso mismo que explican en ese "inicio rápido".

En mi caso, me compliqué un poco más, ya que necesitaba varias opciones de configuración, bueno, en realidad sólo dos: la configuración de opciones en sí misma y mostrar "acerca de". Había una tercera opción que era mostrar la ayuda, pero esa ayuda no se mostraba dentro del panel de configuración, si no que desde ese panel se llamaba a la página de ayuda.
No sé porqué estoy con el pretérito… en fin… seguramente porque es algo que "hice" hace unas semanas…

Básicamente vamos a usar el código mostrado en el Quickstart que te he comentado hace unos párrafos, pero modificándolo un poco.

Lo primero es crear una aplicación para Windows Store, usa la sencilla (Blank App) y en el lenguaje de tu preferencia, yo la crearé usando Visual Basic, pero lo mismo da (al menos en estos primeros pasos). En la figura 1 estoy creando el proyecto.

 

settings 01
Figura 1. Creamos un nuevo proyecto básico para la tienda de Windows

Si estás usando Visual Basic y tienes activado Option Strict On, tendrás que modificar la declaración de rootFrame en el método OnLaunched de App.xaml.vb ya que la plantilla usada no tiene en cuenta la comprobación estricta y dará erro (ver la figura 2).

 

settings 02
Figura 2. Si tienes activado Option Strict debes hacer la conversión correcta en la asignación a rootFrame

 

Acepta el cambio que propone y ahora lo modificamos para que sea más preciso y así también será como el indicado en el código de C# en el que se utiliza as para hacer la conversión, que como seguramente ya sabrás en VB se corresponde con TryCast. Por tanto el código debe quedar así:

 

' En VB debe quedar de esta forma:
Dim rootFrame As Frame = TryCast(Window.Current.Content, Frame)


// En C# la asignación es de esta forma:
Frame rootFrame = Window.Current.Content as Frame;

 

Ahora agregamos un nuevo elemento, en este caso un UserControl (figura 3) al que llamaremos Settings.xaml.

 

settings 03

Figura 3. Añadimos un user control

 

Y pegamos el código del User Control que nos muestra en la página del Quickstart y le hacemos estos cambios:

Truco:

Si copias a partir de xmlns:local="using:SettingsExample" y lo pegas después del xmlns:local de nuestro control de usuario, tendrás que modificar menos.

 

Debes fijarte que xClass tenga el valor de nuestro proyecto en el formato espacio de nombres punto nombre del control, después en xmlns:local debes indicar el espacio de nombres raíz del proyecto, que en mi caso es: Ejemplo_settings_charm tal como puse en la figura 1.

Después, vamos a modificar el color de fondo del título, eso lo podemos hacer buscando la cadena "Orange" y cambiándola por "Navy" (o el color que quieras usar).

El título lo vamos a hacer modificable, por tanto hay que indicarle un nombre al TextBlock que contiene la cadena "Sound Options", que estará casi al final del código:

 

<TextBlock x:Name="txtTitulo"

Abre la parte del código del control de usuario para crear el método MySettingsBackClicked, éste sería el de Visual Basic, el de C# será el mismo que se muestra en el Quickstart pero agregándole la condición que si está en modo Snapped no se muestre (la condición que le he agregado al final).

 

Private Sub MySettingsBackClicked(sender As Object, e As RoutedEventArgs)
    If TypeOf Me.Parent Is Popup Then
        TryCast(Me.Parent, Popup).IsOpen = False
    End If

    ' Si no está en modo snapped...
    If ApplicationView.Value <> ApplicationViewState.Snapped Then
        SettingsPane.Show()
    End If


End Sub

En C# vendría a ser algo así:

//  Si no está  en modo snapped...
if( ApplicationView.Value !=  ApplicationViewState.Snapped )
{
    SettingsPane.Show();
}

 

Todo eso es para que si está en modo Snapped, es decir, se muestra en un área estrecha a la derecha o a la izquierda, que no se muestre el panel de configuración, ya que ese "trozo" de pantalla suele ser de 320 pixel y… bueno, que es muy estrecha.

 

Debido a que el TextBlock txtTitulo es interno a la clase Settings, tendremos que crear una propiedad pública para que podamos acceder a ese contenido desde otra clase de nuestro proyecto.

No te muestro el código para que lo hagas tú.

Pero es simple, utiliza la propiedad Text del control interno (txtTitulo) para asignar el valor y devolver el contenido de esa propiedad a la que llamaremos Titulo.

 

En el código XAML del control debemos hacer unos cambios, ya que utilizan valores de recursos que ya no están definidos, así que… busca los valores indicados a la izquierda y los reemplazas por los indicados a la derecha o después de –> por si no cabe en la misma línea:

BackButtonBackgroundBrush –> BackButtonBackgroundThemeBrush,

BackButtonGlyphBrush –> BackButtonForegroundThemeBrush,

BackButtonPressedGlyphBrush –> BackButtonPressedForegroundThemeBrush,

FocusVisualWhiteStrokeBrush –> FocusVisualWhiteStrokeThemeBrush,

FocusVisualBlackStrokeBrush –> FocusVisualBlackStrokeThemeBrush,

BackButtonHoverBackgroundBrush –> BackButtonPointerOverBackgroundThemeBrush,

BackButtonHoverGlyphBrush –> BackButtonPointerOverForegroundThemeBrush,

Nota:

La clase SettingsPane está definida en el espacio de nombres Windows.UI.ApplicationSettings

 

Ahora vamos a agregar el código en la clase MainPage.

Para los que han elegido Visual Basic:

Añade el constructor (un método con el nombre New) al código de la clase, de forma que podamos agregar código a ese método. Para agregarlo, en la lista desplegable de la derecha busca New, si no te muestra nada, asegúrate de que en la lista de la izquierda esté seleccionada la clase MainPage.

En C# no es necesario ya que siempre se agrega el constructor (un método con el mismo nombre que la clase)

En el constructor hay que añadir una llamada al método inicializarPopup() que será el encargado de hacer algunas "cosillas" de inicialización.

Añadir al principio importaciones a estos espacios de nombres: Windows.UI.ApplicationSettings y Windows.UI.Core.

El primero es para las clases: SettingsPane, la clase de evento SettingsPaneCommandsRequestedEventArgs y la clase de creación de comandos SettingsCommand.

La segunda importación es para poder usar la clase WindowSizeChangedEventArgs del método de evento OnWindowSizeChanged.

De todas formas, esas importaciones las puedes hacer automáticamente si utilizas el corrector de errores de Visual Studio (tanto para Visual Basic como Visual C#).

 

Ahora pega el código xaml de MainPage (de la famosa página de inicio rápido).

Fíjate que te mostrará un error indicando que ApplicationPageBackgroundBrush no está definido, no lo busques porque no lo está, lo que debes hacer es cambiarlo por este otro valor: ApplicationPageBackgroundThemeBrush.

 

Nota:

En esta página puedes encontrar algunos de los valores usados antes en los recursos de estilos que ya no están y te muestra el que le corresponde ahora:

Upgrade Metro App From Beta To RC.

 

Añade también el código que te muestra, ah.. que está en C#, vale… aquí lo tienes si estás usando VB:

(el código te lo dejo en Pastebin que si no, se llena esto demasiado)

 

 

Una cosilla… ahora no tengo "capacidad" para saber porqué, pero en el ejemplo este que estamos haciendo, no se muestra el control que debería mostrarse en el panel de opciones… y la verdad es que no sé porqué… he mirado otros ejemplos, particularmente el de App Settings Sample y ahí si que se ven… aunque en ese caso utilizan una página en lugar de un UserControl… pero ni idea de porqué no se muestra el dichoso control. Bueno, si se muestra, pero no se ve… que es casi lo mismo, pero no es igual…

Lo mismo otro día con más calma lo miro… yo por ahora lo he resuelto cambiando el color de fondo del panel… una chapuza, sí, pero funciona… 😉

<Grid Background="Navy" en lugar de White.

Lo mismo habrá que trastear con los estilos del ToggleSwitch. No sé…

 

Nota del 15/Ene 13.10:

La solución para que se vean los controles es usar RequestedTheme="Light" en App.xaml.

Aquí tienes la explicación de cómo lo he averiguado.

 

Espero que te sea de utilidad.

Nos vemos.

Guillermo