ASP.NET

[Asp.net] Editable Treeview II

· por Marc Rubiño

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.

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.

#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
   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.