Wednesday, September 20, 2006

Wiki de C#, Marquee Tag

Hola a todos, me acabo de encontrar en los foros del Guille una wiki de C# y .NET, la estoy revisando ahora mismo, esta impresionante, la url es: http://es.csharp-online.net/

El plugin que describi en el anterior articulo no tiene efecto en Wordpress, les he preguntado porque cambian la etiqueta <marquee> por <p>, y me han dicho que si se ha sustituido es debido a cuestiones de seguridad, el resultado es el mismo con Flock.

Al menos en Blogspot, si que funciona!!!smile_party

Monday, September 18, 2006

Escribiendo Plugins para LiveWriter ( I )

Introducción:

En esta serie de articulos, comentare lo que estoy aprendiendo acerca de los plugins en Windows Live Writer (de ahora en adelante WLW). Si no tienes instalado este programa descargatelo junto con el SDK.

El objetivo de los plugin, es extender las funcionalidades de un programa, en el caso de WLW concretamente escribiremos plugins para añadir contenidos de manera más interactiva (sin tocar el html), y enriquecer el sistema de blogging, ejemplos de contenidos que podemos añadir como plugins:

  1. Tags de servicios como Del.icio.us, Technorati, etc.
  2. Imagenes online de sitios como Flickr.

Los plugin para WLW reciben el nombre Content Source Plugin, estos se clasifican en dos tipos:

Simple: Permite la inserción de codigo html dentro de un post, Para implementar un plugin de este tipo debemos heredar de la clase base ContentSource.

Smart: Al igual que el anterior permite la inserción de codigo html, pero provee un comportamiento más intuitivo: podemos definir un editor para el SideBar y la habilidad de tener dos representaciones del html (una para la edición y otra para la publicación), entre otras. Para implementar un plugin de este tipo debemos heredar de la clase base SmartContentSource.

Los dos tipos de plugin, pueden originar html desde un cuadro de dialogo, una URL, y datos de Live Clipboard . Para cada una de estas "especificaciones" se debe sobreescribir un metodo en la clase base. En esta entrega solo nos preocuparemos del tipo Simple y de proveer un cuadro de dialogo.

Supongo que ya te has bajado el WLW, así que creemos un nuevo proyecto del tipo Libreria de clases, y agregamos la referencia a la Api de WLW situada en el directorio de instalación, si no cambiaste la ruta de instalación por defecto de la instalación el path debe ser:

C:\Archivos de programa\Windows Live Writer\WindowsLive.Writer.Api.dll

Una vez establecida la referencia podemos crear nuestra clase que herede de ContentSource. Para marcar nuestra clase como plugin, debemos adornarla un atributo:

  • Atributo WriterPlugin

Atributo WriterPlugin, en el constructor de este atributo estamos obligados a indicarle la ID (en forma de GUID, si necesitas una entra aquí) y el Name que sera la cadena por la cual identificaremos al nuestro en la lista.Es posible proporcionar otros parametros al atributo: ImagePath, que indicara la ruta de una imagen incrustada en el assembly, esta imagen sera él icono que lucira nuestro plugin, para que la imagen pueda ser mostrada por el WLW debe ser 20x18 en formato png, tambien es importante que hallamos definido el plugin en el namespace por defecto del proyecto y en caso de haber puesto la imagen dentro de alguna carpeta como Resources,  debemos especificar Resources.imagen.png con el . para indicar la jerarquia;  Description sera una pequeña descripción (2 lineas) del plugin que podremos ver las preferencias del programa; Publish Url, la pagina del autor; HasEditableOptions, añade un botón para editar las opciones en el panel de plugins, ejecutando un metodo que debemos sobreescribir EditOptions.

 y para indicar que esta clase propociona un procedimiento de inserción para un nuevo  contenido mediante un cuadro de dialogo:

  • Atributo InsertableContentSourceAttribute

Especificaremos en el contructor, el texto que tendra en el menu, y si queremos el texto que tendra en la barra (sidebar), en caso de no especificar esta ultimo, se mostrara el texto que tiene del menu en su lugar.

El metodo que se ejecutara al hacer click en el menu o en la barra, sera el que CreateContent que debemos sobreescribir (en el caso de los cuadros de dialogo, como se dijo un poco antes).  Es importante tener claro que este metodo lo ejecuta el WLW, pasando a esta función parametros. Ahora bien, esta función recibe un primer parametro dialogOwner del tipo IWin32Windows, y un segundo del tipo string pasado por referencia newContent, sera este ultimo el que usaremos para devolver el html . Como resultado a esta función pasaremos un valor de la enumeración DialogResult.

El codigo:

 C#

	//el plugin 

using System.Windows.Forms;
using WindowsLive.Writer.Api;
[WriterPlugin("5C572939-449F-499C-8AD8-F17639458440", "MarqueeTag", HasEditableOptions = true,
ImagePath = "Resources.marquee.png", PublisherUrl =
"http://elblogdehoracio.blogspot.com",
Description = "Gives ... can be added")]
[InsertableContentSource("Insert A Marquee Tag", SidebarText = "Marquee Tag")]
public class MarqueeTag : ContentSource
{
public override DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent)
{
DialogResult result;
using (frmMarquee frmInsert = new frmMarquee())
{
result = frmInsert.ShowDialog();
if (result == DialogResult.OK)
{
MessageBox.Show(frmInsert.Handle.ToString() + dialogOwner.Handle.ToString());
newContent = frmInsert.Content;
}
}
return result;
}
}

//el formulario
using System.Windows.Forms;

namespace Typhoon
{
public partial class frmMarquee : Form
{
public string Content;
public frmMarquee()
{
InitializeComponent();
}

private void btnOK_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
char temp = (char)34;
this.Content = string.Format("<marquee direction={4}{1}{4} scrolldelay={4}{2}{4} loop={4}{3}{4}>{0}</marquee>", this.textBox1.Text, this.cmbDirection.Text, this.nudScrolldelay.Value, this.nudLoop.Value, temp);
Close();
}

private void btnCancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
Close();
}
}
}

VB.NET


//el plugin

Imports WindowsLive.Writer.Api
Imports System.Windows.Forms
<WriterPlugin("5C572939-449F-499C-8AD8-F17639458440", "MarqueeTag", _
Description:="Gives ... can be added", _
ImagePath:="Resources.marquee.png", _
PublisherUrl:="http://elblogdehoracio.blogspot.com")> _
<InsertableContentSource("Insert A Marquee Tag", SidebarText:="Marquee Tag")> _
Public Class MarqueeTag
Inherits ContentSource
Public Overrides Function CreateContent(ByVal dialogOwner As System.Windows.Forms.IWin32Window, ByRef newContent As String) As DialogResult
Dim result As DialogResult
Using frmInsert As New frmMarquee()
result = frmInsert.ShowDialog
If result = DialogResult.OK Then
newContent = frmInsert.Content
End If
End Using
Return result
End Function
End Class

//el formulario
Imports System.Windows.Forms
Public Class frmMarquee
Public Content As String
Private Sub btnOK_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnOK.Click
Me.DialogResult = Windows.Forms.DialogResult.OK
//'cuando ponemos "" insertamos un ", en c# desconozco la forma de hacer algo parecido por lo que
//'tuve que recurrir a (char)34
Me.Content = String.Format("<marquee direction=""{1}"" scrolldelay=""{2}"" loop=""{3}"">{0}</marquee>", Me.textBox1.Text, Me.cmbDirection.Text, Me.nudScrolldelay.Value, Me.nudLoop.Value)
End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
Me.DialogResult = Windows.Forms.DialogResult.Cancel
Close()
End Sub
End Class

 El codigo anterior habla por si solo, utilizamos using para crear una instancia, almacenamos el resultado, de ser OK el resultado, pasamos el texto, liberamos el recurso (la instancia) y devolvemos el resulado. El aspecto del form seria el siguiente:



Para que la depuración sea más comoda, añadan estos comandos en los eventos post compilación en las propiedades del proyecto:



  • XCOPY /D /Y /R "$(TargetPath)" "C:\Archivos de programa\Windows Live Writer\Plugins\"
  • C:\Archivos de Programa\Windows Live Writer\WindowsLiveWriter.exe

Conclusiones:


Como vemos, hacer  un plugin para WLW no es dificil. Con pocas lineas de codigo podemos hacer un sencillo plugin como el anterior que nos permite insertar un texto en movimiento, y solo tenemos que añadir la etiqueta marquee, que tendra en efecto en cualquier etiqueta dentro de su definición, permitiendo insertar el resultado de cualquier plugin (haz doble click en la marquesina y comenzaras a editarla), ejemplo:



Me estoy moviendo muy rapido smile_speedy


En las proximas entregas, veremos como hacer plugins más complejos, si creas un plugin puedes enviarlo a las paginas:


http://windowswriterplugins.wordpress.com/


http://wlwplugins.com/ en esta hay un foro, postealo alli


Bibliografia:


http://tech.pederborgpoulsen.dk/2006/09/05/creating-a-plugin-for-windows-live-writer-using-vbnet/


http://nayyeri.net/archive/2006/08/15/Write-a-Windows-Live-Writer-plugin-using-C_2300_.aspx


salu2smile_sarcastic

Wednesday, September 13, 2006

Copiar ficheros utilizando un Progressbar

Comenzando...

En este articulo crearemos una aplicación que mostrara el proceso de copiado de un fichero, tambien utilizaremos la programación multihebra para mejorar la interacción con la interfaz y el uso de cronometros para comparar el rendimiento de la aplicación.

Introducción:

Si haz experimentado con el espacio de nombres System.IO lo más seguro es que te hayas aventurado con la clase File, con ella puedes manipular los ficheros: moverlos,  renombrarlos, etc; pero tiene una pega, y es que no podemos mostrar el progreso a la hora de copiar, algo que no tiene mucha importancia (ya que lo importante es que se copie el fichero) pero que produce ese efecto "mágico" en nuestra aplicación, esto no tiene tamaña complicación, pero antes de escribir ninguna linea de codigo te explicare las bases de este procedimiento:

Ya sabemos que no se puede utilizar File para copiar con barra de progreso, pero en el espacio System.IO hay una clase llamada FileStream, que expone un Stream (Flujo de Bytes) alrededor de un fichero, permite un acceso aleatorio cuando se trata de archivos en disco y soporta llamadas Sincrónicas y Asincrónicas. Lo importante de FileStream es que permite la lectura/escritura desde/sobre un buffer, que no es más que una matriz de Bytes (toda las operaciones sobre ficheros son en base al tipo de dato byte).

La función Read:

Read ( array As Byte(), offset As Integer, count As Integer ) As Integer

Esta función intenta leer desde el flujo de bytes un número de bytes indicado por Count, los cuales escribira desde offset en array, o sea que se escribira en array desde offset hasta offset + count -1. Cuando digo intenta, me refiero a que es posible que lleguemos al final del fichero y no haya mas bytes para leer, en este caso lógicamente se volcaran solo los bytes leidos; el número de bytes leidos sera el resultado de la función, si devuelve cero significa que hemos llegado al fin del fichero. Quiero señalar que esta función utiliza un parametro para devolver resultados, algo muy común en la API de Win32 pero que vemos poco en la API de .NET.


El procedimiento Write:

Write ( array As Byte(), offset As Integer, count As Integer )

Escribe el número de bytes especificado en count, desde el byte en la posición definida por offset, o sea, escribe los bytes desde offset hasta count.


El algoritmo:

Dim result As Integer 
Dim buffer(buffersize) As Byte
Do
result = readfile.Read(buffer, 0, buffersize + 1)
writefile.Write(buffer, 0, result)
Loop Until result = 0

El algoritmo tiene como base un ciclo que se detiene cuando la función read, devuelve cero,  podemos implementar otros bucles, no solo un do loop-until. Para aprovechar todo el buffer hacemos que al leer count sea igual a buffersize +1 debido a que se escribe de offset hasta offset + count -1. En el procedimiento Write, escribimos desde 0 hasta result, que es el número de elementos leidos.

Mostrando el proceso:

Para mostrar el proceso en un progressbar, debemos hacer lo siguiente:


  1. La propiedad Maximun debe tomar el valor del número de bytes del fichero de origen, esto lo logramos mediante la propiedad Lenght del FileStream.
  2. En cada iteración del bucle, debemos aumentar el valor de la propiedad Value en la cantidad devuelta por Read, o sea result.

Quedaría algo así:

Me.pgbcopy.Maximum = CInt(readfile.Length)
Me.pgbcopy.Value = 0
Dim result As Integer
Dim buffer(buffersize) As Byte
Do
result = readfile.Read(buffer, 0, buffersize + 1)
writefile.Write(buffer, 0, result)
Me.pgbcopy.Value += result
Loop Until result = 0

Experimentando con el BufferSize:

De seguro ya habras pensado en probar si aumentando el tamaño del buffer aumentara la rapidez de la copia, esto no es del todo cierto, ya que si bien no es lo mismo utilizar un buffer de 2 bytes que uno de 4096 hay otros factores que influyen: si estan en el mismo disco, si es un disco extraible, etc. Para probar como se comporta el buffersize, crearemos una aplicación en la cual tendremos un log de cada proceso de copia; como la que muestra la figura:


  Para esto debes insertar:



























Control

Nombre

Características Especiales
ButtonbtnCopy-
ListviewltvLogView=Details
ProgressBarpgbCopy-
FolderBrowserDialogfbd-
OpenFileDialogofd

-


¿Como implementar cronómetros?

Para poder comparar la velocidad de copiado debemos hacer uso de cronómetros,  el framework 2.0 trae la clase StopWatch, alojada en System.Diagnostics, esta clase tiene un funcionamiento muy sencillo:

Dim Crono As New Stopwatch 'Creamos una nueva instancia
Crono.Reset() 'Borramos el tiempo transcurrido
Crono.Start() 'Iniciamos el cronómetro
Crono.Stop() 'Detenemos el cronómetro


 Para obtener el tiempo transcurrido, utilizamos la propiedad Elapsed que devuelve un objeto del tipo TimeSpan

Procedimiento Copy:

 Ya tenemos todas las piezas para armar el procedimiento principal, el procedimiento Copy:

Public Sub Copy() 
Dim Crono As New Stopwatch
Try
Crono.Start()
Dim readfile As New FileStream(sourcepath, IO.FileMode.Open)
Dim writefile As New FileStream(destpath, IO.FileMode.CreateNew)
Me.pgbcopy.Maximum = CInt(readfile.Length)
Me.pgbcopy.Value = 0
Dim result As Integer
Dim buffer(buffersize) As Byte
Do
result = readfile.Read(buffer, 0, buffersize + 1)
writefile.Write(buffer, 0, result)
Me.pgbcopy.Value += result
Loop Until result = 0
Crono.Stop()
Dim item As New ListViewItem
With item
.Text = sourcepath
.ImageIndex = 0
.SubItems.Add(destpath)
.SubItems.Add(readfile.Length.ToString)
.SubItems.Add(CStr(buffersize))
.SubItems.Add(Crono.Elapsed.ToString)
End With
readfile.Dispose()
writefile.Dispose()
Me.pgbcopy.Value = 0
Me.ltvLog.Items.Add(item)
Me.btnCopy.Enabled = True
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub

Lo primero que hacemos es insertar un bloque Try, ya que es muy común (poco espacio en disco, ya existe el archivo, etc) encontrarnos con excepciones a la hora de trabajar con ficheros. Luego de iniciar el cronometro, creamos los objetos FileStream, el contructor que utilizamos para esto recibe el path de cada fichero y el método de apertura del fichero. Una vez copiado el fichero, llega el momento del log, creamos un nuevo listviewitem y le añadimos la información. Justo despues de esto, liberamos los recursos de los FileStream, de lo contrario no podriamos interactuar con los ficheros porque estarian bloqueados.

 ¿Por qué es necesaria la programación multihebra?


Al principio mencione que implementariamos algo de programación multihebra, la razón es muy sencilla, si ejecutamos el procedimiento Copy en la misma hebra que el formulario experimentariamos el bloqueo de la interfaz de este último (se congela le ventana). Para esto ejecutamos el procedimiento en otro hilo, lo iniciamos, y desde ese hilo manipularemos los controles del formulario, a continuación el codigo para el evento click del botón, donde iniciamos el hilo:

Private Sub btnCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCopy.Click 
If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
If fbd.ShowDialog = Windows.Forms.DialogResult.OK Then
buffersize = CInt(InputBox( "Insert the Buffer Size for this copy process" , "Buffer Size" , "128" ))
sourcepath = ofd.FileName
destpath = fbd.SelectedPath & "\" & Path.GetFileName(sourcepath)
If buffersize > 0 Then
CopyThread = New Thread(AddressOf Copy) 'Creamos un nuevo hilo, pasandole al contructor la referencia del procedimiento
Me.btnCopy.Enabled = False 'Prevenimos que no se creen más hilos
CopyThread.Start() 'Ejecutamos Copy en otro hilo
End If
End If
End If
End Sub

Debo señalar que los metodos pasados al constructor de la clase Thread (System.Threading) no pueden tener argumentos, pero esto no es impedimento para utilizar parametros, ya que los pasamos indirectamente con las variables del form:

Dim buffersize As Integer
Dim sourcepath As String
Dim destpath As String

¡Un error debido a los hilos!

Si iniciaramos un debugging del proyecto, recibiriamos una excepción: System.InvalidOperationException, no hay porque alarmarse, esta excepción es debido a que hemos intentado manipular un control desde un hilo distinto al de su creación, esto no es algo "seguro", pero en nuestro caso no hay nada de malo con ello además de que esta excepción sucede siempre cuado se hace un debug y rara vez en tiempo de ejecución, de todas formas para evitar las molestias que esto pueda ocasionar, en el evento load escribiremos esto:

Private Sub frmCopy_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 
CheckForIllegalCrossThreadCalls = False 'Evitamos que se comprueba la seguridad de las llamadas entre los hilos
End Sub

En caso de que te se demore mucho el proceso de copiado...

Si intentas copiar un fichero de video con un buffer de 2 bytes, y la espera se te hace demasiada, lo más seguro es que cierres el formualrio, pero Ojo (0_o) esto no finaliza la aplicación (puedes comprobarlo en el debugging, cierra la aplicación mientras estes copiando, verás como el IDE sigue en modo Debug) y bloquea el acceso a los ficheros. Para que una aplicación se finalice totalmente todos sus hilos deben terminarse, este incoveniente lo evitaremos en el evento formClosing, que de dispara antes de que se cierre el formulario, antes de terminar el hilo, comprobamos que existe mediante una condición cortocircuitada:

Private Sub frmCopy_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing 
If Me.CopyThread IsNot Nothing AndAlso Me.CopyThread.ThreadState = ThreadState.Running Then
Me.CopyThread.Abort() 'Termina el Thread
End If
End Sub

Conclusiones:

Espero que hayan aprendido con este articulo de todo un poco, hilos, cronometros, ficheros. Para finalizar les dejo una captura del programa, despues de copiar varios capitulos de la seria Friends con diferentes tamaños de Buffer,

 

Finalizando...


Puedes bajarte el proyecto de aquí, y si quieres deja tu comentario del articulo ;-)

Tuesday, September 12, 2006

Manipular el Ratón a nuestro antojo

Bueno, en los foros del Guille me he encontre con un post que donde se preguntaba como mover el mouse desde codigo, intente hacerlo con MousePosition pero es de solo lectura, paso el tiempo y Ciper, lo respondió esta es la url del post,  y poco despues me tope con esta página de MSDN que ofrece una lista de las equivalencias de la Api de win32 y la Api de .NET, no podemos olvidar que muchas de las funciones de .NET hacen uso de P/Invoke para ejecutar codigo no manejado, la url de la pagina es esta, solo busquen la de los cursores,

Saturday, September 09, 2006

IronPython 1.0!!!

Ya ha sido liberada la versión 1.0 de IronPython, totalmente compatible con los namespaces de .Net y siendo aún Python. El proyecto es Shared Source, esta hospedado en CodePlex, algo así como el SourceForge de Microsoft.

http://www.codeplex.com/Release/ProjectReleases.as...

De más esta decir lo importante que es IronPython con respecto a generar codigo dinamicamente, ya que IronPython al igual Python es en si un interprete.

Sunday, September 03, 2006

Graficos de Pie 3D en GDI

Cuando instale el Visual Studio .NET 2005, descargue los ejemplos del portal del VB, entre ellos habia uno que dibujaba un pie en 2D mostrando las capacidades del disco duro, a la segunda vez de probar el programa decidi hacer mi propio componente (incluyendole 3D) y de esa forma aprender GDI+. Me inspire en el grafico que trae el Acronis Partition Expert, que luce mucho mejor que el de Windows. El resultado lo pueden ver aquíy las intrucciones aquí. En dotnetmania 09, Erich Buhler escribio un articulo de como implementar componentes de tipo Pie,Barras, donde explica como lograr el aspecto 3D, y tambien pueden visitar el articulo en Code Project aquí
donde se explica paso a paso, mediante geometria analit. como hacer un pie3d avanzado.

Espero les guste el mio, saludos ;-)