domingo, 12 de diciembre de 2010

De Notepad a Wordpad

En este pequeño post voy a continuar con el desarrollo de la aplicación notepad para .NET utilizando los demás ControlDialogs y añadiéndole más funcionalidades. Para ello he cambiado el control Textbox por un control RichTextbox, que además de las típicas funcionalidades del control Textbox añade muchas más propiedades que nos permitirán cambiar cosas como el color del texto, añadir imágenes,… y así nuestra aplicación se parecerá más a un procesador de textos tipo Word. A parte está el tema de la impresión que no es fácil, ya que aquí no se vale arrastrar el control al formulario, cambiar 3 o 4 propiedades y ya funciona. El tema es un poco complicado porque de lo que se trata es que el texto se imprima dentro del área de impresión y no se vaya por los márgenes, y a parte está el tema del color, que a ser posible no se imprima todo en un único color.
Si ya nos vale con que se nos vaya el texto por los márgenes y se imprima todo en negro, tan sencillo como añadir un control PrintDocument al formulario y en el evento Print_Page añadir el siguiente código:
Private Sub PDoc_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PDoc.PrintPage
e.Graphics.DrawString(RichTextBox.Text, RichTextBox.Font, Brushes.Black, e.MarginBounds.Left, RichTextBox.Font.GetHeight(e.Graphics))

End Sub
En el soporte técnico de Microsoft ofrecen una buena alternativa que es la de extender el control RichTextBox creando una clase llamada RichTextBoxPrintCtrl, que hereda de la clase RichTextBox y que tiene un método Print que nos permitirá imprimir el contenido del RichTextBox. En el proyecto que se adjunta está implementado de esta manera -y así me quedo tan ancho y con un problema menos- y en http://support.microsoft.com/kb/811401 está el código y más documentación.

A parte para rizar más el rizo quiero trabajar con un formulario MDI, es decir, un formulario padre con varios subformularios hijos en su interior. Esto complica el tema ya que estás trabajando con varios formularios hijos y hay que identificar el formulario hijo activo con el que estás trabajando.
Para ello añado al proyecto un formulario llamado Form1, al que le arrastro el control RichTextBoxPrintCtrl y que tendrá definidas las propiedades y los eventos necesarios que yo utilizaré cuando cree una instancia del formulario Form1. Para ello cada vez que se cree un nuevo formulario hijo llamaré al método CrearForm() que me devolverá un objeto Form1 y como vemos el valor de su propiedad texto será “Sin título-“ concatenado a un número que será sumarle 1 al valor almacenado en la propiedad P_ChildFormNumber.


Private Function crearForm() As Form1
' Crea una nueva instancia del formulario secundario
Dim ChildForm As New Form1
' Lo Convierte en un elemento secundario de este formulario MDI antes de mostrarlo.
ChildForm.MdiParent = Me

P_ChildFormNumber += 1
ChildForm.Text = "Sin título-" & childFormNumber.ToString()
Return ChildForm
End Function

A parte quiero añadir en el Formulario Form1 la funcionalidad de poder arrastrar un documento al formulario y que éste se me abra en el RickTextBox. Esto se consigue manejando los eventos DragEnter y DragDrop del control RickTextBox con código siguiente:
Private Sub richTextBox_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs) Handles RichTextBox.DragEnter
If (e.Data.GetDataPresent(DataFormats.Text)) Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.None
End If

End Sub

Private Sub richTextBox_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles RichTextBox.DragDrop
Dim i As Integer, s As String

' Obtener la posición inicial para pegar el texto.
i = CInt(RichTextBox.SelectionStart)
s = RichTextBox.Text.Substring(i)
RichTextBox.Text = RichTextBox.Text.Substring(0, i)

' Pegar el texto en el Richtextbox
RichTextBox.Text = RichTextBox.Text + _
e.Data.GetData(DataFormats.Text).ToString()
RichTextBox.Text = RichTextBox.Text + s

End Sub

El método richTextBox_DragEnter se llama cuando el usuario arrastra un documento al control RichTextBox y si el formato del documento es texto los datos se copiarán en el destino de colocación, el RickTextBox. Cuando se llama al método richTextBox_DragDrop es para pegar el contenido del documento en el control RichTextBox.


Otro tema es el programar que cuando se llame al Paint, se cree un archivo temporal y se abra con el Paint. Una ver el usuario cierre el Paint y guarde los cambios la imagen se añadirá al control RichTextBox. En la imagen se ve que cuando el usuario haga clik en Paint se ejecutará un código como el siguiente:

Dim rutaimagen As String = System.IO.Path.GetTempFileName()
Dim bitmap As New Bitmap(250, 250, Imaging.PixelFormat.Format32bppArgb)
Dim g As Graphics = Graphics.FromImage(bitmap)
Dim s As New SolidBrush(Color.White)
g.FillRectangle(s, 0, 0, 250, 250)
bitmap.Save(rutaimagen)

' Llamo al Paint con los argumentos puestos de esta forma porque sino en

' Windows XP no me reconoce los parámetros que contienen la ruta de la imagen
Dim paint As Process = Process.Start("mspaint.exe", """" & rutaimagen & """")

' Espero hasta que el usuario acabe
paint.WaitForExit()
' Anado la imagen al RichTexbox
If (Not Me.ActiveMdiChild Is Nothing) Then
Dim cajatexto As RichTextBox = CType(Me.ActiveMdiChild.ActiveControl, RichTextBox)
' Me aseguro de hacer un dispose con el End Using
Using imagen As Image = Image.FromFile(rutaimagen)
Clipboard.SetImage(imagen)
cajatexto.Paste()
Clipboard.Clear()
End Using
End If
' Elimino el fichero temporal creado
System.IO.File.Delete(rutaimagen)

Como siempre cuelgo el código por si alguien le mola el tema y está disponible aquí. Así con un sencillo “Copy-Paste” puedes adaptar este editor de texto profesional a varios tipos de aplicaciones y de formatos de archivo y aún más útil sería crearse una plantilla del proyecto. De esta manera tardarás menos en crear tus aplicaciones ya que para proyectos similares tendrás la estructura lista y no perderás el tiempo en diseñar una y otra vez todo desde 0.

Me queda por comentar lo que se refiere a localización y globalización, es decir, hacer que la interfaz de nuestra aplicación esté disponible en varios idiomas y se pueda cambiar de idioma de una forma muy sencilla. Localización y globalización lo explicaré con un sencillo vídeo donde demostraré como una misma aplicación puede tener el texto de los controles en varios idiomas (en el caso de la aplicación wordpad verás que están disponibles los idiomas castellano, catalán e inglés) sin necesidad de crear 3 formularios diferentes –cosas que a veces he visto en producción y para mantener eso te puedes volver loco-.