miércoles 25 de noviembre de 2009

ASP.NET – Resetear o limpiar modalpopup extender

modalpopup_clear Se habrán dado cuenta que cuando hacemos uso del modalpopup del ajaxcontrol toolkit, osea ingresamos valores en los controles contenidos en él, y luego de guardar la información y cerramos el modalpopup… al volver a abrir el modalpopup, éste mantiene los valores que escribimos en cada control, inclusive mantiene seleccionado el dropdownlist y los listboxes, tal como los habíamos seleccionado.

Esta conducta, muchas veces es ventajosa, porque nos ahorra el volver a escribir o seleccionar algunas opciones de los controles. Pero, qué pasa si queremos que cada vez que abrimos el modalpopup extender esté limpio otra vez?

Pues fácil, vamos a mostrar un pequeño ejemplo didáctico.

1.- Creamos un proyecto web ASP.NET y añadimos la referencia al ajaxcontrol toolkit.

2.- Creamos una página aspx llamada clear_modalpopup.aspx que contenga el siguiente código:

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<
html>
<
head>
<
title>xxx</title>
<
style type="text/css">
*
{
font-family: Trebuchet MS, Verdana;
}
.modalPopup
{
border: solid 5px red;
background-color: orange;
filter: alpha(opacity=70);
opacity: 0.7;
}
</style>
</
head>
<
body>
<
form id="form1" runat="server">
<
div>
<
asp:ScriptManager ID="ScriptManager1" runat="server" />
<
asp:LinkButton ID="LinkButton1" runat="server" Text="Añadir información" />
<
asp:Panel ID="Panel1" runat="server" Style="display: block" CssClass="modalPopup"
Height="250px" Width="400px">
<
table style="width: 100%">
<
tr>
<
td colspan="2">
<
strong>Datos del usuario</strong>
</
td>
</
tr>
<
tr>
<
td style="width: 10%">
Nombre:
</td>
<
td>
<
asp:TextBox ID="txtName" Width="90%" runat="server"></asp:TextBox>
</
td>
</
tr>
<
tr>
<
td>
Apellido:
</td>
<
td>
<
asp:TextBox ID="txtLastName" Width="90%" runat="server"></asp:TextBox>
</
td>
</
tr>
<
tr>
<
td>
Email:
</td>
<
td>
<
asp:TextBox ID="txtEmail" runat="server" Width="90%"></asp:TextBox>
<
asp:RequiredFieldValidator ID="rfvEmail" runat="server" ControlToValidate="txtEmail"
Display="Dynamic" ErrorMessage="*"></asp:RequiredFieldValidator>
</
td>
</
tr>
<
tr>
<
td>
Nacionalidad:
</td>
<
td>
<
asp:DropDownList ID="ddlCountry" runat="server" Width="90%">
<
asp:ListItem Value="0" Text="(Seleccione una opción)"></asp:ListItem>
<
asp:ListItem Value="1" Text="Española"></asp:ListItem>
<
asp:ListItem Value="2" Text="Mexicana"></asp:ListItem>
<
asp:ListItem Value="3" Text="Chilena"></asp:ListItem>
<
asp:ListItem Value="4" Text="Peruana"></asp:ListItem>
</
asp:DropDownList>
</
td>
</
tr>
<
tr>
<
td colspan="2" style="text-align: right; height: 100px; vertical-align: bottom">
<
asp:Button ID="btnSave" runat="server" Text="Guardar" />
</
td>
</
tr>
</
table>
</
asp:Panel>
<
ajaxToolkit:ModalPopupExtender ID="ModalPopupExtender1" runat="server" TargetControlID="LinkButton1"
PopupControlID="Panel1" OkControlID="btnSave" />
</
div>
</
form>

<
script type="text/javascript">
function
pageLoad() {
var ModalPopupExtender1 = $find("ModalPopupExtender1");
ModalPopupExtender1.add_hidden(Controls_Clear);
}
function Controls_Clear() {
var txtName = $get("txtName");
var txtLastName = $get("txtLastName");
var txtEmail = $get("txtEmail");
var ddl = $get("ddlCountry");
var rfvEmail = $get("rfvEmail");

txtName.value = "";
txtLastName.value = "";
txtEmail.value = "";
rfvEmail.style.display = "none";
ddl.selectedIndex = 0;
}
</script>

</
body>
</
html>

Y eso es todo, la parte interesante está en el pequeño código javascript, el cual es quien se encarga de resetear los valores tanto de los textboxes, los controles de validación y el dropdownlist.

Uds pueden extender este código para que funcione para la mayoría de controles ASP.NET.

;)

sábado 14 de noviembre de 2009

ASP.NET – Obtener resolucion de pantalla

irequiressesion Dejemos claro que si se quiere obtener la resolución de pantalla, hay que trabajar por el lado del cliente, una forma es usando javascript.

Hay diversidad de formas de obtener esta información, una de ellas es por ejemplo es hacer un redirect hacia otra página la cual a su vez debe hacer nuevamente redirect a la página original, pero adjuntando un par de parámetros GET, los cuales llevan la información del ancho y alto de pantalla recuperados via javascript… etc. Yo me resisto a usar esta técnica porque me parec inesperado que la página regrese con unos parámetros adicionales (ejemplo: pagina.aspx?width=1024&height=768)

Yo propongo otra forma, similar pero más sencilla. Empecemos.

Necesitamos un control de usuario (wucScreenResolution.ascx) y un controlador genérico (ScreenResolution.ashx).

1.- Vamos a crear el control de usuario llamado wucScreenResolution.ascx,

Este es todo el código que va en el .ascx lado:

<iframe id="iframeForScreenResolution" runat="server" width="0%" height="0%">
</
iframe>

Y este el código que va en el ascx.vb:

If IsNothing(Session("ScreenWidth")) Then
iframeForScreenResolution.Attributes.Add("onload", _
"location.href = 'ScreenResolution.ashx?w=' + " & _
"screen.width + '&h=' + screen.height + '&OriginalUrl=' + location.href")
End If

Ahora veamos el código completo que va en el archivo ScreenResolution.ashx:

Imports System.Web
Imports System.Web.Services

Public Class ScreenResolution
Implements IHttpHandler
Implements IRequiresSessionState

Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

Dim w As String = HttpContext.Current.Request.QueryString("w")
Dim h As String = HttpContext.Current.Request.QueryString("h")
Dim OriginalUrl As String = HttpContext.Current.Request.QueryString("OriginalUrl")

HttpContext.Current.Session("ScreenWidth") = w
HttpContext.Current.Session("ScreenHeight") = h


context.Response.Redirect(OriginalUrl)

End Sub

ReadOnly Property
IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property

End Class

Dense cuenta que he añadido Implements IRequiresSessionState … lo cual me permite trabajar con sesiones desde el archivo ScreenResolution.ashx.

Finalmente, arrastra el control de usuario en la página en la cual te interese saber la resolución de pantalla. <body>


<
formid="form1"runat="server">


<
uc1:wucScreenResolutionID="wucScreenResolution1"runat="server" />

</
form>

</
body>

De preferencia arrastra el control en la página con la que arranca la aplicación (login.aspx, default.aspx, …etc). Además, si te das cuenta… si la variable de sesión que guarda los valores del ancho y alto de pantalla ya se cargaron… no será necesario que se ejecute el código nuevamente.

Aquí muestro cómo usar el código:

If Not IsNothing(Session("ScreenWidth")) Then
Dim
ScreenWidth As Integer = Session("ScreenWidth")
Select Case ScreenWidth
Case 1024
Panel1.Width = Unit.Pixel(100)
Case 1152
Panel1.Width = Unit.Pixel(200)
Case 1280
Panel1.Width = Unit.Pixel(300)
Case 1440
Panel1.Width = Unit.Pixel(400)
Case Else
'more code
End Select
End If

Hay que resaltar que si se está usando autenticación por formularios, hay que agregar la siguiente entrada en el web.config:

<location path="ScreenResolution.ashx">
<
system.web>
<
authorization>
<
allow users="*"/>
</
authorization>
</
system.web>
</
location>
Y eso sería todo ;) 

sábado 7 de noviembre de 2009

ASP.NET – Crear textbox dinamicamente - II

Parte I, Parte II.

postbackurl Luego que terminé la primera parte de este post, me hicieron varias preguntas interesantes, como por ejemplo: cómo paso los valores de estos textboxes generados dinámicamente a otra página?

Bueno pues, este post se ha creado para responder esa inquietud.

Para la página en donde se crearán los textboxes necesito el sgte código aspx:

<form id="form1" runat="server">
<
asp:Button ID="Button1" runat="server" Text="Generar textboxes" />
<
br />
<
asp:PlaceHolder ID="phDinamicControls" runat="server"></asp:PlaceHolder>
<
hr />
<
asp:Button ID="Button2" runat="server" Text="Enviar datos a otra página" PostBackUrl="~/WebForm2.aspx" />
</
form>

Luego, en el código beside, ponemos este código:

Private Property QuantityDinamicControls() As Integer
Get
If
ViewState("Quantity") = Nothing Then ViewState("Quantity") = 0

Return DirectCast(ViewState("Quantity"), Integer)

End Get
Set
(ByVal value As Integer)
ViewState("Quantity") = value
End Set
End Property

Protected Sub
Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click

Me.QuantityDinamicControls += 1

End Sub

Protected Overloads Overrides Sub
CreateChildControls()

If Page.IsPostBack Then GenerateControls()

End Sub

Private Sub
GenerateControls()

Dim Quantity As Integer = 0
While Quantity <= Me.QuantityDinamicControls
Dim txt As New TextBox()
txt.Attributes.Add("style", "margin-bottom: 10px")
txt.ID = "txt" + (Quantity + 1).ToString()
txt.Text = "Algún texto va aquí..."

phDinamicControls.Controls.Add(txt)
phDinamicControls.Controls.Add(New LiteralControl("<br/>"))
Quantity += 1
End While

End Sub

El cual, es casi el mismo que el post anterior, (por favor lean el post anterior).

La diferencia con el post anterior?

::en el código aspx añadí un botón con la propiedad PostBackUrl = “~/WebForm2.aspx”, lo cual me permitirá enviar los datos hacia la otra página.

Ahora, vamos a mostrar el código que va en la página WebForm2.aspx … para ver cómo se recepcionan los valores de los textboxes:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Dim MyDynamicControls As New List(Of String)

For Each item As String In Request.Form.AllKeys
If item.Contains("txt") Then
MyDynamicControls.Add(Request.Params(item))
End If
Next

End Sub

Eso es todo, lo que hice fue crear una colección de strings, y allí guardo los datos que me interesan… en este caso los textboxes, por eso antes de guardarlos pregunto si comienzan con “txt”.

Simple no? ;)

martes 27 de octubre de 2009

ASP.NET – RadioButtonList con imagenes

radiobutton Hay que reconocer que una imagen siempre es mucho más representativa que una frase… y mucho más atractiva claro está.

Por ello, a veces las palabras son reemplazadas por bellas imágenes.

Imaginemos por ejemplo que tenemos un catálogo de productos y queremos elegir un producto de una lista (como la imagen de arriba).

Para ello necesito:

1.- un control ASP.NET RadioButtonList:

<form id="form1" runat="server">
<
asp:RadioButtonList ID="RadioButtonList1" runat="server">
</
asp:RadioButtonList>
</
form>

2.- y un poquito de código VB.NET:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack Then Return

Dim
c As Char = Html32TextWriter.DoubleQuoteChar
Dim js As String = "return this.parentNode.previousSibling.checked=true;"
Dim IMGs As New List(Of String)

IMGs = MyImages()
Dim i As Integer = 0
For Each img As String In IMGs
RadioButtonList1.Items.Add(New ListItem(String.Format("<a href={0}javascript:;{0} onclick={0}{1}{0}><img src={0}{2}{0} style={0}border: none{0} /></a>", c, js, img), i))
i += 1
Next img

End Sub
    Private Function MyImages() As List(Of String)
Dim IMGs As New List(Of String)

IMGs.Add("images/excel.png")
IMGs.Add("images/tools_for_excel.png")
IMGs.Add("images/word.png")

Return IMGs

End Function

Eso es todo. Paso a explicar el código:

En el Page_Load lo que hago es declarar una variable llamada js = "return this.parentNode.previousSibling.checked=true;" , lo que esta línea hace es dejar marcado el radio al hacer click en la imagen… ;)

Luego simplemente a través de un bucle For añado un poco de código html a cada radio button que voy añadiendo… de esta manera se muestran las imágenes.

Y finalmente hay una función MyImages() As List(Of String) que me permita cargar las imágenes que guardo en una carpeta llamada "images/" que luego se mostrarán en cada radiobutton. Simple no?

;)

miércoles 21 de octubre de 2009

ASP.NET – Combinar celdas del gridview

combinar celdas Se ve mucho más profesional mostrar información con un aspecto similar a la imagen que acompaña a este post, no les parece?

Yo creo que eso está fuera de discusión, definitivamente, es un gol de media cancha…

Ahora la pregunta es: cómo hago eso? es dificil?

Para nada, es muy sencillo. Lo único que tenemos que hacer es poner un pequeño código en el evento RowDataBound del gridview.

Veamos un ejemplo completo:

1.- Vamos a añadir un gridview a nuestra página aspx:

        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false">
<
Columns>

<
asp:TemplateField HeaderText="Categoría">
<
ItemTemplate>
<%#Eval("Category")%>
</ItemTemplate>
</
asp:TemplateField>

<
asp:TemplateField HeaderText="Producto">
<
ItemTemplate>
<%#Eval("Product")%>
</ItemTemplate>
</
asp:TemplateField>

</
Columns>
</
asp:GridView>



2.- Creamos un datatable y lo cargamos de datos:



    Dim LastCategory As String = String.Empty
Dim CurrentRow As Integer = -1
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If IsPostBack Then Return

'creamos un datatable al vuelo...
Dim dt As New DataTable()
Dim myColumn As DataColumn

myColumn = New DataColumn()
myColumn.DataType = Type.GetType("System.String")
myColumn.ColumnName = "Category"
dt.Columns.Add(myColumn)

myColumn = New DataColumn()
myColumn.DataType = Type.GetType("System.String")
myColumn.ColumnName = "Product"
dt.Columns.Add(myColumn)

'esto permitirá cargarle datos al datatable
Dim Products As New List(Of ListItem)
Products = MyProducts()
Dim i As Integer = 0
For Each li As ListItem In Products
i += 1

Dim row As DataRow
row = dt.NewRow()
row("Category") = li.Text
row("Product") = li.Value
dt.Rows.Add(row)
Next

'enlazo el datatable al gridview...
'previamente lo ordeno alfabéticamente
Dim dv As DataView
dv = dt.DefaultView
dv.Sort = "Category ASC"
dt = dv.ToTable

GridView1.DataSource = dt
GridView1.DataBind()

End Sub



'función que permite cargar los productos
Private Function MyProducts() As List(Of ListItem)
Dim Products As New List(Of ListItem)

Products.Add(New ListItem("Fruta", "Manzana"))
Products.Add(New ListItem("Fruta", "Papaya"))
Products.Add(New ListItem("Verdura", "Zanahoria"))
Products.Add(New ListItem("Verdura", "Zapallo"))
Products.Add(New ListItem("Fruta", "Plátano"))
Products.Add(New ListItem("Fruta", "Uvas"))
Products.Add(New ListItem("Verdura", "Apio"))
Products.Add(New ListItem("Verdura", "Poro"))
Products.Add(New ListItem("Legumbre", "Lechuga"))
Products.Add(New ListItem("Legumbre", "Coliflor"))
Products.Add(New ListItem("Legumbre", "Beterraga"))
Products.Add(New ListItem("Vitamina", "PVM"))

Return Products

End Function



3.- Aquí está el código que me permite combinar celdas de una columna… para que tenga el aspecto que queremos:



Protected Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound

If e.Row.RowType = DataControlRowType.DataRow Then

Dim
row As DataRowView = CType(e.Row.DataItem, DataRowView)

If LastCategory = row("Category") Then

If
(GridView1.Rows(CurrentRow).Cells(0).RowSpan = 0) Then
GridView1.Rows(CurrentRow).Cells(0).RowSpan = 2
Else
GridView1.Rows(CurrentRow).Cells(0).RowSpan += 1

End If
e.Row.Cells.RemoveAt(0)

Else

e.Row.VerticalAlign = VerticalAlign.Top
LastCategory = row("Category").ToString()
CurrentRow = e.Row.RowIndex

End If

End If

End Sub

Analicemos el código del evento RowDataBound:

1.- Usamos una variable LastCategory que guarde el nombre de la categoría de la fila que vamos enlazando al gridview.

2.- Si el nombre de la nueva categoría es igual al que se guardó en la variable LastCategory lo que hacemos en combinar la celda con las anteriores celdas de la misma columna.

3.- Si el nombre de la nueva categoría es diferente, entonces no combinamos las celdas… y se actualiza el valor de LastCategory.

Es todo.

Ah, me olvidaba… no te olvides que para que la agrupación de celdas se realice correctamente, previamente debes ordenar por categorías, de manera alfabética… ya sea ascendente o descendente… porque sino las mismas categorías harán varios grupos.

;)

miércoles 14 de octubre de 2009

ASP.NET – Cambiar el idioma del calendarextender ajax control toolkit

calendar Como ya se habrán dado cuenta, el calendarextender viene por defecto mostrando sus textos en el idioma inglés, será porque se hizo por gente de habla inglesa? ;)

No es problema, pero configurarlo para que muestre sus textos en el idioma de nuestra aplicación web es muy fácil, veamos:

1.- Modificamos el web.config de acuerdo al idioma que deseamos, por ejemplo Yo pondré es-PE, que es español-Perú

<globalization culture="es-PE" uiCulture="es-PE" />

2.- Vamos al script manager y le agregamos EnableScriptGlobalization=”true”

<asp:ScriptManager ID="ScriptManager1" EnableScriptGlobalization="true" runat="server">
</
asp:ScriptManager

Obviamente los pasos aplicados arriba aplican para una aplicación que tiene un idioma específico no? osea, no cambia dinámicamente el idioma.

Si se tuviera que cambiar dinámicamente el idioma de la aplicación, entonces tendríamos que quitar del web.config los tags que ingresamos previamente y añadirlo desde código en el page_load de alguna página:

1.-

Imports System.Threading

Partial Public Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Dim DinamicLanguage As String = "es-PE"
Thread.CurrentThread.CurrentCulture = New System.Globalization.CultureInfo(DinamicLanguage)

End Sub
   …   …

2.- Agregarle al script manager lo sgte:

<asp:ScriptManager ID="ScriptManager1" EnableScriptGlobalization="true" runat="server"> </asp:ScriptManager

Y eso es todo, ejecuten su aplicación y verán los textos acorde al idioma configurado. ;)

miércoles 7 de octubre de 2009

Editor html en linea

editor_html Quería compartirles un editor html en línea, sobre todo free ;)… que uso muy a menudo desde hace buen tiempo. Se encuentra en http://htmledit.squarefree.com/ y aunque parezca muy simple nos permite crear extractos de código html que muchas veces necesitamos y no tenemos nuestro editor HTML preferido a la mano.

Este editor en línea, inclusive nos permite trabajar con estilos css… es decir, creas tus estilos css y luego el html e inmediatamente puedes ver como va quedando.

Espero que les agrade tanto como a mi.

;)

viernes 18 de septiembre de 2009

ASP.NET – Crear textbox dinamicamente - I

viewstate

Parte I, Parte II.

Esta pregunta me la hicieron hoy, y a simple vista les pareciera algo complejo de hacer…

¿Cómo creo controles textbox de servidor de manera dinámica? por ejemplo, al click de un botón quisiera que se genere un textbox más…

Pasos:

1.- Agregamos un control de servidor asp:Placeholder y un botón al código aspx:

<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Generar textboxes" />
<br />
<asp:PlaceHolder ID="phDinamicControls" runat="server"></asp:PlaceHolder>

</form>

2.- en el code-beside agregamos el sgte código:

Private Property QuantityDinamicControls() As Integer
Get
If ViewState("Quantity") = Nothing Then ViewState("Quantity") = 0

Return DirectCast(ViewState("Quantity"), Integer)

End Get
Set(ByVal value As Integer)
ViewState("Quantity") = value
End Set
End Property

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click

Me.QuantityDinamicControls += 1

End Sub

Protected Overloads Overrides Sub CreateChildControls()

If Page.IsPostBack Then GenerateControls()

End Sub

Private Sub GenerateControls()

Dim Quantity As Integer = 0
While Quantity <= Me.QuantityDinamicControls
Dim txt As New TextBox()
txt.Attributes.Add("style", "margin-bottom: 10px")
txt.ID = "txt" + (Quantity + 1).ToString()
txt.Text = "Algún texto va aquí..."

phDinamicControls.Controls.Add(txt)
phDinamicControls.Controls.Add(New LiteralControl("<br/>"))
Quantity += 1
End While

End Sub

Les explico:

+ Tenemos una propiedad llamada QuantityDinamicControls, que nos permite guardar/recuperar (en el viewstate) la cantidad de controles que hemos generado hasta el momento.

+ Dado que estamos en un entorno web, tenemos que ver cómo hacemos para retener los valores de esos controles generados dinamicamente…. Lo que heremos es recrearlos cada vez que haya un postback al servidor, y esa operación la hacemos sobreescribiendo el método CreateChildControls().

+ Tenemos el método GenerateControls() , que generará los controles… y los irá agregando al PlaceHolder cada vez que hay un postback.

Y para terminar, si queremos recuperar los valores de los controles generados dinamicamente:

Private Function GetTxtControls() As String
Dim sb As New StringBuilder
Dim i As Integer = 0

While i <= Me.QuantityDinamicControls
Dim txt As String = "txt" + (i + 1).ToString()
Dim txtControl As TextBox = TryCast(phDinamicControls.FindControl(txt), TextBox)

sb.Append(txtControl.Text & "<br />")

i += 1
End While

Return sb.ToString

End Function

Te estarás preguntando si en cada refrescado de página los textboxes pierden su valor… la respuesta es: No te preocupes de eso… los valores se pierden y se recrean de manera transparente para ti… osea no tienes que preocuparte de ese aspecto.

Eso sería todo ;)

Parte I, Parte II.

lunes 7 de septiembre de 2009

ASP.NET – Gridview con thead y tbody

thead El gridview renderiza en un elemento html conocido como <table>… </table>, pero renderiza como una tabla mal formada… osea, no incluye en su estructura de cabecera el elemento thead, y por lo tanto no estarías haciendo las cosas bien, por otro lado tendrá problema para ser leído por ejemplo desde dispositivos móbiles… tampoco podrías aplicar un estilo css que diferencie a las filas comunes vs las cabeceras… ya que renderizarían el mismo html.

Por otro lado, si llegaste hasta este post es porque necesitas que tu gridview tenga thead(si fuera posible añades tu razón en un comentario debajo jeje)

Entonces, para que tu gridview tenga thead, haz lo sgte:

1.- Cargas tu gridview con tu data.

2.- Añades esta línea:

GridView1.HeaderRow.TableSection = TableRowSection.TableHeader

Se supone que también debes añadir UseAccessibleHeader = True, pero no es necesario que lo hagas porque por defecto viene a True.

Eso es todo, pero debes respetar el orden… sino obtendrás un error de NullReferenceException, se entiende por qué?

;)

viernes 4 de septiembre de 2009

Bing vs Google

No puedes elegir cuál será tu motor de búsquedas por defecto? Realiza tus búsquedas en ambos a la vez y coméntanos cuál es más interesante para ti ;)

http://google-vs-bing.com/

bing-google

miércoles 19 de agosto de 2009

ASP.NET – Enviar correos con imagenes embebidas

tefiHay varias formas de enviar correos con imágenes embebidas en el mensaje:

1.- Podrías mandar un correo en formato html (vean este post de cómo enviar correos en formato html) e incluyan la imagen a través de un tag html img. Ejemplo:

<span style=”color: blue”>Este es el cuerpo del mensaje, el cual incluye una llamada a una image guardada en un servidor</span>

<img src=”http://www.google.com/intl/en_ALL/images/logo.gif” alt=”Esta es la imagen incluida en el correo” />

… esto puede funcionar, pero puede que no.

En qué casos podría fallar?

+Si se cambió la ruta de la imagen en el servidor, ya no verías la imagen.

+Si se cambió el nombre a la imagen, ya no verías la imagen.

+Si el correo del cliente no está configurado para mostrar correos html, ya no verías la imagen.

+Si el servidor está atendiendo demasiadas peticiones… demorarías en ver la imagen… o no la verías.

+Si el servidor está inoperativo ya no verías la imagen.

+Si te cortan el internet, ya no verías la imagen.

+etc, etc, etc…

Algo más? Sí, estarías consumiendo ancho de banda… osea un problema más.

En fin, hay más casos, pero estarás de acuerdo conmigo en que mejor es buscar otro método más seguro y eficaz… un método por ejemplo que me permita embeber la imagen en el correo, de modo que se haga independiente una vez recibido el correo.

Claaaaaarooooooooooooo… eso sería perfecto!!! pero existe una forma de hacer eso?

La respuesta es sí. Vamos a mostrar dos formas de hacerlo:

1.- Enviar correo en texto plano con imagenes embebidas

2.- Enviar correo en formato html con imagenes embebidas

1.- Enviar correo de texto plano con imagenes embebidas.- Para ello usamos el siguiente método:

Dim Mail As MailMessage = New MailMessage()
Dim Mensaje As String = "Este es el mensaje<img src='cid:imagen001' />"

Mail.From = New MailAddress("unmonitored@jabs.com.pe")
Mail.To.Add("sserrano@jabs.com.pe")
Mail.Subject = "correo con imagen embebida"
Mail.Body = Mensaje

Dim TextoConImagenes As AlternateView
TextoConImagenes = AlternateView.CreateAlternateViewFromString(Mensaje, Nothing, "text/plain")
Dim imagen As LinkedResource
imagen = New LinkedResource((Server.MapPath("Images\tefina.gif")))
imagen.ContentId = "imagen001"
TextoConImagenes.LinkedResources.Add(imagen)

Mail.AlternateViews.Add(TextoConImagenes)

Dim mailClient As New SmtpClient()
Dim basicAuthenticationInfo As New NetworkCredential("algun_correo", "su_contraseña")
mailClient.Host = "su_sevidor_de_correo"
mailClient.UseDefaultCredentials = True
mailClient.Credentials = basicAuthenticationInfo

mailClient.Send(Mail)

He pintado de rojo la parte que permite embeber una imagen en el mensaje… chequeen sobre todo esa parte.

y en el mensaje del correo, dense cuenta que he añadido <img src='cid:imagen001' /> … esa sería la imagen embebida. ;)

2.- Enviar correo en formato html con imagenes embebidas.- Casi, lo mismo… sólo cambian algunas cositas:

Dim Mail As MailMessage = New MailMessage()
Dim Mensaje As String = "Este es el mensaje<img src='cid:imagen001' />"
Dim MensajeHTML As String = "<span style='color: blue'>Este es el mensaje HTML</span><br /><img src='cid:imagen001' />"

Mail.From = New MailAddress("unmonitored@jabs.com.pe")
Mail.To.Add("sserrano@jabs.com.pe")
Mail.Subject = "correo con imagen embebida"
Mail.Body = Mensaje

Dim HTMLConImagenes As AlternateView
HTMLConImagenes = AlternateView.CreateAlternateViewFromString(MensajeHTML, Nothing, "text/html")
Dim imagen As LinkedResource
imagen = New LinkedResource((Server.MapPath("Images\tefina.gif")))
imagen.ContentId = "imagen001"
HTMLConImagenes.LinkedResources.Add(imagen)

Mail.AlternateViews.Add(HTMLConImagenes)

Dim mailClient As New SmtpClient()
Dim basicAuthenticationInfo As New NetworkCredential("algun_correo", "su_contraseña")
mailClient.Host = "su_sevidor_de_correo"
mailClient.UseDefaultCredentials = True
mailClient.Credentials = basicAuthenticationInfo

mailClient.Send(Mail)

Para ambos casos, no se olviden de importar:

Imports System.Net.Mail
Imports System.Net

Cada imagen debe tener un ID, vale decir, un identificador… el cual le permitirá tener una ubicación en el mensaje y ser tratada como recurso embebido.

;)

viernes 7 de agosto de 2009

ASP.NET - Descargar archivos del servicio web

ftws Una pregunta bastante frecuente:

Cómo descargo un archivo que está siendo administrado por un servicio web?

por ejemplo, descargarse archivos pdf… que podrían estar inclusive en otro servidor… osea, el servicio web es el intermediario, no tengo acceso a los archivos más que mediante el acceso al servicio web.

Vamos a ver, en realidad esto es muy sencillo.

Necesitamos dos cosas:

+ El servicio web y

+ La aplicación web que hace la petición de archivos.

1.- El servicio web

Imports System.Web.Services
Imports System.IO

<WebService(Namespace:="http://tempuri.org/")> _
Public Class Service1
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    Public Function ReturnBytesToClient(ByVal FileName As String) As Byte()
        Dim fs As FileStream = Nothing

        Try
            Dim FilePath As String = Server.MapPath("~") & "\Files\" & FileName

            'obtenemos el archivo del servidor
            fs = File.Open(FilePath, FileMode.Open, FileAccess.Read)

            Dim byteBuffer(CInt(fs.Length - 1)) As Byte
            fs.Read(byteBuffer, 0, CInt(fs.Length))


            Return byteBuffer

        Catch exp As Exception
            Return Nothing
        Finally
            If Not fs Is Nothing Then fs.Close()
        End Try

    End Function

End Class

Ese es todo el código para el servicio web… lo explico:

Hay un método llamado ReturnBytesToClient el cual acepta un parámetro, ese parámetro es el nombre del archivo que se está solicitando… entonces… como ven en la imagen de arriba… dentro de la estructura de archivos del servicio web, hay una carpeta llamada Files, allí se guardan los archivos a despachar.

Cuando este método es invocado, accede al archivo mediante un filestream y luego eso se devuelve al cliente que lo solicita, como un conjunto de bytes.

2.- Aplicación web

Antes de utilizar el servicio web, tenemos que crear una referencia en nuestra aplicación web a dicho servicio web:

ftwapp

Este link muestra cómo añadir referencias a servicios web.

La referencia al servicio web debe tener un nombre, yo lo he llamado FileTransfers, como pueden ver en la imagen.

Luego, viene el código que recupera el archivo:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load

    RecuperarArchivoDelWebService()

End Sub

Private Sub RecuperarArchivoDelWebService()
    Dim NombreDelArchivoASolicitar As String = "SoftwareTest.pdf"

    Dim ws As New FileTranfers.Service1
    Dim bytes() As Byte = ws.ReturnBytesToClient(NombreDelArchivoASolicitar)

    Using ms As New IO.MemoryStream(bytes)
        ms.WriteTo(Response.OutputStream)
    End Using

End Sub

Esta es la versión más simple del método para recuperar el archivo… lo que hace este diminuto pero espectacular trozo de código es mostrar la ventanita de Guardar archivo. Ejecuten el código para ver el resultado.

Con eso ya hemos recuperado el archivo del servicio web.

Aquí les muestro una nueva versión del mismo método, el cual adicionalmente nos permite especificar cuál será el nuevo nombre del archivo:

Private Sub RecuperarArchivoDelWebService()
    Dim NombreDelArchivoASolicitar As String = "SoftwareTest.pdf"
    Dim NuevoNombreDelArchivo As String = "SoftwareTesting.pdf"

    Dim ws As New FileTranfers.Service1
    Dim bytes() As Byte = ws.ReturnBytesToClient(NombreDelArchivoASolicitar)

    Using ms As New IO.MemoryStream(bytes)
        Response.AddHeader("Content-Disposition", "attachment; filename=" & NuevoNombreDelArchivo)
        ms.WriteTo(Response.OutputStream)
    End Using

End Sub

;)

lunes 27 de julio de 2009

Burlando la autenticacion de sqlservercentral.com

ssc0 Para quienes no conocen sql server central, es un portal web con muchos recursos para desarrolladores y administradores de bases de datos.

Para poder visualizar los interesantes artículos de este portal, sólo hay que crear una cuenta de usuario, de manera gratuita… simple.

Hoy, desde google llegué a este sitio web, y como hace mucho tiempo que no estoy por sqlservercentral.com, olvidé la contraseña que antes usaba… y a veces demora el correo que te devuelve tu contraseña cuando lo solicitas…

Pero pensé que esto no me podía detener y decidí ver si podía burlar el formulario de autenticación para ver el artículo sin loguearme. Y lo logré. El truco lo hice sobre Internet Explorer 8, usando la herramienta Developer Toolbar de Microsoft.

En realidad no fue gran cosa, veamos:

1.- Si ingresan al sqlservercentral e intentan leer un artículo, pero no están autenticados, sólo podrán ver las primeras 3 líneas del artículo y además con un efecto de opacidad bastante molesto.

Yo hice las pruebas sobre esta página : http://www.sqlservercentral.com/articles/65804/

ssc2

Como ven en la imagen, he activado la barra Developer toolbar.. luego de ello, hice click sobre el ícono que se muestra seleccionado en la imagen… luego de ello, cuando pasen el cursor sobre la página web… notarán que se forman recuadros, esos recuadros son los distintos elementos html que han sido usados para darle la estructura a la página que estamos viendo, por ejemplo, el recuadro de la imagen muestra un elemento div:

ssc3

Ahora, traten de seleccionar el mismo área que yo he seleccionado en la imagen… si no lo pueden hacer… vayan a la developer toolbar y localizen la línea que tiene un div con id=”limitContainer”… hagan click allí y luego modifiquen los datos de la derecha, osea, quitenle el check igual como se muestra en la imagen:

ssc4

después de esto, ya deberían poder visualizar el artículo completo… sin necesidad de autenticarse.

Puedes hacer el mismo truco si usas Mozilla Firefox, con la herramienta FireBug.

Ahora, la pregunta es: Qué se hizo mal? por qué ocurre esto?

Podrían haber varias respuestas:

Una de las respuestas sería que no deberían haber traído el artículo entero… sino sólo un extracto, y recién recuperar el artículo completo de la base de datos después que el usuario se ha logueado correctamente.

Bueno, a ver si los de sqlservercentral arreglan ese asunto.

;)

jueves 23 de julio de 2009

ASP.NET - Disfrazar y reducir tamaño de url con tinyurl

Muchos ya saben qué es tinyurl, para los que no saben: tinyurl es un servicio gratuito que permite convertir una url en otra url de tamaño mucho más reducido.

Tú dirás (si es que aún no la ves) y para qué sirve esto?
Si te pones a pensar en la utilidad que puede tener, descubrirás que puede ser de gran valor. Te pongo unos casos:
- A veces envías correos electrónicos en el cual incluyes urls extensas, las cuales terminan mostrándose en dos o tres líneas y que finalmente hacen dificil reconstruir la url.

- Si no quisiéramos que nuestra url sea detectada por robots, esta sería una buena manera de disfrazar nuestras urls... pues sólo leerían algo como tinyurl.com/xxxx ... lo cual o dice nada.

- Si no quisiéramos que el usuario sepa a dónde conduce el link... a menos que haga click en él... para que el click sea contabilizado.

- etc, etc, etc ... a estas alturas ya se te habrá ocurrido un par de casos en los que es útil tinyurl.

Ahora, lo que vamos a hacer es automatizar el proceso de reducción de url via tinyurl.
1.- Necesitamos una fx que me permita recuperar el html de la página de tinyurl... así:

Public Shared Function GetHTMLFromURL(ByVal URL As String) As String
Dim ASCII As New System.Text.ASCIIEncoding
Dim netWeb As New System.Net.WebClient
Dim lsWeb As String
Dim laWeb As Byte()

Try
laWeb = netWeb.DownloadData(URL)
lsWeb = ASCII.GetString(laWeb)
Catch ex As Exception
Throw New Exception(ex.Message.ToString + ex.ToString)
End Try

Return lsWeb

End Function

2.- Usamos la función desde el page_load de nuestra página aspx... así:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim OriginalUrl As String = "http://dotnet-peru.blogspot.com"

'la página api-create.php es la que hace el trabajo sucio
Dim ShortUrl As String = GetHTMLFromURL("http://tinyurl.com/api-create.php?url=" & OriginalUrl)

End Sub

De esta manera, la url http://dotnet-peru.blogspot.com se convirtió en http://tinyurl.com/nccaze

Y eso es todo, realmente fue muy sencillo no es cierto? ;)

lunes 20 de julio de 2009

ASP.NET – Tooltip en HeaderImageUrl del gridview

risk_gridview El control gridview nos permite poner una imagen como alternativa al texto de la cabecera de una columna, esta imagen la ponemos usando el atributo HeaderImageUrl de un TemplateField:

<asp:GridView ID="gridrisk_event" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField HeaderImageUrl="~/images/locked.gif">
<ItemTemplate>
<asp:CheckBox ID="chkInactive" Enabled="false" runat="server" Checked='<%#Eval("IsInactive") %>' />
</ItemTemplate>
</asp:TemplateField>

… …

</Columns>
</asp:GridView>

Y eso, es todo… la imagen se verá muy bien… pero a veces, la image necesita una pequeña ayudita, como por ejemplo, poenerle un tooltip… porque no siempre es obvio lo que representa… entonces, para ponerle tooltip a la imagen, usamos el evento RowDataBound del gridview:

Private Sub gridrisk_event_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gridrisk_event.RowDataBound
If e.Row.RowType = DataControlRowType.Header Then
     e.Row.Cells(0).ToolTip = "Indica si el riesgo ha sido desactivado."
End If

End Sub

Donde e.Row.Cells(0) representa la cabecera de la primera columna, si quisieras la cabecera de la segunda columna … cómo harías? obvio: e.Row.Cells(1)

Eso sería todo, el tooltip mejorará tu gridview ;)