septiembre 2008 - Artículos
Siguiendo con las respuestas en los foros hoy voy a explicar como poder validar si tenemos seleccionado un elemento de una lista.
http://forums.microsoft.com/MSDN-ES/ShowPost.aspx?PostID=3922327&SiteID=11
Para poder realizar esto de la forma más simple y que sea compatible con los otros validadores utilizaré un CustomValidator.
Crearé un formulario con una lista un customValidator y un botón para provocar la recarga de la página.
Para que la validación no influya en el rendimiento de la aplicación validaremos en el cliente y crearemos una función javascript para la validación.
<script type="text/javascript">
function ValidarListaSeleccionada(source, arguments)
{
var list = document.getElementById(source.controltovalidate);
if( list != null)
{
var listValue = list.value;
if(listValue =="")
arguments.IsValid=false;
else
arguments.IsValid=true;
}
}
</script>
Este script es muy simple pero muy eficaz ;-).
Para crear una función que podamos utilizar con customValidator tiene que tener dos parámetros:
La validación consiste en localizar el control que queremos validar gracias a la propiedad controltovalidate del customValidator y recuperar el valor seleccionado de la lista. Si el valor esta vacío quiere decir que el usuario no ha seleccionado nada.
Una vez que tenemos la función creada configuraremos el validador para que la utilice.
<asp:CustomValidator ID="CustomValidator1" runat="server"
ControlToValidate="ListBox1"
ErrorMessage="Tienes que seleccionar un elemento de la lista."
ClientValidationFunction="ValidarListaSeleccionada"
ValidateEmptyText="True">
</asp:CustomValidator>
Importante:
Y con esto ya hemos creado un validador personalizado para nuestra lista.
Happy Coding
Los móviles de última generación están empezando a sacar partido al impresionante filón que es el acelerómetro los desarrolladores están jugando con sus posibilidades y ya se pueden ver pruebas verdaderamente impresionantes.
Quien tenga ganas de testear se puede bajar el SDK y hay varios ejemplos de códigos .
Fuente http://www.xatakamovil.com/2008/09/15-touch-diamond-vr-hologram
Saludos.
Siguiendo con el anterior artículo de personalizar un TreeView I para poder editar los nodos hoy veremos precisamente la parte donde habilitamos la edición de los nodos.
Crearemos una nueva clase que llamaremos TreeNodeEdit que heredara de TreeNode. Con esto ya tendremos la base para montar nuestro nodo editable.
[ToolboxData("<{0}:TreeNodeEdit runat=server></{0}:TreeNodeEdit>")]
public class TreeNodeEdit : TreeNode
Crearemos las propiedades públicas para poder configurar nuestro Nodo.
-
bool Editable -->Para hablilitar la edición del nodo.
- string TextBoxValue --> Valor de la edición.
- string OldValue --> Valor anterior a la edición.
- string EditImageUrl --> Url del a imagen del botón editar.
- string CancelImageUrl --> Url de la imagen del botón cancalar.
- string SaveImageUrl --> Url de la imagen del botón guardar.
- string EditTooltip --> Tooltip del botón editar.
- string SaveTooltip --> Tooltip del botón guardar.
- string CancelTooltip --> Tooltip del botón cancelar.
- string TextBoxValueCSSView --> estilo del textbox de la edición.
Ejemplo:
[Localizable(true), Category("Edit")]
public string CancelImageUrl
{
get{
if (ViewState["CancelImageUrl"] != null)
return ViewState["CancelImageUrl"].ToString();
else
return "";
}
set{
ViewState["CancelImageUrl"] = value;
}
}
Ahora veremos realmente donde se encuentra la chica del control y eso es en el evento RenderPostText que es que se lanza justo después de pintar el nodo y nos servirá para pintar los controles de edición justo cuando termina el nodo.
protected override void RenderPostText(HtmlTextWriter writer)
if (Editable)
{
#region Variables
this.SelectAction = TreeNodeSelectAction.Select;
string idEdit = Guid.NewGuid().ToString();
string idView = Guid.NewGuid().ToString();
string idTb = Guid.NewGuid().ToString();
Page page = HttpContext.Current.CurrentHandler as Page;
#endregion Variables
#region script
if (page == null)
throw new NotSupportedException("Error");
#endregion script
Primero comprobamos si el nodo está habilitado para ser editado y luego inicializamos los identificadores que posteriormente necesitaremos para localizar los controles que utilizaremos para la edición del nodo.
Otra parte importante del código es que como no podemos acceder directamente a la página desde un nodo del TreeView, la tenemos que recuperar del contexto actual.
-
Panel Modo Vista: crearemos un panel que contendrá los controles que mostrarán los datos en modo vista, una label para mostrar los datos, la imagen para habilitar la edición la cual si no se informa utilizaremos una que tenemos guardada en los recursos del ensamblado y finalmente le añadiremos el evento onclick de cliente para lanzar la edición desde javascript (editNode).

#region Panel modo Vista
writer.AddAttribute("id", idView);
writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "inline");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
Label lb = new Label();
lb.CssClass = this.TextBoxValueCSSView;
lb.Text = string.Format("({0})", TextBoxValue);
lb.RenderControl(writer);
writer.Write(" ");
Image imgV = new Image();
if (!string.IsNullOrEmpty(EditImageUrl))
imgV.ImageUrl = EditImageUrl;
else
imgV.ImageUrl = page.ClientScript.GetWebResourceUrl(this.GetType(), "TreeviewEditControl.Resources.ItemEdit.gif");
imgV.Attributes.Add("onclick", "editNode( '" + idEdit + "', '" + idView + "')");
imgV.Style.Add(HtmlTextWriterStyle.Cursor, "Pointer");
imgV.ImageAlign = ImageAlign.Middle;
imgV.ToolTip = this.EditTooltip;
imgV.RenderControl(writer);
writer.Write(" ");
writer.RenderEndTag();
#endregion Panel modo Vista
-
Panel Modo Edición: crearemos otro panel para mostrar los controles cuando el nodo esté en edición. Un textBox para insertar los datos y dos imageButtons. Una para guardar los datos y otra para anular la edición. Igual que anteriormente le añadiremos el evento onclick para las acciones desde javascript ( viewNode, cancelNode).
writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none");
writer.AddAttribute("id", idEdit);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
TextBox tb = new TextBox();
tb.Text = TextBoxValue;
tb.ID = idTb;
tb.Width = Unit.Pixel(40);
tb.RenderControl(writer);
writer.Write(" ");
ImageButton imgE = new ImageButton();
if (!string.IsNullOrEmpty(SaveImageUrl))
imgE.ImageUrl = SaveImageUrl;
else
imgE.ImageUrl = page.ClientScript.GetWebResourceUrl(this.GetType(), "TreeviewEditControl.Resources.disk_blue.gif");
imgE.OnClientClick = "viewNode('" + _owner.ClientID + "', '" + idTb + "','EditNode|" + this.ValuePath + "')";
imgE.ImageAlign = ImageAlign.Middle;
imgE.ToolTip = this.SaveTooltip;
imgE.RenderControl(writer);
writer.Write(" ");
//Imagen para cancelar
Image imgC = new Image();
if (!string.IsNullOrEmpty(CancelImageUrl))
imgC.ImageUrl = CancelImageUrl;
else ...
Otra de las cosas que tenemos que tener muy encuenta es guardar correctamente el viewState del control. Para eso las propiedades públicas las vamos guardando en una variable privada que es un StateBag ( guarda el stado de vista de los controles Asp.net ).
Luego cuando el control guarda el viewState utilizaremos la clase Pair para guardar el estado de las propiedades base del control y las nuevas propiedades que nosotros hemos generado. Y al recuperar el estado actuamos a la inversa para que de esta manera se mantenga es estado completo del control.
StateBag _viewState;
private StateBag ViewState
{
get{
if (this._viewState == null)
{
this._viewState = new StateBag();
if (!((IStateManager)this._viewState).IsTrackingViewState)
((IStateManager)this._viewState).TrackViewState();
}
return this._viewState;
}
}
protected override object SaveViewState()
{
Pair state = new Pair();
state.First = base.SaveViewState();
if (this._viewState != null)
state.Second = ((IStateManager)this._viewState).SaveViewState();
return state;
}
protected override void LoadViewState(object state)
{
if (state == null)
base.LoadViewState(state);
else
{
Pair p = (Pair)state;
base.LoadViewState(p.First);
((IStateManager)this.ViewState).LoadViewState(p.Second);
}
}
Para finalizar con la generación de este control solo nos faltaría destacar los recursos embebidos como las imágenes y el fichero Javascript
[assembly: System.Web.UI.WebResource("TreeviewEditControl.Resources.disk_blue.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("TreeviewEditControl.Resources.disk_blue_error.gif", "img/gif")]
[assembly: System.Web.UI.WebResource("TreeviewEditControl.Resources.ItemEdit.gif", "img/gif")]
namespace TreeviewEditControl
La función más importante del fichero javascript es la que genera el postback al servidor y envía el identificador del control y una array de argumentos que utilizaremos para recuperar los datos del nodo editado.
function viewNode( control, txtID, args )
{
var txt = document.getElementById( txtID );
if( txt != null)
__doPostBack( control, args + "|" + txt.value );
}
Una vez finalizado el control lo utilizaremos en una página web y podemos recuperar los datos con el evento que creamos en el TreeView.
protected void TreeViewEdit1_TreeNodeEdit(object sender, TreeNodeEditEventArgs e)
{
//Guardar Datos
bool check = e.Checked;
string Nombre = e.NodoText;
string ID = e.NodoValue;
string valorInicial = e.OldValue;
string nuevoValor = e.NewValue;
}
Bueno yo creo que ya ha quedado claro como se ha creado el control, si tenéis dudas y queréis probar el código os lo dejo para que os lo podáis bajar.
En más de una ocasión nos ha sido de mucha utilidad un control del tipo TreeView para mostrar una jerarquía de datos, pero el control se nos puede quedar un poco limitado cuando pretendemos interactuar con los datos.
Por ese motivo he creado un control TreeView con nodos editables, y el resultado final es como este:
Comenzaremos creando un proyecto de librerías de clase para albergar nuestro control, a la clase le llamaremos TreeViewEdit y heredaremos de System.Web.UI.WebControls.TreeView.
[ToolboxData("<{0}:TreeViewEdit runat=server></{0}:TreeViewEdit>")]
public class TreeViewEdit : TreeView
Realmente toda la potencia del control no se encuentra en el TreeView, sino en los Nodos que le daremos la posibilidad de ser editados. En esta clase lo único que tenemos que hacer es un nuevo evento para capturar la información modificada del nodo.
Para eso crearemos un nuevo evento llamado TreeNodeEdit.
[Category("Data")]
public event TreeNodeEditEventHandler TreeNodeEdit;
protected virtual void OnTreeNodeEdit(TreeNodeEditEventArgs e)
{
if (TreeNodeEdit != null)
{
TreeNodeEdit(this, e);
}
}
Si os fijáis nuestro evento se basa en un delegado personalizado TreeNodeEditEventHandler y TreeNodeEditEventArgs para customizar el conjunto de argumentos que utilizaremos para devolver los datos del nodo modificado.
El delegado:
public delegate void TreeNodeEditEventHandler(object sender, TreeNodeEditEventArgs e);
La clase que utilizaremos para nuestros argumentos:
public class TreeNodeEditEventArgs : System.EventArgs
{
#region Variables
private string _nodoText;
private string _nodoValue;
private string _oldValue;
private string _newValue;
private bool _checked;
#endregion Variables
#region Propiedades
public string NodoText { get { return _nodoText; } }
public string NodoValue { get { return _nodoValue; } }
public string OldValue { get { return _oldValue; } }
public string NewValue { get { return _newValue; } }
public bool Checked { get { return _checked; } }
#endregion Propiedades
#region Constructor
public TreeNodeEditEventArgs(string nodotext, string nodoValue,
string oldValue, string newValue, bool check)
{
this._nodoText = nodotext;
this._nodoValue = nodoValue;
this._oldValue = oldValue;
this._newValue = newValue;
this._checked = check;
}
#endregion Constructor
}
Gracias a esta clase cuando consumamos el evento del TreeView podremos recuperar los datos:
bool Check = e.Checked;
string Nombre = e.NodoText;
string ID = e.NodoValue;
string valorInicial = e.OldValue;
string NuevoValor = e.NewValue;
Finalmente para tener finalizada la primera parte del control tan solo nos queda sobrescribir dos eventos:
-
RaisePostBackEvent para procesar los eventos procesados por el control al enviar los datos al servidor.
protected override void RaisePostBackEvent(string eventArgument)
{
base.RaisePostBackEvent(eventArgument);
string[] args = eventArgument.Split('|');
if (args.Length > 0)
{
if (args[0] == "EditNode")
{
TreeNodeEdit node = (TreeNodeEdit)this.FindNode(args[1].ToString());
TreeNodeEditEventArgs arg = new TreeNodeEditEventArgs(node.Text,
node.Value,
node.OldValue,
args[2].ToString(),
node.Checked);
//Guardamos la modificación en el estado del control y lanzamos el evento correcto
node.TextBoxValue = args[2].ToString();
OnTreeNodeEdit(arg);
}
}
}
Esperamos una colección de argumentos separado por el caracter '|' que envía el cliente al lanzar el postback.
-
Comprobamos que los argumentos que esperamos son realmente de edición (args[0] == "EditNode")
-
El segundo argumento es el Path del nodo que hemos editado.
-
Creamos un TreeNodeEditEventArgs con los datos recuperados del nodo.
-
Invocamos el evento TreeNodeEdit a través del método OnTreeNodeEdit, con el argumento correctamente rellenado para que quien consuma nuestro evento pueda interactuar con los datos modificados.
- OnPreRender para registrar el fichero Javascript que utilizaremos como recurso incrustado y darle una funcionalidad más ligera al interactuar con el cliente.
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
this.Page.ClientScript.RegisterClientScriptResource(GetType(), "TreeviewEditControl.Resources.JS.js");
}
Me parece que para ser la primera parte es suficiente, en el próximo artículo entraremos a fondo con la clase TreeNodeEdit que es la clase que representan los nodos del TreeView.
Hoy una consulta en el foro MSDN me ha parecido muy interesante y por ese motivo y porque la explicación es un poco larga, describiré más detalladamente como poder hacer lo que Roberto Corona necesita.
http://forums.microsoft.com/MSDN-ES/ShowPost.aspx?PostID=3843776&SiteID=11
En resumen Roberto está creando un control de servidor del cual pretende que una propiedad muestre los botones que se encuentren en el formulario para poderlo seleccionar, al etilo de un validador.

Para hacer esto crearé un proyecto web para probar el control y otro proyecto que será el propio control de servidor. La clase del control la llamaré ServerControl1.cs, ya lo sé, muy poco original !!
El control es realmente sencillo:
Nuestro control heredará de WebControl y crearemos la propiedad ControlID para guardar el identificador del botón que queremos controlar
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
[DefaultValue(""), TypeConverter(typeof(ButtonConverter))]
public string ControlID
{
get
{
object o = ViewState["ControlID"];
if (o == null)
return String.Empty;
return (string)o;
}
set
{
if (ControlID != value)
ViewState["ControlID"] = value;
}
}
protected override void RenderContents(HtmlTextWriter output)
{
if(String.IsNullOrEmpty(ControlID))
output.Write("Tienes que seleccionar un Botón");
else
output.Write(ControlID);
}
}
Lo destacable de este código es la clase ButtonConverter, que es la que se encargará de devolvernos los controles button del formulario para poderlo elegir desde el control, esta clase esta basada en la clase ControlIDConverter.
http://msdn.microsoft.com/es-es/library/system.web.ui.webcontrols.controlidconverter(VS.80).aspx
La clase ControlIDConverter se deriva de la clase StringConverter y proporciona una lista de id. de control para su presentación en un control de cuadrícula de propiedades en entornos en tiempo de diseño. La clase ControlIDConverter también actúa como clase base para las clases AssociatedControlConverter y ValidatedControlConverter, que son los convertidores de tipos para los controles Web y los controles que admiten atributos de propiedades de validación, respectivamente.
class ButtonConverter : StringConverter
protected virtual bool FilterControl(Control control)
private string[] GetControls(IDesignerHost host, object instance)
{
IContainer container = host.Container;
IComponent component = instance as IComponent;
if ((component != null) && (component.Site != null))
{
container = component.Site.Container;
}
if (container == null)
{
return null;
}
ComponentCollection components = container.Components;
ArrayList list = new ArrayList();
foreach (IComponent component2 in components)
{
Control control = component2 as Control;
if(control.GetType().Name == typeof(Button).Name)
{
list.Add(control.ID);
}
}
list.Sort(Comparer.Default);
return (string[])list.ToArray(typeof(string));
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
El método a destacar es GetControl que es el que recupera la lista de IDs de los botones y el efecto final es exactamente como el de ControlToValidate de los validadores de Asp.NET.

Ahora ya tenemos nuestra propiedad extendida para utilizar nuestro control de la manera que más nos guste.
Os dejo el proyecto de ejemplo. 
Desde ayer que me están bombardeando con el nuevo navegador de Google, lo has probado? lo has probado?.
Pues para demostrar que no todo en la programación no es ni blanco ni negro, aqui tenéis el primer agujero de seguridad y además es un fallo garrafal de los desarrolladores por dejadez. No todo siempre es culpa de Microsoft ;-)
Este nuevo e innovador navegador está basado en Webkit de Apple, pero no se han molestado ni en implementar el parche que el mismo Apple publico para resolver este problema de seguridad.
Y ya van dos errores...
http://www.kriptopolis.org/
http://es.wikipedia.org/wiki/WebKit
Suerte que es una versión Beta. ;-)
Nos vemos.