Mensajes de Foro con Algoritmo Recursivo vb.NET
[Visual Basic .NET]

Fecha: 30/Abr/2005 (13/04/2005)
Autor: Julio Anthony Aguilar Castillo ([email protected])

 


Un ejemplo practico con el cual haras uso de tablas y algoritmos recursivos para recuperar tu información. Esto es aplicable a foros aunque tambien puedes usarlo para información que requiera vinculación padre-hijo en tablas de bd, además de la creacion y uso de controles en tiempo de ejecución.

La idea general parte de una tabla recursiva de la siguiente forma:

taMensajesForo(

CodigoTemaForo char(3)
CodigoMensajeForo char(3)
tituloMensajeForo char(50)
descripcionMensajeForo char(1000)
CodigoMensajeForoPadre char(15)
usuariocrea char(20)

)
Tenemos esta tabla donde registramos los mensajes para un tema especifico (para estos casos el idMensajePadre quedaria vacio o nulo).. luego en algun momento respondemos a un determinado mensaje entonces el campo idMensajePadre almacenará el codigo del mensaje al cual respondemos.
La información se almacena sin mayor dificultad el problema es cuando queremos recuperar la información,

Ok aqui vamos.. lo primero es generar dos procedimientos almacenados uno para recuperar los mensajes que no tengan hijos y el otro para obtener mensajes hijos de un mensaje padre

1er procedimiento:

CREATE PROCEDURE spObtenerMensajesTema
(
@CodigoTemaForo CHAR(15)
)
AS
SELECT
CodigoTemaForo,
CodigoMensajeForo,
CodigoMensajeForoPadre,
TituloMensajeForo,
DescripcionMensajeForo,
usuariocrea

FROM
taMensajesForo

WHERE
CodigoTemaForo = @CodigoTemaForo and
CodigomensajeForoPadre = '000000000000000'
// Usamos este codigo para decir que no tiene hijos

2do procedimiento

CREATE PROCEDURE spObtenerMensajesHijo
(
@CodigoMensajeForoPadre CHAR(15)
)
AS
SELECT
CodigoTemaForo,
CodigoMensajeForo,
CodigoMensajeForoPadre,
TituloMensajeForo,
DescripcionMensajeForo,
usuariocrea

FROM
saelse.taMensajeForo

WHERE
CodigomensajeForoPadre = @CodigoMensajeForoPadre
// obtenemos todos los mensajes de un mensaje padre

OK ,... hasta aqui hemos generados los 2 procedimientos almacenados que luego invocaremos desde .net. Ahora nos toca generar nuestro webform para obtener la data

 

Web form mensaje.aspx

Creamos el form mensajel archivo .aspx y su codebehind .aspx.vb


En el archivo aspx Agregamos 1 datagrid que hara enlace con la data

<asp:datagrid id="dgMensajesTema" runat="server" Width="100%" OnItemCommand="panel_click" AutoGenerateColumns="False"
GridLines="Horizontal">
<Columns>
... aqui agregaremos las columnas
</Columns>
</asp:datagrid>

A este datagrid lo llamamos dgMensajesTema le dimos el evento panel_click en el evento OnItemCommand y ahora le vamos a agregar las columnas

<asp:BoundColumn DataField="CodigoMensajeForoPadre" Visible="false"></asp:BoundColumn>
<asp:BoundColumn DataField="CodigoTemaForo" Visible="false"></asp:BoundColumn>
<asp:BoundColumn DataField="CodigoMensajeForo" Visible="false"></asp:BoundColumn>

Tres campos enlazados pero que no los mostraremos, esto nos servira para hacer las consultas a la BD, debes tener en cuenta que codigomensajeforopadre esta en la columna 0, codigotemaforo en la columna 1 y asi sucesivamente...
luego agregamos la columna donde mostraremos los mensajes sin padre (un template column) y un panel donde agregaremos sus hijos

<asp:TemplateColumn HeaderText="Mensaje">
<ItemTemplate>
// insertamos una tabla solo para q la info salga ordenada
<table width="100%" border="0" cellSpacing="0" cellPadding="0" borderColor="gainsboro">
<tr>
<td colspan="2" height="20px" >
<asp:ImageButton id="Imagebutton2" commandname="panel" runat="server" ImageUrl="../imagenes/plus.gif"></asp:ImageButton>
Mensaje creado por <%# DataBinder.Eval(Container.DataItem,"usuarioCrea") %> :
<asp:HyperLink id="hlMensajes" runat="server" tooltip= '<%# DataBinder.Eval(Container.DataItem,"DescripcionMensajeForo") %>' NavigateUrl='enlace a un webform detalle' text = '<%# DataBinder.Eval(Container.DataItem,"TituloMensajeForo") %>' >
</asp:HyperLink>
</td>
<td bgcolor="#e6eefb" height="20px" class="normal" align="right">
<asp:Label id="Label5" runat="server" CssClass="normal">
<%# DataBinder.Eval(Container.DataItem,"FechaCreacion") %>
</asp:Label>
</td>
</tr>
</table>
// fin de la tabla.

// aqui insertamos el panel para mostrar y ocultar los mensajes hijo

<asp:Panel id="Panel2" runat="server" Width="100%" Visible="false">
<TABLE id="Table3" cellSpacing="0" cellPadding="0" width="100%" border="0">
<TR vAlign="top">
<TD id="PanelIzquierdo" vAlign="top" width="60%" bgColor="#f9f8f7" rowSpan="2" runat="server"></TD>
</TR>
</TABLE>
</asp:Panel>

</ItemTemplate>
</asp:TemplateColumn>

Bueno doy la aclaración de que en un primer momento "PanelIzquierdo" no va a aparecer (es obvio verdad?) y tan solo se mostraran los mensajes padre tal como muestra la figura:


Y solo haciendo click en la cruz al lado de cada mensaje obtendremos todos los mensajes hijo

Ahora nos vamos al codigo behind osea al mensaje.aspx.vb
En el PanelIzquierdo mostraremos la info de los hijos insertando dinamicamente controls , para nuestro caso hyperlinks e imagenes entonces

Primero declaramos nuestras variables

Dim codigoTemaForo As String

Nuestros controles para insertarlos dinamicamente

Dim WithEvents hlResponder As HyperLink
Dim WithEvents hlnodo As HyperLink
Dim WithEvents hlTituloAs HyperLink
Dim iDinamico As System.Web.UI.WebControls.Image

en el Load

Private Sub Page_Load(......)
   CodigoTemaForo="001200500000002"
	'Si la pagina es accesada por primera vez
	If Page.IsPostBack  = false then
		'La idea es cargar los mensajes que no tengan hijos,
        dgMensajesTema.DataSource = f_Obtenermensajetema(CodigoTemaForo)
        dgMensajesTema.DataBind()
	End If
End Sub

Luego vamos declarando todas las funciones definidas y las que nos serviran 
Las dos funciones que devuelven datasets y llaman a los procedimientos almacenados que creamos
 Function f_Obtenermensajetema(ByVal paramCodigoTemaForo As String) As DataSet
' Crea instancia de conexion y el objeto Command
Dim cnxDBINTRANET As New SqlConnection(ConfigurationSettings.AppSettings("connectionString"))
Dim sqlObtenerMensajeTemaForo As New SqlDataAdapter("spObtenerMensajesTema", cnxDBINTRANET)
' Marcamos el Command como un SPROC
sqlObtenerMensajeTemaForo.SelectCommand.CommandType = CommandType.StoredProcedure
' Agregamos los parametros al procedimiento
Dim parametercodigoTema As New SqlParameter("@CodigoTemaForo", SqlDbType.Char, 15)
parametercodigoTema.Value = paramCodigoTemaForo
sqlObtenerMensajeTemaForo.SelectCommand.Parameters.Add(parametercodigoTema)
Dim dsObtenerMensajeTema As New DataSet
' Creamos y llenamos el DataSet
sqlObtenerMensajeTemaForo.Fill(dsObtenerMensajeTema)
Return dsObtenerMensajeTema
End Function Function f_ObtenermensajesHijo(ByVal paramcodigoMensajePadre As String) As DataSet
' Crea instancia de conexion y el objeto Command
Dim cnxDBINTRANET As New SqlConnection(ConfigurationSettings.AppSettings("connectionString"))
Dim sqlObtenerMensajeTemaForo As New SqlDataAdapter("spObtenerMensajesHijo", cnxDBINTRANET)
' Marcamos el Command como un SPROC
sqlObtenerMensajeTemaForo.SelectCommand.CommandType = CommandType.StoredProcedure
' Agregamos los parametros al procedimiento
Dim parametercodigoTema As New SqlParameter("@CodigoMensajePadre", SqlDbType.Char, 15)
parametercodigoTema.Value = paramcodigoMensajePadre
sqlObtenerMensajeTemaForo.SelectCommand.Parameters.Add(parametercodigoTema)
Dim dsObtenerMensajeTema As New DataSet
' Creamos y llenamos el DataSet
sqlObtenerMensajeTemaForo.Fill(dsObtenerMensajeTema)
Return dsObtenerMensajeTema
End Function
Luego el evento que se lanza al hacer clic sobre la cruz (Imagebutton2), es aqui donde llamamos al algoritmo recursivo Public Sub panel_click(ByVal sender As System.Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs)
If e.CommandName = "panel" Then 'ocultamos o mostramos el panel de resultados
Dim temp1 As Panel = CType(e.Item.FindControl("Panel2"), Panel)
temp1.Visible = Not (temp1.Visible) 'recuerdan los campos ocultos? pues bien es aqui donde los usamos 'en esta caso al codigoMensajeForoPadre
obtenerhijos(e.Item.Cells(2).Text, 1, temp1)
Dim temp2 As ImageButton = CType(e.Item.FindControl("Imagebutton2"), ImageButton)
If temp1.Visible Then 'cambiamos el icono del imagebutton
temp2.ImageUrl = "../imagenes/minus.gif"
Else
temp2.ImageUrl = "../imagenes/plus.gif"
End If
End If
End Sub

Ahora definimos a nuestro obtenerhijos. Este metodo hace uso de llamadas recursivas como veremos


Sub obtenerhijos(ByVal codigomensaje As String, ByVal nivel As Integer, ByVal panel As Panel)
Dim dr_ As DataSet = f_ObtenerMensajesHijo(codigomensaje)
Dim dr As DataRow
For Each dr In dr_.Tables(0).Rows Dim mensajePadre As String
Dim TituloPadre As String
Dim DescripcionPadre As String
Dim usuarioPadre As String
Dim fechaPadre As String
Dim nroarchivos As Integer mensajePadre = CType(dr("CodigomensajeForo"), String)
TituloPadre = CType(dr("TituloMensajeForo"), String)
DescripcionPadre = CType(dr("DescripcionMensajeForo"), String)
usuarioPadre = CType(dr("usuarioCrea"), String)
fechaPadre = CType(dr("Fechacreacion"), DateTime).ToShortDateString() 'ubicamos al panel donde insertaremos los mensajes hijo Dim parent As Control = panel.FindControl("PanelIzquierdo") hlResponder = New HyperLink
hlTitulo = New HyperLink
hlnodo = New HyperLink With hlTitulo
.CssClass = "normal"
.Visible = True
.Text = "Creado por " + usuarioPadre + " : " + TituloPadre + ". Fecha: " + fechaPadre
.NavigateUrl = Request.ApplicationPath + " pagina de detalle de mensaje.."
End With

With hlResponder
.ImageUrl = "../imagenes/1x1.gif"
.Width = New System.Web.UI.WebControls.Unit(10)
.Height = New System.Web.UI.WebControls.Unit(30)
End With

With hlnodo
.ImageUrl = "../imagenes/node.gif"
End With

'aqui insertamos imagenes en blanco para dar la apariencia de arbol a nuestros mensajes obtenerEspacio(nivel, panel) 'aqui agregamos los controles parent.Controls.Add(hlnodo)
parent.Controls.Add(hlTitulo)
parent.Controls.Add(hlResponder) parent.Controls.Add(New LiteralControl("<" & "br" & ">"))
'aqui la llamada recursiva If mensajePadre <> "" Then
obtenerhijos(mensajePadre, nivel + 1, panel)
End If Next dr
End Sub
Aqui el módulo para los espacios en blanco esto para dar la apariencia de arbol Sub obtenerEspacio(ByVal entero As Integer, ByVal panel As Panel)
While entero > 0
iDinamico = New System.Web.UI.WebControls.Image
With iDinamico
.Visible = True
.ImageUrl = "../imagenes/1x1.gif"
.Width = New System.Web.UI.WebControls.Unit(30)
End With
Dim parent As Control = panel.FindControl("Panelizquierdo")
parent.Controls.Add(iDinamico)
entero = entero - 1

End While
End Sub

Y listo!! eso es todo, ya arriba hay una imagen con las muestra del ejemplo ejecutandose

Aclaraciones .

Tenemos una tabla con SQL Server 2000 llamada taMensajes con las características que mencione anteriormente. La conexión a la BD es almacenada en el web.config asi:

<appSettings>
<add key="ConnectionString" value="server=localhost;database=dbPrueba;uid=sa;pwd=sa;" />
</appSettings>


Dim cnxDBINTRANET as New SqlConnection(ConfigurationSettings.AppSettings("connectionString")) Dim

Reeemplazariamos el valor de ConfigurationSettings.AppSettings("connectionString") por "server=localhost;data......" y eso es todo.

Vi un ejemplo con algoritmos recursivos pero lo hacia la BD.. eso tambien sirve aunque cuando el numero de llamadas crece el gestor puede colapsar.

Espero que les sirva y se diviertan implementandolo, nos vemos.GBY


Espacios de nombres usados en el código de este artículo:

imports System.Data.SqlClient


Fichero con el código de ejemplo: khaleb2002_MensajesForoConAlgoritmoRecursivo.zip - Tamaño 5KB


ir al índice