Apaixonado por tecnologia. Trabalho com tecnologia desde 2003.

Saiba mais sobre minha vida profissional aqui .

Fale comigo.
Siga-me no Twitter
Ultimos comentários
Calendário de Posts
<<  setembro 2017  >>
stqqssd
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

Siga o @DicaDoNerd no Twitter e acompanhe as novidades


Depois de apanhar um pouco com o evento SelectedNodeChanged do TreeView do ASP.NET descobri que o fato de você clicar no checkbox não dispara o evendo para o servidor. Procurei um pouco e encontrei este site que funcionou perfeitamente.

Publiquei este artigo que demostra como preencher um TreeView de forma recursiva utilizando LinqToSql, como este não é o foco, não vou entrar em detalhes sobre o preenchimento.

Primeiro vamos montrar a estrutura para exclusão, neste artigo é possível entender as propriedades ObjectTrackingEnabled e DeferredLoadingEnabled. Para a estrutura do Context utilizei uma técnica bem interessante deste livro (que aliás é excelente) que foi originado deste site

image Criei uma tabela de segmentos que utiliza uma recursividade (ParentID) para preechimento e o grande problema era excluir os dados no TreeView.

Criei uma solução bastante simples para este problema. Habilitei o Checkbox no TreeView e passo uma lista de Ids que serão excluídas.

O primeiro passo é habilitar o TreeView e criar um botão para excluir os itens selecionados.

 
 
 
 
 
 
<asp:LinkButton ID="lk" runat="server" OnClick="Excluir" Text="Excluir" />
<asp:TreeView ID="tv" ShowCheckBoxes="All" runat="server" />

image
HTML Resultado.

 

Agora vamos incluir o javascript no projeto que será resposável para selecionar os checkboxs.

<script type="text/javascript">
function OnCheckBoxCheckChanged(evt) {
var src = window.event != window.undefined ? window.event.srcElement : evt.target;
var isChkBoxClick = (src.tagName.toLowerCase() == "input" && src.type == "checkbox");
if (isChkBoxClick) {
var parentTable = GetParentByTagName("table", src);
var nxtSibling = parentTable.nextSibling;
if (nxtSibling && nxtSibling.nodeType == 1)//check if nxt sibling is not null & is an element node
{
if (nxtSibling.tagName.toLowerCase() == "div") //if node has children
{
//check or uncheck children at all levels
CheckUncheckChildren(parentTable.nextSibling, src.checked);
}
}
//check or uncheck parents at all levels
CheckUncheckParents(src, src.checked);
}
}
function CheckUncheckChildren(childContainer, check) {
var childChkBoxes = childContainer.getElementsByTagName("input");
var childChkBoxCount = childChkBoxes.length;
for (var i = 0; i < childChkBoxCount; i++) {
childChkBoxes[i].checked = check;
}
}
function CheckUncheckParents(srcChild, check) {
var parentDiv = GetParentByTagName("div", srcChild);
var parentNodeTable = parentDiv.previousSibling;

if (parentNodeTable) {
var checkUncheckSwitch;

if (check) //checkbox checked
{
var isAllSiblingsChecked = AreAllSiblingsChecked(srcChild);
if (isAllSiblingsChecked)
checkUncheckSwitch = true;
else
return; //do not need to check parent if any(one or more) child not checked
}
else //checkbox unchecked
{
checkUncheckSwitch = false;
}

var inpElemsInParentTable = parentNodeTable.getElementsByTagName("input");
if (inpElemsInParentTable.length > 0) {
var parentNodeChkBox = inpElemsInParentTable[0];
parentNodeChkBox.checked = checkUncheckSwitch;
//do the same recursively
CheckUncheckParents(parentNodeChkBox, checkUncheckSwitch);
}
}
}
function AreAllSiblingsChecked(chkBox) {
var parentDiv = GetParentByTagName("div", chkBox);
var childCount = parentDiv.childNodes.length;
for (var i = 0; i < childCount; i++) {
if (parentDiv.childNodes[i].nodeType == 1) //check if the child node is an element node
{
if (parentDiv.childNodes[i].tagName.toLowerCase() == "table") {
var prevChkBox = parentDiv.childNodes[i].getElementsByTagName("input")[0];
//if any of sibling nodes are not checked, return false
if (!prevChkBox.checked) {
return false;
}
}
}
}
return true;
}
//utility function to get the container of an element by tagname
function GetParentByTagName(parentTagName, childElementObj) {
var parent = childElementObj.parentNode;
while (parent.tagName.toLowerCase() != parentTagName.toLowerCase()) {
parent = parent.parentNode;
}
return parent;
}

</script>

Pronto! Camada de apresentação pronta, vamos preparar a estruta que será responsável pela exclusão.

 

image Anexando a classe no dbml (LinqToSql)

Tem um artigo que demonstra como criar o objeto relacional com o Linq.

 

Crie uma classe DBHelper.

public static class DbHelper
{
public static dbDadosDataContext getContextData(bool ObjectTrackingEnabled)
{
var db = getContextData();
db.ObjectTrackingEnabled = ObjectTrackingEnabled;
return db;
}

public static dbDadosDataContext getContextData(bool ObjectTrackingEnabled, bool DeferredLoadingEnabled)
{
var db = getContextData();
db.ObjectTrackingEnabled = ObjectTrackingEnabled;
db.DeferredLoadingEnabled = DeferredLoadingEnabled;
return db;

}

public static dbDadosDataContext getContextData()
{
//Colocar a connectionString no web.Config
string strCnn = ConfigurationManager.ConnectionStrings["cnnConnectionString"].ConnectionString;
var db = new dbDadosDataContext(strCnn);
db.DeferredLoadingEnabled = false;
db.ObjectTrackingEnabled = false;
return db;
}

}

Agora vamos criar o método responsável pela exclusão.

 

public class Segmento : IDisposable
{
private dbDadosDataContext db;
public Segmento()
{
db = DbHelper.getContextData();
}

public void ExcluirSegmentos(List<int> listSegments)
{
db.ObjectTrackingEnabled = true; // Habilito o tracking por haver manipulação no BD.
var segs = db.SegmentoEntidades.Where(p => listSegments.Contains(p.IDSegmento));
db.SegmentoEntidades.DeleteAllOnSubmit(segs);
db.SubmitChanges();

}

public void PreencherTreeViewSegmento(TreeView objTreeView, int? IDNoCarregar)
{
//A vantagem de utilizar o IQuarable é que a execução do Banco só será realizada no Bind.
IQueryable<SegmentoEntidade> seg = db.SegmentoEntidades
.Where(p => p.Inativo == false)
.OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento);

//Limpo os nodes existentes.
objTreeView.Nodes.Clear();

var itens = seg.Where(p => (p.ParentID == null ? 0 : p.ParentID) == (IDNoCarregar == null ? 0 : IDNoCarregar))
.OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento);

//passo por todos os nodes que foi passado como parâmetro para carregar. (organizando em ordem alfabética
foreach (var item in itens)
{
//Preencho o nó pai
TreeNode nodePai = new TreeNode();
nodePai.Value = item.IDSegmento.ToString();
nodePai.Text = item.Segmento;
nodePai.ImageToolTip = item.Segmento;
//nodePai.ImageUrl = _db.IconeEntidades.Where(p => p.IDIcone == item.IDIcone).FirstOrDefault().urlImagem;
//Preecho os filhos
PreencherNoFilho(nodePai, seg);

//Adiciono o nó que foi preenchido
objTreeView.Nodes.Add(nodePai);
}

}
protected void PreencherNoFilho(TreeNode parentNode, IQueryable<SegmentoEntidade> segmentos)
{
//Passo por todos os elementos filhos do nó pai
var itens = segmentos.Where(p => p.ParentID == int.Parse(parentNode.Value)).OrderBy(p => p.ParentID)
.OrderBy(p => p.Segmento)

foreach (var item in itens)
{

TreeNode noFiltro = new TreeNode();
noFiltro.Value = item.IDSegmento.ToString();
noFiltro.Text = item.Segmento;
noFiltro.SelectAction = TreeNodeSelectAction.SelectExpand; //Expando no nó.
parentNode.ChildNodes.Add(noFiltro); //adiciono na coleção de nós
PreencherNoFilho(noFiltro, segmentos); //Preenchemos os filhos deste Node (recursividade)
}


}

~Segmento()
{
this.Dispose();
}

#region IDisposable Members

public void Dispose()
{
GC.Collet();
GC.SuppressFinalize(this);
}

#endregion
}

Agora vamos criar os métodos na camada de apresentação.

Page_Load.

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Preencho o controle treeview
FillControls(); //
//atribuimos o evento click ao checkbox
tv.Attributes.Add("onclick", "OnCheckBoxCheckChanged(event)");

}

}

O método responsável para

Preencher o controle TreeView.

protected void FillControls()
{

using (Segmento seg = new Segmento())
{
seg.PreencherTreeViewSegmento(tv, null);

}

}

Agora vamos criar o método que irá excluir.

protected void Excluir(object sender, EventArgs e)
{
List<int> lista = new List<int>();
//Preencho a lista com os Nodes selecionados.

foreach (TreeNode item in tv.CheckedNodes)
lista.Add(int.Parse(item.Value));

using (Negocio.Cliente.Segmento seg = new Negocio.Cliente.Segmento())
{
seg.ExcluirSegmentos(lista);
}
FillControls();
}

Pronto, de uma forma bem simples e estruturada preenchemos e excluimos itens com checkbox.

Espero que tenham gostado.

Sigam o @DicaDoNerd no Twitter.

     Neste artigo vou demonstrar uma forma muito simples de criar uma TreeView, funcional com manutenção simples e com um estilo legal. Para criar esta TreeView utilizei LinqToSql que facilita muito a vida de qualquer programador. O primeiro passo é criar duas tabelas: Uma delas irá conter as url’s com as imagens de cada nó e outra o cadastro do nó (TreeNode).

Diagrama do Banco de Dados
Banco  
Diagrama das Classes (DataContext)  
Datacontext  

Script para criação do Banco de dados

USE [dbTreeView]
GO
/****** Object: Table [dbo].[tbIcones] Script Date: 05/02/2010 01:17:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tbIcones](
[IDIcone] [int] IDENTITY(1,1) NOT NULL,
[Descricao] [varchar](20) NOT NULL,
[urlImagem] [varchar](50) NOT NULL,
CONSTRAINT [PK_tbIcones] PRIMARY KEY CLUSTERED
(
[IDIcone] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: Table [dbo].[tbTreeView] Script Date: 05/02/2010 01:17:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[tbTreeView](
[IDNo] [int] IDENTITY(1,1) NOT NULL,
[Alias] [varchar](20) NOT NULL,
[ParentID] [int] NOT NULL,
[IDIcone] [int] NOT NULL,
CONSTRAINT [PK_tbTreeView] PRIMARY KEY CLUSTERED
(
[IDNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: ForeignKey [FK_tbTreeView_tbIcones] Script Date: 05/02/2010 01:17:11 ******/
ALTER TABLE [dbo].[tbTreeView] WITH CHECK ADD CONSTRAINT [FK_tbTreeView_tbIcones] FOREIGN KEY([IDIcone])
REFERENCES [dbo].[tbIcones] ([IDIcone])
GO
ALTER TABLE [dbo].[tbTreeView] CHECK CONSTRAINT [FK_tbTreeView_tbIcones]
GO

Tudo gerado, mãos a obra.

     Para preencher os nós e seus respectivos filhos teremos que utilizar recursividade no preenchimento dos TreeNode’s, vamos criar um método que recebe um objeto TreeView e o ID do nó como parâmetro que começaremos a preencher.

     No contrutor da classe Preenchimento(), atribuo coloco em memória os dados que utilizaremos (lista de ícones e Lista de nós).

private dbTreeViewDataContext _db;
private List<IconeEntidade> _icones;
private List<TreeViewEntidade> _TreeView;
public Preenchimento()
{
//Carrego tudo na memória para fazer as iterações
_db = new dbTreeViewDataContext();
//Como não faremos nenhum Update,desabilito a segmentação
//para evitar que o linq faça o mapeamento do que está sendo alterado
//com isso ganhamos performance.
_db.ObjectTrackingEnabled = false;
//Coloco em memória a lista de icones.
_icones = _db.IconeEntidades.ToList<IconeEntidade>();
//Coloco em memória a lista que contem os nós que será preenchida
_TreeView = _db.TreeViewEntidades.ToList<TreeViewEntidade>();

}

Agora vamos preencher os TreeNode’s pai e para cada TreeNode, preenchemos seus filhos.

public void PreencherTreeView(System.Web.UI.WebControls.TreeView objTreeView, int IDNoCarregar)
{
//Limpo os nodes existentes.
objTreeView.Nodes.Clear();

//passo por todos os nodes que foi passado como parâmetro para carregar. (organizando em ordem alfabética
foreach (var item in _TreeView.Where(p => p.ParentID == IDNoCarregar).OrderBy(p => p.ParentID).OrderBy(p => p.Alias))
{
//Preencho o nó pai
TreeNode nodePai = new TreeNode();
nodePai.Value = item.IDNo.ToString();
nodePai.Text = item.Alias;
nodePai.ImageToolTip = item.Alias;
nodePai.ImageUrl = _db.IconeEntidades.Where(p => p.IDIcone == item.IDIcone).FirstOrDefault().urlImagem;
//Preecho os filhos
PreencherNoFilho(nodePai);

//Adiciono o nó que foi preenchido
objTreeView.Nodes.Add(nodePai);
}

}

Para preencher os TreeNode’s filhos, recebemos o TreeNode pai (parent).

protected void PreencherNoFilho(TreeNode parentNode)
{
//Passo por todos os elementos filhos do nó pai
foreach (var item in _TreeView.Where(p => p.ParentID == int.Parse(parentNode.Value)).OrderBy(p => p.ParentID).OrderBy(p => p.Alias))
{

TreeNode noFiltro = new TreeNode(item.Alias,item.IDNo.ToString(),
_icones.Where(p => p.IDIcone == item.IDIcone).FirstOrDefault().urlImagem); //Adiciono a url da imagem
noFiltro.ImageToolTip = item.Alias; // Atribuo o texto que será exibido
noFiltro.SelectAction = TreeNodeSelectAction.SelectExpand; //Expando no nó.
parentNode.ChildNodes.Add(noFiltro); //adiciono na coleção de nós
PreencherNoFilho(noFiltro); //Preenchemos os filhos deste Node (recursividade)
}
}

Nossa estrutura pronta, vamos partir para a formatação.

Continua - Parte II

Continuando este artigo

     Vamos criar o arquivo de estilo da TreeView. (Estilo.css)

.TreeView{height:50px;color: #333333;position:absolute;clear: left;}
.TreeView p {border: 1px solid #999999;height: 15px;width: 250px;color: #666666;font-family: verdana;font-size: 9px;font-weight: bold;font-style: normal;background-color: #F7F7F7;padding: 5px 3px 3px 10px;}
.EstiloNo{font-family: Verdana; color: #333333; font-size: 10px; padding: 2px; margin-left: 2px;}
.EstiloNoCabecalho{ font-family: Verdana;color: #333333;font-size: 10px;padding: 2px;margin-left: 2px;font-weight: bold;}
.EstiloNoSelecionado{font-family: Verdana;color: #333333;font-size: 10px;padding: 2px;margin-left: 2px;font-style: italic;font-weight: bold;}


     Vamos criar o HTML, vejam que desabilitei o SessionState EnableSessionState”, claro que em um item como este não faz uma diferença significativa, mas, por boa prática, iremos desabilita-lo. Um dos grandes problemas com performance em projetos web é a habilitação de recursos que nunca são utilizados. (ViewState, SessionState e etc). Como a TreeView utiliza ViewState, deixei habilitado.

     Veja também, que ao utilizar o Ajax, habilitei no ScriptMananger a propriedade: EnablePartialRendering="true";

     No update panel, setei o modo de update para UpdateMode="Always"

     Por fim,  setei o objeto TreeView (AsyncPostBackTrigger) em uma Trigger dentro do UpdatePanel.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Minato.Exemplos.TreeView._Default" EnableSessionState="False" EnableViewState="true" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Exemplo treeView Recursiva - Alexandre Minato</title>
<link href="App_Themes/Estilo.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form id="frmPrincipal" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<div class="TreeView">
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
<ContentTemplate>
<p><asp:Label ID="lblMensagem" runat="server" Text="Selecione um nó" /></p>
<asp:TreeView ID="objTreeView" runat="server" onselectednodechanged="objTreeView_SelectedNodeChanged">
<RootNodeStyle CssClass="EstiloNoCabecalho" />
<SelectedNodeStyle CssClass="EstiloNoSelecionado" />
<LevelStyles>
<asp:TreeNodeStyle CssClass="EstiloNo" />
</LevelStyles>
<NodeStyle CssClass="EstiloNo" />
</asp:TreeView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="objTreeView" />
</Triggers>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
="true" />
<div class="TreeView">
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Always">
<ContentTemplate>
<p><asp:Label ID="lblMensagem" runat="server" Text="Selecione um nó" /></p>
<asp:TreeView ID="objTreeView" runat="server" onselectednodechanged="objTreeView_SelectedNodeChanged">
<RootNodeStyle CssClass="EstiloNoCabecalho" />
<SelectedNodeStyle CssClass="EstiloNoSelecionado" />
<LevelStyles>
<asp:TreeNodeStyle CssClass="EstiloNo" />
</LevelStyles>
<NodeStyle CssClass="EstiloNo" />
</asp:TreeView>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="objTreeView" />
</Triggers>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>

     Vamos colocar a funcionalidade no CodeBehind e pronto!!! Tudo funcionando.

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
PreencherTreeView();

}

protected void PreencherTreeView()
{
using (Preenchimento preenchimento = new Preenchimento())
{
preenchimento.PreencherTreeView(objTreeView, 0);

}

}

protected void objTreeView_SelectedNodeChanged(object sender, EventArgs e)
{
lblMensagem.Text = String.Format("Nó selecionado: {0}", objTreeView.SelectedNode.Text);

}

Agora vamos ver o resultado final desta implementação

tv 

 Demo onLine

Abraços

Minato 8-)

Baixem a Solução

 
teste