Blog alskare

.Net y lo que surja

Archive for the ‘Controles’ Category

C# Mantener la configuración de tamaño y posición de los formularios

Posted by alskare en 15/09/2009

No sería la primera vez que los clientes piden que, en los formularios WinForms, se guarde tanto la posición como el tamaño del formulario, incluso después de cerrar la aplicación. Bajo esta premisa, me inicio en un nuevo post que tiene como finalidad, precisamente esto, almacenar la información de tamaño del formulario y recuperarla cada vez que se abra de nuevo el mismo formulario.

Framework.Net nos ofrece una serie de posibilidades amplias para poder guardar información en el área de trabajo del usuario. Una de estas opciones es el namespace System.XML, el cual nos permite poder trabajar con ficheros XML, tanto a nivel de lectura como de escritura. Aprovechando estas características, se me ocurre la creación de un fichero en el que poder almacenar los datos de tamaño y posición del formulario. Ahora bien, el problema de una aplicación es la cantidad de formularios existentes, así que empiezo a pensar en un fichero con una estructura similar a la siguiente:

<?xml version="1.0" encoding="utf-8"?>
<Configuracion>
    <NombreForm1>
        <WindowState>ValorWindowStateForm1</WindowState>
        <Top>ValorTopForm1</Top>
        <Left>ValorTopForm1</Left>
        <Width>ValorTopForm1</Width>
        <Height>ValorTopForm1</Height>
    </NombreForm1>
    <NombreForm2>
        <WindowState>ValorWindowStateForm2</WindowState>
        <Top>ValorTopForm2</Top>
        <Left>ValorTopForm2</Left>
        <Width>ValorTopForm2</Width>
        <Height>ValorTopForm2</Height>
    </NombreForm2>
</Configuracion>

De esta manera, si los datos de cada formulario representa un nodo dentro del archivo XML, puede resultar más o menos sencilla la adaptación de la librería a cualquier otro valor.

Una vez está decidido cuál será el medio de almacenamiento, cabe comenzar a pensar en cómo se realizarán las operaciones de lectura y escritura sobre el fichero XML, así que, recordando las clases de Programación Orientada a Objeto, se me ocurre la idea de crear un formulario base del que heredarán todos aquellos formularios que necesiten almacenar dicha información.

Manos a la obra. Comenzamos creando un nuevo proyecto que será una Biblioteca de controles de WindowsForms, que contendrá un formulario denominado FormularioBase. Éste será un formulario estándar en el que primarán dos cosas:

  1. En el evento FormClosing se almacenarán los datos que interesen en el fichero de configuración
  2. En el evento Load se recuperarán, en caso de existir previamente, los datos de configuración y se aplicarán al formulario

Así, una vez creado el campo que contendrá el fichero de configuración,

private string ficheroXML = Path.Combine(Application.StartupPath, "ConfForm.xml");

nos ponemos a implementar el proceso de almacenamiento de los datos:

/// <summary>
 /// Almacena la información del formulario actual en fichero XML
 /// </summary>
 private void GuardaConfiguracionForm()
 {
     string nombreForm = this.Name;
     XmlNode nodoPrincipal;

     try
     {
         // Intancia del documento XML que permitirá guardar la configuración
         XmlDocument doc = new XmlDocument();

         // Se comprueba que exista el fichero
         if (!File.Exists(ficheroXML))
         {
             // No existe el fichero. Se añaden los nodos iniciales
             doc.AppendChild(doc.CreateComment("Configuración de formularios - älskare, Sep/09"));

             // Creación del nodo principal
             nodoPrincipal = doc.CreateNode(XmlNodeType.Element, "Configuracion", null);

             // Se añade el nodo principal al documento
             doc.AppendChild(nodoPrincipal);
         }
         else
         {
             // El fichero existe previamente. Se recupera la información
             doc.Load(ficheroXML);

             // Lectura del nodo principal
             nodoPrincipal = doc.SelectSingleNode("Configuracion");
         }

         // Se comprueba si existe un nodo con el nombre del form actual
         XmlNode nodoFormActual = nodoPrincipal.SelectSingleNode(nombreForm);
         if (nodoFormActual == null)
             nodoFormActual =
                   nodoPrincipal.AppendChild(
                     doc.CreateNode(XmlNodeType.Element, nombreForm, null));

         // Almacenamiento de los valores
         NodoValor(doc, nodoFormActual, "WindowState", this.WindowState);
         NodoValor(doc, nodoFormActual, "Top", this.Top);
         NodoValor(doc, nodoFormActual, "Left", this.Left);
         NodoValor(doc, nodoFormActual, "Width", this.Width);
         NodoValor(doc, nodoFormActual, "Height", this.Height);


         // Se guarda el fichero de configuración en disco
         XmlTextWriter tw = new XmlTextWriter(ficheroXML, Encoding.UTF8);
         tw.Indentation = 4;
         tw.IndentChar = " "[0];
         tw.Formatting = Formatting.Indented;

         doc.Save(tw);
         doc = null;

         tw.Flush();
         tw.Close();
     }
     catch { }
 }

 /// <summary>
 /// Crea un nuevo par clave-valor en el nodo actual en caso de que no exista
 /// <returns></returns>
 XmlNode NodoValor(XmlDocument doc, XmlNode nodoPadre, string Clave, Object Valor)
 {
     XmlNode nodoActual = nodoPadre.SelectSingleNode(Clave);
     if (nodoActual == null)
         nodoActual = nodoPadre.AppendChild(doc.CreateNode(XmlNodeType.Element, Clave, null));
     nodoActual.InnerText = Valor.ToString();
     return nodoActual;
 }

 

Una vez realizado el proceso de escritura del fichero, falta poder recuperarlo para aplicar los cambios en el formulario cuando se abra.

/// <summary>
/// Realiza una lectura del fichero de configuración y aplica los cambios al form
/// </summary>
private void LecturaConfiguracionForm()
{
    // No se controlan errores. En caso de existir cualquier error
    // no se modifica nada en el formulario actual
    try
    {
        string nombreForm = this.Name;
        XmlDocument doc = new XmlDocument();
        doc.Load(ficheroXML);

        // Lectura del nodo principal
        XmlNode nodoPrincipal = doc.SelectSingleNode("Configuracion");

        // Lectura del nodo del formulario actual
        XmlNode nodoActual = nodoPrincipal.SelectSingleNode(nombreForm);

        // Lectura del modo de presentación de la ventana
        this.WindowState = (FormWindowState)Enum.Parse(typeof(FormWindowState), GetValor(nodoActual, "WindowState").ToString(), false);

        if (this.WindowState == FormWindowState.Normal)
        {
            this.Top = Convert.ToInt32(GetValor(nodoActual, "Top"));
            this.Left = Convert.ToInt32(GetValor(nodoActual, "Left"));
            this.Width = Convert.ToInt32(GetValor(nodoActual, "Width"));
            this.Height = Convert.ToInt32(GetValor(nodoActual, "Height"));
        }
    }
    catch { }
}

/// <summary>
/// Obtiene el valor de la clave
/// </summary>
private object GetValor(XmlNode nodoPadre, string clave)
{
    XmlNode valor = nodoPadre.SelectSingleNode(clave);
    return valor.LastChild.Value;
}

Implementadas tanto la escritura como la lectura de los valores, quedará aplicar dichos cambios al formulario actual. En esta parte, debo reconocer que no he conseguido eliminar un efecto un tanto raro que se da al abrir el formulario, sobre todo, si el formulario a mostrar está contenido en otro en modo MDI. Seguiremos trabajando sobre el tema para ver si consigo eliminar dicho efecto antiestético.

public FormularioBase()
{
    this.SuspendLayout();
    InitializeComponent();
    this.ResumeLayout();

    this.Load += new EventHandler(FormularioBase_Load);
    this.FormClosing += new FormClosingEventHandler(FormularioBase_FormClosing);
}

void FormularioBase_Load(object sender, EventArgs e)
{
    LecturaConfiguracionForm();
}
void FormularioBase_FormClosing(object sender, FormClosingEventArgs e)
{
    GuardaConfiguracionForm();
}

Bueno, pues ya está. A partir de ahora, cada vez que cree un nuevo WinForms, tan sólo añadiré una referencia a la librería en la que tengo este formulario base y, por supuesto, haré que cada formulario herede de FormularioBase.

public partial class Form2 : jnSoftware.Utiles.FormularioBase 
    {
        public Form2()
        {
            InitializeComponent();
        }
    }

 

Si le interesa a alguien, dejo enlace en el que se puede descargar un ejemplo de uso: descargar.

Anuncios

Posted in C#, Controles, WinForms | 2 Comments »

Personalizar el HTMLEditor de AjaxControlToolkit

Posted by alskare en 10/09/2009

Primeros pasos

Una de las novedades que nos ha traído el Ajax Control Toolkit (May 2009 Release) ha sido un editor HTML para nuestras páginas la mar de práctico. Hasta el momento, cada vez que he tenido que hacer uso de algún control similar con la finalidad de que el usuario pueda escribir negritas o subrayados, siempre he tenido que echar mano de algún componente externo. Debo reconocer que alguno de estos controles han sido realmente buenos y han cumplido a la perfección el objetivo. No obstante, al hacer uso de componentes de varios desarrolladores, llega un momento que el tamaño de la aplicación crece y, sobre todo, cualquier actualización se eterniza. Todo esto, sin contar con que cada control suele incluir sistemas propios de configuración (no será la primera vez que pierdo horas buscando dónde se indica un path para, por ejemplo, añadir una colección de botones propia).

Al ver por vez primera el HTMLEditor debo reconocer que me entusiasmé, puesto que veía la gran utilidad que tenía tener englobado el control con el resto de extensores que suelo usar a menudo. Propiedades y métodos más o menos conocidos, personalización de opciones…todo con una misma “filosofía”!!!!

Este entusiasmo inicial comenzó a decaer nada más empezar a utilizarlo (igual es que nos contagiamos de los clientes y cada día nos volvemos más exigentes). Y, por supuesto, nada más empezar a utilizarlo significa hacer uso de una estructura mínima para poder hacer uso del mismo

Estructura básica para hacer uso del HTMLEditor

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit.HTMLEditor"
    TagPrefix="AjaxCT" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>HTML Editor - Pruebas iniciales</title>
    <style type="text/css" media="all">
        * {
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="sm" runat="server" />
    <div>
        <h1>HTML Editor</h1>
        <AjaxCT:Editor ID="MiEditor" runat="server"  />
    </div>
    </form>
</body>
</html>

Con este código conseguimos añadir un control HTMLEditor en una de nuestras páginas. Apreciamos que no se ha cambiado un ápice de la “estructura original”, es decir, que el resultado que obtendremos también será el de una estructura básica:

image

Primeros pasos y… Primeros problemas

Una vez pasada la euforia inicial, cuando empezamos a aplicar el control en algún tipo de aplicación, comenzamos a apreciar que, en ocasiones, un control tan “extenso” puede llegar a ser demasiado para situaciones concretas. Por ejemplo, en ocasiones he contado con un espacio ínfimo para crear un control de este estilo y, en estos casos, hay que empezar a apurar botones (otra situación podría ser la de eliminar el tamaño de letra para evitar que los usuarios creen mensajes en los foros al estilo HOIGAN).

Así, de buenas a primeras, me marco dos objetivos iniciales:

  1. Aprender a personalizar el número de botones que aparecerán en las barras de herramientas del control
  2. Aprender a mostrar/ocultar estas barras de herramientas en tiempo de diseño/ejecución.

Primeras soluciones

Mirando un poco el explorador de objetos, veo que la clase AjaxControlToolkit.HTMLEditor.Editor contiene un par de métodos que me llaman la atención:

  • FillTopToolbar
  • FillBottonToolbar.

Así, en el ejemplo siguiente vamos a crear un editor que incluya, exclusivamente, los botones de formato estándar Negrita, Cursiva y Subrayado. Por tanto, empezaremos creando una nueva clase en la carpeta App_Code, la cual, heredará directamente del editor original.

using AjaxControlToolkit.HTMLEditor;

namespace jnSoftware.WebControls
{
    public class jnHTMLEditor : Editor
    {

        /// <summary>
        /// Método en el que se colocarán los elementos de la barra superior del control
        /// </summary>
        protected override void FillTopToolbar()
        {
            // Botón de negrita
            this.TopToolbar.Buttons.Add(
                new AjaxControlToolkit.HTMLEditor.ToolbarButton.Bold());
            // Botón de cursiva
            this.TopToolbar.Buttons.Add(
                new AjaxControlToolkit.HTMLEditor.ToolbarButton.Italic());
            // Botón de subrayado
            this.TopToolbar.Buttons.Add(
                new AjaxControlToolkit.HTMLEditor.ToolbarButton.Underline());
        }


        /// <summary>
        /// Método en el que se colocarán los elementos de la barra inferior del control
        /// </summary>
        protected override void FillBottomToolbar()
        {
            // Vista Diseño
            this.BottomToolbar.Buttons.Add( 
                new AjaxControlToolkit.HTMLEditor.ToolbarButton.DesignMode());
            // Vista Previa
            this.BottomToolbar.Buttons.Add(
                new AjaxControlToolkit.HTMLEditor.ToolbarButton.PreviewMode());

            // base.FillBottomToolbar();
        }
    }
}

A partir de aquí, ya vemos que podemos empezar a personalizar la barra de herramientas, tanto superior, como inferior. De hecho, gracias al IDE de VisualStudio es realmente sencillo y cómodo añadir el resto de botones deseados.

image

 

Agregando propiedades

En alguna que otra ocasión, he hecho uso de los editores de texto mostrando u ocultando las barras de herramientas, dependiendo del contexto. Así, como lo necesito para el editor, lo que hago es crear una propiedad que permita mostrar u ocultar las barras de herramientas:

using AjaxControlToolkit.HTMLEditor;

namespace jnSoftware.WebControls
{
    public class jnHTMLEditor : Editor
    {

        /// <summary>
        /// Muestra u oculta la barra de herramientas superior
        /// </summary>
        public bool ShowTopToolBar
        {
            get { return showTopToolBar; }
            set { showTopToolBar = value; }
        }
        private bool showTopToolBar = true ;


        /// <summary>
        /// Muestra u oculta la barra de botones inferior
        /// </summary>
        public bool ShowBottonToolbar
        {
            get { return showBottomToolbar; }
            set { showBottomToolbar = value; }
        }
        private bool showBottomToolbar = true;


        /// <summary>
        /// Método en el que se colocarán los elementos de la barra superior del control
        /// </summary>
        protected override void FillTopToolbar()
        {
            if (showTopToolBar)
            {
                // Botón de negrita
                this.TopToolbar.Buttons.Add(
                    new AjaxControlToolkit.HTMLEditor.ToolbarButton.Bold());
                // Botón de cursiva
                this.TopToolbar.Buttons.Add(
                    new AjaxControlToolkit.HTMLEditor.ToolbarButton.Italic());
                // Botón de subrayado
                this.TopToolbar.Buttons.Add(
                    new AjaxControlToolkit.HTMLEditor.ToolbarButton.Underline());
            }
        }

        /// <summary>
        /// Método en el que se colocarán los elementos de la barra inferior del control
        /// </summary>
        protected override void FillBottomToolbar()
        {
            if (ShowBottonToolbar)
            {
                // Vista Diseño
                this.BottomToolbar.Buttons.Add(
                    new AjaxControlToolkit.HTMLEditor.ToolbarButton.DesignMode());
                // Vista Previa
                this.BottomToolbar.Buttons.Add(
                    new AjaxControlToolkit.HTMLEditor.ToolbarButton.PreviewMode());
            }
        }
    }
}

Una vez creada la clase, bastará con hacer alguna pequeña modificación en el fichero .ASPX para que se muestre el control creado:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="jnSoftware.WebControls" TagPrefix="jnControles" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>HTML Editor - Pruebas iniciales</title>
    <style type="text/css" media="all">
        * {
            font-family: Arial, Helvetica, sans-serif;
        }
    </style>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="sm" runat="server" />
    <div>
        <h1>HTML Editor</h1>
        <jnControles:jnHTMLEditor ID="MiEditor" runat="server"  />
    </div> 
    </form>
</body>
</html>

 

image

Posted in ASP.Net, C#, Controles | 4 Comments »

WinForms. Mostrar progreso con ProgressBar

Posted by alskare en 13/01/2009

Uno de los atractivos de WinForms es que, cuando existe un procedimiento que tarda un rato en ejecutarse, se le puede ir mostrando al usuario un porcentaje aproximado de la duración que seguirá el sistema con el proceso. El control ProgressBar es una herramienta visual atractiva para realizar este tipo de acciones.

El uso de un control ProgressBar realmente es bien sencillo, puesto que tan sólo se trata de modificar un par de propiedades: Maximum, donde indicamos el valor entero máximo que admitirá el control y Value, que representará el progreso actual. Hasta aquí, todo es más o menos normal y sencillo. Un ejemplo de uso podría ser, aproximadamente el siguiente:

barraProgreso.Maximum = 100
For i As Integer = 0 To 100
    System.Threading.Thread.Sleep(500)
    barraProgreso.Value = i
Next

Uno de los problemas que surgen cuando se está ejecutando un proceso es que, si por ejemplo, el usuario minimiza la ventana de la aplicación o la coloca en segundo plano, nos encontramos con que, al situar la ventana de la aplicación de nuevo en un primer plano, la barra de progreso no sigue actualizándose, puesto que el proceso que se está ejecutando es el “cálculo” y no los “refrescos” de pantalla.

Una posible solución a este problema se encuentra en el procedimiento System.Windows.Forms.Application.DoEvents(), el cual, según la propia ayuda de .Net:

Al ejecutar un formulario Windows Forms, crea el nuevo formulario, que espera recibir eventos y controlarlos. Cada vez que el formulario controla un evento, procesa todo el código asociado al evento. Los demás eventos esperan en la cola. Mientras el código está controlando el evento, la aplicación no responde. Por ejemplo, la ventana no se vuelve a dibujar si se arrastra otra ventana encima de ella.

Viendo esto, tan sólo se trata de realizar una pequeña modificación en la rutina de presentación del progreso:

Private Sub PulsadoClick(ByVal sender As Object, ByVal e As EventArgs)
    barraProgreso.Maximum = 100
    For i As Integer = 0 To 100
        System.Threading.Thread.Sleep(500)
        barraProgreso.Value = i

        Application.DoEvents()
    Next
End Sub

Debo reconocer que antes de ver algo tan sencillo, como es habitual en mí, he pasado por hacer alguna cosa con el control BackgroundWorker, para que el proceso de presentación del progreso se ejecutase en segundo plano. No se me ha ocurrido mirar el rendimiento de ambos, pero es de suponer que la ejecución de tareas en segundo plano provoca un mayor consumo de recursos y, en consecuencia, un menor rendimiento.

 

Etiquetas de Technorati: ,

Posted in C#, Controles, WinForms | 3 Comments »