December 2007 - Artículos

Cuando en una aplicación Windows Forms diseñamos una barra de herramientas mediante el control ToolStrip, habremos observado que en tiempo de ejecución, al situar el cursor sobre un control ToolStripButton, el color de fondo del mismo cambia para notificar al usuario de tal circunstancia.

Pero no es el control ToolStripButton sobre el que vamos a hablar aquí, sino que lo haremos acerca de un compañero suyo de la barra de herramientas: ToolStripLabel.

Supongamos que la característica visual que acabamos de comentar (cambio de color de fondo al entrar el cursor), queremos aplicarla a un ToolStripLabel.

Como truco rápido, podríamos recurrir a no emplear en absoluto un ToolStripLabel sino un ToolStripButton, configurado para muestre únicamente texto, como vemos en el siguiente código fuente.

 

private void Form1_Load(object sender, EventArgs e)

{

    // proporcionar apariencia de etiqueta al botón

    this.toolStripButton1.Text = "Prueba";

    this.toolStripButton1.DisplayStyle = ToolStripItemDisplayStyle.Text;

    this.toolStripButton1.AutoToolTip = false;

 

    // asignar un literal a esta etiqueta de la barra de herramientas

    this.toolStripLabel1.Text = "Prueba";

}

 

En la siguiente imagen podemos ver un formulario con un ToolStrip que contiene, en primer lugar un ToolStripButton, y a continuación un ToolStripLabel.

Aparentemente ambos son iguales, con la excepción del resaltado visual automático que proporciona el ToolStripButton al situar encima el cursor del ratón.

Pero supongamos que este truco no nos vale porque es necesario utilizar obligatoriamente el control ToolStripLabel, y además debemos controlar programáticamente la activación/desactivación del color de fondo utilizado para el resaltado.

Ante tales requerimientos, una solución pasaría por desarrollar una clase derivada de ToolStripLabel, en la que dispusiéramos de una propiedad para controlar el estado de activación del resaltado, otra para establecer el color utilizado al activar dicho estado, y finalmente, un reemplazo para el método OnPaint, en el que pintaríamos el control cuando sea necesario aplicarle el color especial, o bien dejar el comportamiento de dibujo por defecto en el resto de casos. Todo esto lo vemos en el siguiente código fuente.

//....

using System.Windows.Forms;

using System.Drawing;

//....

public class ToolStripEtiqueta : ToolStripLabel

{

    bool bResaltar = false;

    Color clrFondoResaltado = SystemColors.Control;

 

    // activar-desactivar el resaltado

    public bool Resaltar

    {

        get

        {

            return bResaltar;

        }

        set

        {

            bResaltar = value;

            this.Invalidate();

        }

    }

 

    // color para el resaltado del control

    public Color FondoResaltado

    {

        get

        {

            return clrFondoResaltado;

        }

 

        set

        {

            clrFondoResaltado = value;

        }

    }

 

    protected override void OnPaint(PaintEventArgs e)

    {

        base.OnPaint(e);

 

        // si hay que dibujar el control resaltado

        if (bResaltar)

        {

            // obtener el contexto gráfico

            Graphics oGraphics = e.Graphics;

 

            // dibujar la superficie del control

            oGraphics.FillRectangle(new SolidBrush(this.clrFondoResaltado),

                e.ClipRectangle);

 

            // dibujar el texto mostrado por la etiqueta

            oGraphics.DrawString(this.Text,

                this.Font,

                new SolidBrush(this.ForeColor),

                e.ClipRectangle.X + 1,

                e.ClipRectangle.Y + 2);

        }

    }

}

Cabe resaltar en el anterior código, que el método OnPaint recibe como parámetro un tipo PaintEventArgs, gracias al cual, obtenemos acceso al contexto gráfico (un tipo Graphics) del objeto sobre el que hemos de realizar operaciones de dibujo (nuestro control ToolStripEtiqueta). La propiedad Graphics.ClipRectangle, nos devuelve el rectángulo correspondiente al área que ocupa el control, la cual pintamos utilizando el color oportuno y el método FillRectangle. Mientras que para escribir el texto literal de la etiqueta utilizamos el método DrawString.

Llegado el momento de poner en práctica la clase que acabamos de escribir, lo que haremos será añadir a nuestro formulario un ToolStrip y varios controles a este último, uno de ellos será, naturalmente, una instancia de nuestra clase ToolStripEtiqueta. Para demostrar el efecto de aplicación del color controlado por nuestro código, emplearemos un CheckBox, que encargará de cambiar el estado de la propiedad Resaltar del objeto ToolStripEtiqueta. Para facilitar el uso de este ejemplo, todas las operaciones las realizaremos desde código sin usar el diseñador de formularios, por lo que simplemente necesitaremos copiar y pegar los fuentes en un proyecto de Visual Studio.

public partial class frmPruebaEtiqueta : Form

{

    ToolStrip tsBarra;

    CheckBox chkResaltar;

    ToolStripEtiqueta lblEtiquetaPropia;

    ToolStripButton btnBoton;

    ToolStripLabel lblEtiquetaNormal;

 

    public frmPruebaEtiqueta()

    {

        InitializeComponent();

    }

 

    private void frmPruebaEtiqueta_Load(object sender, EventArgs e)

    {

        // crear una barra de herramientas para el formulario

        tsBarra = new ToolStrip();

        tsBarra.Dock = DockStyle.Top;

        this.Controls.Add(tsBarra);

 

        // crear controles para la barra de herramientas:

 

        // crear un control etiqueta personalizado

        lblEtiquetaPropia = new ToolStripEtiqueta();

        lblEtiquetaPropia.Text = "Etiqueta nueva";

        lblEtiquetaPropia.FondoResaltado = Color.SpringGreen;

 

        // crear un botón y una etiqueta normal

        btnBoton = new ToolStripButton("Aceptar");

        btnBoton.DisplayStyle = ToolStripItemDisplayStyle.Text;

        lblEtiquetaNormal = new ToolStripLabel("Normal");

 

        // añadir los controles a la barra

        tsBarra.Items.AddRange(new ToolStripItem[] { lblEtiquetaPropia, btnBoton, lblEtiquetaNormal });

 

        // añadir un CheckBox al formulario

        // mediante el que por código activaremos

        // el resaltado para la etiqueta personalizada

        chkResaltar = new CheckBox();

        chkResaltar.Name = "chkResaltar";

        chkResaltar.Text = "Resaltar";

        chkResaltar.Location = new Point(75, 75);

        chkResaltar.CheckedChanged += new EventHandler(chkResaltarCheckedChanged);

        this.Controls.Add(chkResaltar);

    }

 

    private void chkResaltarCheckedChanged(object sender, EventArgs e)

    {

        // cambiar el resaltado del control de etiqueta en la barra

        lblEtiquetaPropia.Resaltar = !lblEtiquetaPropia.Resaltar;

    }

}

La siguiente imagen muestra el ejemplo en ejecución, donde nuestro control ToolStripEtiqueta se muestra en estado resaltado.

Bien, pues esto sería todo 8-)

Un saludo.

Publicado por Luis Miguel Blanco | con no comments
Archivado en: ,

Hace unos pocos días me encontré ante el siguiente problema: desde una máquina virtual montada en VMWare con Windows XP y SQL Server 2005, necesitaba realizar una prueba consistente en conectar a otro servidor SQL Server 2005, instalado en la máquina principal con Windows Vista, para consultar una tabla existente en una de sus bases de datos. Pensando en que por defecto, la posibilidad de conexión ya estaría habilitada en el servidor SQL, intenté registrar desde la máquina virtual el SQL Server del equipo principal, obteniendo el error que vemos en la siguiente imagen.

Tengo instalada la edición Developer de SQL Server 2005, y dado que evidentemente, la posibilidad de conectar a una instalación remota existente en otro servidor de datos no se encontraba establecida por defecto, había que habilitarla de forma manual.

A continuación describimos los pasos a realizar para habilitar el establecimiento de conexiones remotas en SQL Server (ediciones Developer y Express). Como acabo de indicar, el servidor SQL se encuentra alojado en Windows Vista, no obstante, la explicación resulta igualmente válida para Windows XP.

En primer lugar ejecutaremos la utilidad "Configuración de superficie de SQL Server", que se encuentra en la siguiente ruta de menú de Windows: Inicio > Todos los programas > Microsoft SQL Server 2005 > Herramientas de configuración > Configuración de superficie de SQL Server, abriéndose la siguiente ventana.

Una vez iniciada esta herramienta, haremos clic en la opción "Configuración de superficie para servicios y conexiones", lo que nos mostrará un nuevo cuadro de diálogo con los componentes que podemos configurar.

Tal y como se indica en la siguiente imagen, expandiremos el nodo correspondiente al motor de datos, seleccionaremos el elemento "Conexiones remotas", y en el panel derecho haremos clic en la opción "Conexiones locales y remotas". Dentro de este último apartado elegiremos el tipo de conexión a utilizar, por ejemplo "Usar TCP/IP y canalizaciones con nombre".

Después de aplicar los cambios, se mostrará un mensaje advirtiéndonos de que esta nueva configuración no surtirá efecto hasta que no se vuelva a iniciar el servicio del motor de datos, por lo que siguiendo en esta misma ventana de configuración, haremos clic en el elemento "Servicio", que nos proporciona acceso precisamente al servicio del motor de SQL Server, y la posibilidad de pararlo y arrancarlo de nuevo, como vemos en la siguiente imagen.

En el caso de que estemos utilizando SQL Server con nombre de instancia y sin emplear un número concreto de puerto TCP/IP, como siguiente paso deberemos habilitar el servicio SQL Server Browser, que se encuentra como último nodo en esta ventana de configuración de superficie para SQL Server. En esta ocasión simplemente expandiremos el mencionado nodo, y en el panel derecho iniciaremos el servicio en caso de que no estuviese ya en ejecución.

Habilitar este servicio entraña ciertos riesgos de seguridad que deben ser sopesados. Tengamos en cuenta que para nuestro caso lo estamos habilitando dentro de un escenario de pruebas, por lo que una vez terminadas las mismas y para evitar potenciales problemas, podemos deshabilitarlo.

Como paso final, debemos configurar el Firewall de Windows para que los servicios del motor de datos y SQL Server Browser puedan comunicarse con el exterior.

Después de abrir el panel de control de Windows Vista, dentro del apartado "Seguridad" haremos clic en el elemento "Dejar pasar un programa a través de Firewall de Windows", como vemos en la siguiente imagen.

Esta acción abrirá el cuadro de diálogo de configuración del Firewall y nos situará directamente en la pestaña "Excepciones", desde donde podremos añadir, pulsando el botón "Agregar programa", nuevas aplicaciones para que el Firewall del sistema operativo les permita el paso.

Los ejecutables correspondientes a los servicios del motor de datos y explorador de SQL (SQL Server Browser), se encuentran en las siguientes rutas (siempre y cuando hayamos utilizado las opciones predeterminadas durante la instalación de SQL Server 2005):

C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe

C:\Program Files\Microsoft SQL Server\90\Shared\sqlbrowser.exe

Una vez agregados estos ejecutables, se visualizarán en la pestaña "Excepciones" del Firewall, tal y como podemos ver en la siguiente imagen.

Tras este último paso, podemos dar por concluida la configuración de SQL Server 2005 para que permita el establecimiento de conexiones remotas. En el siguiente enlace es posible encontrar información adicional:

http://support.microsoft.com/kb/914277

Espero que os resulte de utilidad.

Un saludo.

 

Publicado por Luis Miguel Blanco | 70 comment(s)
Archivado en:

Cuando vayamos a desarrollar una aplicación ASP.NET utilizando Visual Studio 2008 bajo Windows Vista, es posible que nos encontremos con una desagradable sorpresa a la hora de realizar la depuración. Dicho problema consiste en que el depurador, transcurridos aproximadamente 90 segundos, cancela la ejecución de la aplicación, devolviéndonos al IDE de Visual Studio, y dejándonos en un estado de total desolación 8-(.

Para solventar el problema, debemos realizar un ajuste en la configuración existente en IIS 7 para el grupo de aplicaciones. A continuación describimos los pasos a dar:

En primer lugar abrimos la consola de IIS, y en el panel izquierdo "Conexiones", desplegamos el árbol de nodos, haciendo clic en "Grupos de aplicaciones".

A continuación pasamos al panel central, en el que actualmente deberemos visualizar el detalle de "Grupos de aplicaciones", haciendo clic en el elemento "DefaultAppPool".

Seguidamente, en el panel derecho "Acciones", dentro del apartado "Modificar grupo de aplicaciones", hacemos clic en "Configuración avanzada", abriéndose un cuadro de diálogo, dentro del cual debemos localizar el apartado "Modelo de proceso", y dentro de este, el elemento "Tiempo máximo de respuesta de ping (segundos)", que por defecto tendrá el valor 90. Lo que tenemos que hacer es aumentar el valor de este elemento a una cantidad que nos permita realizar una sesión de depuración sin tantos apuros de tiempo. Aceptaremos el cuadro de diálogo, y a partir de ese momento ya podremos depurar la aplicación con un margen de tiempo mayor.

En la siguiente imagen podéis ver un detalle de lo que acabamos de comentar.

Por otro lado debemos mencionar también que si desarrollamos una aplicación Web alojada localmente sin utilizar IIS, no tendremos este problema ;-).

Un saludo a todos.

 

Publicado por Luis Miguel Blanco | 2 comment(s)
Archivado en:

Una de las formas en que podemos formatear un valor de tipo DateTime consiste en utilizar su método ToString, pasando al mismo una cadena de formato. Si por ejemplo, queremos obtener una fecha y hora con un formato completo, proporcionado "de serie" por la plataforma, pasaremos la cadena "F" a este método, como vemos a continuación:

Dim dtFecha As DateTime = DateTime.Now

' mostrar la fecha con el patrón predeterminado 
' de formato completo para fecha y hora
MessageBox.Show(dtFecha.ToString("F"))

' resultado --> domingo, 16 de diciembre de 2007 18:37:11

Pero supongamos que queremos un formato más personalizado para visualizar el valor de esta variable, algo un poquito rebuscado como esto:


hora 18:57 fecha Diciembre/16/2007


Por supuesto que podríamos resolver el problema sustituyendo, en el método DateTime.ToString, la cadena con el patrón de formato "F" por una cadena que contuviera la combinación adecuada de caracteres especiales de formato, que ofreciera la salida que acabamos de indicar.

Pero he aquí que resulta que en nuestra aplicación hemos utilizado este dichoso patrón de formato en un gran número de lugares, y realizar estas sustituciones sería un trabajo bastante largo y farragoso 8-(

Es por ello, que una posible solución sería la que proponemos a continuación:

En primer lugar, declarar los espacios de nombre System.Globalization y System.Threading en nuestro código.

A continuación instanciar un objeto de la clase DateTimeFormatInfo, cuyos miembros contienen la información de las diversas características de formato a aplicar a una fecha. Para nuestro caso particular, el patrón de formato "F", si asignamos una cadena a la propiedad FullDateTimePattern, cambiaremos dicho formato predeterminado por uno propio.

Dado que en esta nueva cadena de formato vamos a mostrar el nombre del mes, tendremos que asignar también a la propiedad MonthNames, un array con los nombres de los meses (¡¡atención!!, de trece elementos, el último vacío), ya que en caso contrario, el nombre se mostrará en inglés.

Seguidamente, para poder hacer efectiva esta nueva configuración de formato para las fechas de la aplicación, deberemos cambiarla en la configuración cultural del programa. Esto lo conseguiremos obteniendo, de la hebra actual en ejecución, su objeto CultureInfo, y a su propiedad DateTimeFormat asignaremos el objeto DateTimeFormatInfo que hemos creado anteriormente. Finalizaremos la operación volviendo a asignar el objeto CultureInfo a la hebra principal en ejecución, y a partir de ese momento, el nuevo patrón de formato será aplicado en todos aquellos puntos de nuestro código en los que utilicemos DateTime.ToString("F").

El código a utilizar sería el siguiente:

Imports System.Globalization
Imports System.Threading
'....
'....
' obtener la fecha
Dim dtFecha As DateTime = DateTime.Now

' crear un objeto para modificar la configuración
' de formato completo para las fechas
Dim oDateTimeFormatInfo As DateTimeFormatInfo = New DateTimeFormatInfo()

' asignar la cadena de formato y un array con los nombres de los meses
oDateTimeFormatInfo.FullDateTimePattern = "\h\ora HH:mm \fec\ha MMMM/dd/yyyy"
oDateTimeFormatInfo.MonthNames = New String() {"Enero", _
    "Febrero", _
    "Marzo", _
    "Abril", _
    "Mayo", _
    "Junio", _
    "Julio", _
    "Agosto", _
    "Septiembre", _
    "Octubre", _
    "Noviembre", _
    "Diciembre", _
    ""}

' obtener la información cultural actual del sistema
' a partir de la hebra de ejecución en curso
Dim oCultureInfo = New CultureInfo(Thread.CurrentThread.CurrentCulture.LCID)

' asignar la nueva configuración de formato de fecha-hora
' al objeto que contiene la información cultural,
' estableciendo dicha configuración sobre la hebra
' principal de ejecución
oCultureInfo.DateTimeFormat = oDateTimeFormatInfo
Thread.CurrentThread.CurrentCulture = oCultureInfo

' mostrar la fecha formateada con el patrón de fecha completa
MessageBox.Show(dtFecha.ToString("F"))

Y esto sería todo, espero que os resulte de ayuda. 8-D

Un saludo

Publicado por Luis Miguel Blanco | 1 comment(s)
Archivado en:

¡Pues al final me embarqué en la aventura del blog!, así que a partir de ahora intentaré contribuir con algunas aportaciones a esta gran base de conocimiento, que día a día, toda la estupenda gente que colabora en Geeks.ms, convierte en un formidable recurso para la comunidad de desarrolladores. Es todo un placer y un honor estar por aquí.

Quería expresar mi profundo agradecimiento a Isaac Fernández por sus continuos ánimos para crear este blog, así como a Jorge Serrano y Rodrigo Corral, compañeros de andanzas literarias en dotNetMania, por toda su ayuda a la hora de dar mis primeros pasos en la "dimensión blog" 8-). Muchas gracias a todos, sois una gente extraordinaria ;-).

Y antes de entrar en "harina técnica". ¿Por qué "El aprendiz de brujo" como título del blog?. Supongo que a todos los que habitualmente trabajamos en el desarrollo de software, en más de una ocasión nos hemos encontrado ante situaciones en las que el código de la aplicación parece que adquiere vida propia, y se comporta de modo totalmente descontrolado, al igual que sucedía en la leyenda popular sobre la que Goethe basó su balada homónima, donde el joven aprendiz Trylbi, se ve inmerso en un terrible caos al utilizar los hechizos mágicos de su maestro en brujería.

Pues bien, desde este blog aportaremos nuestros pequeños hechizos de software, con la intención de contrarrestar las malignas fuerzas de la magia negra de los bugs, esperando que resulten de utilidad a todo aquel que quiera darse un paseo por nuestro libro de sortilegios 8-).

Un saludo a todos.
Luismi