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.