En la entrada anterior, vimos como extender el control TextBox (del .NET Framework) para restringir la entrada a sólo ciertos caracteres definidos en una expresión regular. Pues bien, en esta ocasión vamos a crear un control que ayudará a medir la popularidad de algún artículo o comentario.
Para la apariencia del control, tomé como base "CSS Star Rating", el cuál incluye una hoja de estilos y una imagen. Como ya había comentado en la entrada anterior, para hacer uso de estos elementos es necesario registrarlos y compilarlos, a continuación se muestra la forma de hacerlo:
[assembly: System.Web.UI.WebResource("Buayacorp.Web.Resources.rating.css", "text/css", PerformSubstitution = true)]
[assembly: System.Web.UI.WebResource("Buayacorp.Web.Resources.star.gif", "image/gif")]
Como habrán podido observar, puse el atributo PerformSubstitution=true
para poner expresiones del tipo background: url('<%= WebResource("Buayacorp.Web.Resources.star.gif") %>') top left repeat-x;
dentro de rating.css (es necesario hacer esto ya que si sólo se pone background: url(star.gif) top left repeat-x;
, no funcionará)
A continuación, una parte del código del control:
public class BCRating : WebControl, IPostBackEventHandler
{
#region Propiedades
// Sirve para indicar si se usa una hoja de estilos personalizada
public bool UseCustomCss {/*...*/}
// Sirve para obtener el valor que ha sido seleccionado por el usuario
public int SelectedRating {/*...*/}
// Sirve para establecer el promedio de valoración
public double Value {/*...*/}
#endregion
#region "Renderizado" del control
protected override void Render(HtmlTextWriter writer)
{
// Asegurar que el control está dentro de un formulario con atributo runat=server
Page.VerifyRenderingInServerForm(this);
if (!DesignMode)
{
RenderCss(writer);
CreateLinks(writer);
}
else// Escribir el ID del control si el control está en modo de diseño
writer.Write(ID);
}
private void RenderCss(HtmlTextWriter writer)
{
// Sólo agregar un tag <link /> a la página, aún cuando haya varios controles
if (HttpContext.Current.Items["rating_ctrl"] == null && !UseCustomCss)
{
// Obtenemos la URL del recurso embedido
string resource = Page.ClientScript.GetWebResourceUrl(GetType(), "Buayacorp.Web.Resources.rating.css");
// Crear el tag <link /> y agregarlo a la colección de controles
HtmlLink lnk = new HtmlLink();
lnk.Href = resource;
lnk.Attributes["rel"] = "stylesheet";
lnk.Attributes["type"] = "text/css";
Controls.Add(lnk);
lnk.RenderControl(writer);
HttpContext.Current.Items["rating_ctrl"] = true;
}
}
private void RenderLinks(HtmlTextWriter writer)
{
// Agregar el tag: <ul class="star-rating">
writer.AddAttribute(HtmlTextWriterAttribute.Class, "star-rating");
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
// Calcular el ancho del control, en base al promedio de valoración
// 25 -> equivale al ancho de la imagen que forma parte del control (Mejorar esta parte!)
int width = (int)Math.Floor(25 * Value);
// Agregar un tag <li class="current-rating">Actualmente X/5<li>
writer.AddAttribute(HtmlTextWriterAttribute.Class, "current-rating");
writer.AddAttribute(HtmlTextWriterAttribute.Style, string.Format("width:{0}px", width));
writer.RenderBeginTag(HtmlTextWriterTag.Li);
writer.Write(string.Format("Actualmente {0}/5", Value));
writer.RenderEndTag();
// La valoración es de 1-5
links = new HtmlAnchor[5];
for (int i = 0; i < links.Length; i++)
{
// Crear tags de la forma <li><a href="..." class="starX" title="X de 5">X</a></li>
links[i] = new HtmlAnchor();
links[i].EnableViewState = false;
links[i].InnerText = (i + 1).ToString();
links[i].HRef = Page.ClientScript.GetPostBackClientHyperlink(this, (i + 1).ToString());
links[i].Attributes["class"] = "star" + (i + 1).ToString();
links[i].Title = (i + 1) + " de " + links.Length;
Controls.Add(links[i]);
writer.RenderBeginTag(HtmlTextWriterTag.Li);
links[i].RenderControl(writer);
writer.RenderEndTag();
}
writer.RenderEndTag();
}
#endregion
private HtmlAnchor[] links;
public event EventHandler SelectedRatingChanged;
#region IPostBackEventHandler Members
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument)
{
if (!string.IsNullOrEmpty(eventArgument))
{
SelectedRating = int.Parse(eventArgument);
if (SelectedRatingChanged != null)
SelectedRatingChanged(this, EventArgs.Empty);
}
}
#endregion
}
En el código mostrado arriba, sólo quiero comentar la implementación de la interfaz IPostBackEventHandler, ésto posibilitará que nuestro control maneje los postbacks y genere los eventos necesarios.
El control mostrado tiene algunas deficiencias y poca flexibilidad para personalizarlo, se deja como tarea para el lector solucionar estas cosas. 🙂