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
<<  novembro 2017  >>
stqqssd
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

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.

Que Jquery é uma bibliocate que facilita muito a vida do programador, isso ninguém duvida, mas, uma coisa que as vezes temos problemas e entender os plugins que são criados. Quando entendemos colocamos a mão na cabeça e dizemos “Como sou burro, era só isso!”… Sempre que tenho um problema que não consigo resolver a primeira coisa é pesquisar no Google mas como não gosto simplesmente do “Copy and Paste”, fico tentando entender bibliotecas, refatorando dll’s, estudando alguns frameworks, então, lá vai mais uma dica.

Vou utilizar neste exemplo o plugin masked input.

Existe uma forma bem simples de criar, basta criar um estilo (css) para seu Input (asp:TextBox)

.campoFormData {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCEP {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormTel {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormDDD {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCPF {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormCNPJ {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}
.campoFormD2 {font-family: Arial, Helvetica, sans-serif;font-size: 12px;font-weight: normal;color: #FFFFFF;text-decoration: none;background-color: #666666;border: 0px solid #666666;}

Criamos classes para cada Tipo de formatação (vale lembrar que isso será utilizado no Selector ($) do Jquery).

Feito isso, vamos para o HTML.

<asp:TextBox ID="txtCPF" runat="server" MaxLength="15" CssClass="campoFormCPF"  />
<asp:TextBox ID="txtCEP" runat="server" CssClass="campoFormCEP" MaxLength="9" />
<asp:TextBox ID="txtTelefone" runat="server" MaxLength="10" CssClass="campoFormTel" />
<asp:TextBox ID="txtCelularDDD" runat="server" MaxLength="2" CssClass="campoFormDDD" Width="20px" />
<asp:TextBox ID="txtCelular" runat="server" MaxLength="10" CssClass="campoFormTel" />

Agora vamos referenciar a biblioteca do Jquery.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://cloud.github.com/downloads/digitalBush/jquery.maskedinput/jquery.maskedinput-1.3.min.js">
</script>

Apenas lembrando para quem utiliza o Visual Studio 2010 (Framework 4.0) é possível habilitar a biblioteca do Jquery (sem precisar fazer a referência do script) habilitando a propriedade EnableCdn (Content Delivery Network). Neste link você encontra algumas dicas bem bacanas sobre algumas novidades e aqui você encontra as bibliotecas disponíveis no Cdn.

<asp:ScriptManager ID="smMaster" runat="server" EnablePartialRendering="true" EnableCdn="true" />

Agora vamos criar o script que será responsável por executar o script.

function AtribuirMascaras() {

$(".campoFormD2").unmask();
$(".campoFormD2").mask("99");

$(".campoFormData").unmask();
$(".campoFormData").mask("99/99/9999");

$(".campoFormCEP").unmask();
$(".campoFormCEP").mask("99999-999");

$(".campoFormTel").unmask();
$(".campoFormTel").mask("9999-9999");

$(".campoFormDDD").unmask();
$(".campoFormDDD").mask("99");

$(".campoFormCPF").unmask();
$(".campoFormCPF").mask("999.999.999-99");

$(".campoFormCNPJ").unmask();
$(".campoFormCNPJ").mask("99.999.999/9999-99");

}

Com a função pronta, vamos executar:

<script type="text/javascript">
$(document).ready(function () {
AtribuirMascaras();
});
</script>

Um problema comum para quem utiliza este plugin (Mask) com UpdatePanel é ele se “perder” isso acontece, pelo fato de que foi enviada uma requisição para o servidor e o HTML retornado não tem mais o evento vinculado aos controles.

Postei há algum tempo uma forma bem simples de resolver este problema, basta fazer isso:

<script>
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(registraScript);

function registraScript(sender, args) {
AtribuirMascaras();
}


</script>

Isso faz com que ao final de cada requisição ao servidor o script seja registrado novamente.

Sou um apaixonado por produtividade e inovação. Sempre procuro novas tecnologias, maneiras diferente de fazer a mesma coisa, no entando, isso sempre toma muito tempo este é o motivo de manter este blog. Compartilhar algumas dicas, truques sobre o que adoro fazer.

Há tempos utilizo LinqToSql e acredito que todos os componentes que fazem mapeamento relacional como LinqToSql, EntityFramework, Hibernate, JPA e etc, fazem com que o desenvolvimento reduza pela metade do tempo, desde que, saiba exatamente como usar o framework, neste caso a curva de aprendizado será grande, excepcionalmente se você não for um Nerd que dedica horas da sua vida à pesquisar coisas que talvez nunca use. :)

Você pode utilizar um LinqDataSource como fonte de dados para GridView, FormView, DetailsView.

Para isso você deverá criar um Contexto para o LinqToSql (existem diversos tutoriais na internet, por ser bastante básico, não vou entrar em detalhes por não ser o foco do post).

Criado o Contexto você inclui o objeto LinqDataSource, neste exemplo, simulo um objeto Contexto (dbDataContex) Ordenando por “Nome”, utilizo um objeto anônio (Anonymous Types), em TableName, informo qual a classe (Entidade) que contem o Mapeamente Relacional gerado pelo LinqToSql, por fim vinculamos o evento (linqDsClientes_Selected) que será utilizado na seleção de um item no GridView.

<asp:LinqDataSource ID="linqDsClientes" runat="server"  
ContextTypeName="dbDataContext" OrderBy="Nome"
Select="new (IDCliente, Nome, Email, DataNascimento)"
TableName="ClienteEntidades" onselected="linqDsClientes_Selected">
</asp:LinqDataSource>

Com objeto criado, vamos associar ao gridview, para isso basta na propriedade DataSourceID, informar o ID do objeto LinqDataSource.

<asp:GridView ID="GridView1" runat="server" DataSourceID="linqDsClientes" />
<!--Label que informará quantos registros foram encontrados -->
<asp:Label ID="lblQtdade" runat="server" />

Pronto, assim, já temos vinculado a grid com o controle, agora, vamos manipular as informações.

Para filtrar informações no LinqDataSource (WhereParameters).

linqDsClientes.WhereParameters.Clear(); //Limpa possíveis parâmetros
linqDsClientes.Where = "Nome.Contains(@Nome)"; // Procuro na propriedade Nome o texto que contenha o parâmetro passado
linqDsClientes.WhereParameters.Add("Nome", System.Data.DbType.String, txtNome.Text); // Adiciono os parâmetros.

Agora uma coisa bem simples, que demorei um tempão para descobrir é o count(), que pode ser feito através do Evento OnSelected.

protected void linqDsClientes_Selected(object sender, LinqDataSourceStatusEventArgs e)
{
lblResultado.Text = string.Format("Total de clientes encontrados: {0}", e.TotalRowCount);
}

Certamente ainda há muito o que ser pesquisado sobre este objeto, mas, foi uma pequena contribuição…

Mande sugestão ou dúvida para contato@alexandreminato.com.br

Até a próxima 8-)

Um constante problema ao utilizar algum script de qualquer Framework (Jquery, ExtJs) e colocarmos dentro de um UpdatePanel do ASP.NET o script simplesmente para de funcionar. Isso acontece pelo fato de como uma transação assíncrona ocorrida dentro do UpdatePanel transita apenas o pedaço de código para o servidor.

Existe uma forma bem simples de resolver este problema. Toda vez que é feita uma requisição o Script Mananger controla eventos como Inicio da Requisição (beginRequest), Carregamento do Página (PageLoad) e finalização da requisição (endRequest).

Basta “chamar” o script novamente que o problema estará resolvido. Isso acontece também, quando utilizado apenas um ScriptMananger na Master Page, você pode escolher se deseja controlar a requisição na Master Page ou se deseja controlar em Páginas (ASPX) separadamente.

Vamos lá!

<script>
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(registraScript);

function registraScript(sender, args) {
//Execute aqui seu script.
}


</script>


Resolvi escrever este post, pois, no analytics notei que muitas pessoas que chegam até o meu blog, estão com perguntas como: Update Panel + Jquery ou MasterPage Jquery…. estas são as campeãs de solicitação.

 

Não encontrou o que procurava?  Entre em contato. ;-)

Normalmente sou desconfiado de soluções de auto-complete. Vou realizar um próximo post, que mostra como fazer a mesma coisa utilizando JQuery. Porém a solução do Jquery exige que você coloque todas as informações da consulta no formato Json no momento em que carrega a página, isso pode ser eficiente para poucos registros, para grande quantidades de registros, acredito que o Auto Complete do Ajax Control Toolkit seja mais indicado.

image
Imagem 1

Vou utilizar Linq To Sql para a implementação deste exemplo, pois acredito que é um dos frameworks que trazem maior produtividade em desenvolvimento. Estou utilizando dados e exemplos de um projeto real que está em produção, por isso, os nomes não sejam conhecidos como o Northwind ou Pubs.

Bom, vamos primeiro entender a estrutura.

Criei uma entidade (utilizando o LinToSql). (Imagem 1), assim, teremos acesso aos dados e podemos criar os métodos para acessos aos dados utilizando o mapeamento relacional gerado automaticamente.

Vamos agora criar o método que será utilizado pelo webservice que é necessário para utilização do auto-complete.

using System;
using System.Linq;

namespace PitStop.Dados
{
public class CadastroCliente : IDisposable
{
//Crio a variavel que irá conter o contexto dos Dados.
private dbDataContext dbContext;

/// <summary>
/// Construtor da classe.
/// </summary>
public CadastroCliente()
{
//No construtor, crio a instancia do contexto.
dbContext = new dbDataContext();
}

/// <summary>
/// Retorna clientes que possam atender a consulta
/// </summary>
/// <param name="textoBuscar">Parte do nome do cliente</param>
/// <param name="count">Quantidade de itens que serão retornados</param>
/// <returns>array com nome dos clientes</returns>
public string[] RetornarNomesClientes(string textoBuscar, int count)
{
//Aqui é um pulo do gato, como vamos apenas fazer a consulta, desabilitamos o tracking que por padrão fica habilitado.
//Funciona apenas para consulta, qualquer ação que necessite que o estado seja salvo (Update, Insert, Delete) é necessário habilitar.
dbContext.ObjectTrackingEnabled = false;

return dbContext.ClienteEntidades
.Where(p => p.Inativo == false && p.Nome.ToLower().Contains(textoBuscar)) // Filtro clientes ativos e que contenham o texto.
.Select(m => m.Nome) // não há necessidade da entidade inteira, então, retorno apenas o nome
.Take(count) // retorno apenas a quantidade quantidade necessária
.ToArray(); // Converto o resultado para um array.
}


// Fecho o que pode estar aberto.
#region IDisposable Members

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

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

#endregion
}
}

Agora, vamos criar o WebService que será chamado pelo componente do AutoCompleteExtender. Clique com o Botão direito e selecione o item “WebService”.

image

Ao adicionar o WebService, vamos criar o método responsável por invocar o método que criamos nos passos anteriores.

using System.Web.Services;
using PitStop.Dados;

namespace PitStop.Web.webService
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class AutoComplete : System.Web.Services.WebService
{
public AutoComplete()
{
}

/// <summary>
/// Retorna dados para o autoComplete
/// </summary>
/// <param name="prefixtext">Texto que será pesquisado</param>
/// <param name="count">Quantidade de registros que serão retornados</param>
/// <returns>array com nome dos clientes</returns>
[WebMethod]
public string[]RetornarClientes(string prefixtext, int count)
{
using (CadastroCliente cad = new CadastroCliente())
{
return cad.RetornarNomesClientes(prefixtext, count);
}

}
}

}

Pronto, estrutura pronta agora vamos preparar a interface, que consumirá a informação

Em uma nova página aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="TesteComplete.aspx.cs" Inherits="PitStop.Web.TesteComplete" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<!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>Teste Auto Complete</title>
<style type="text/css">
.autocomplete_list {visibility:visible;margin:0px!important;padding:1px;background-color:GrayText;color:Black;
border:buttonshadow;border-width:1px;border-style:solid;cursor:default;text-align:left;list-style-type:none;
font-weight:normal;font-family:Verdana;font-size:10px;}
.autocomplete_highlighted_listitem {background-color: GrayText;color:Black;padding:3px;}
.autocomplete_listitem {background-color:Window;color:Gray;font-family: Verdana;font-size: 10px;padding:3px;}

</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ToolkitScriptManager ID="sm" runat="server" />
<asp:AutoCompleteExtender ID="ace" runat="server"
EnableCaching="true"
MinimumPrefixLength="1"
TargetControlID="txtNome"
ServicePath="~/webService/AutoComplete.asmx"
ServiceMethod="RetornarClientes"
CompletionInterval="10"
CompletionSetCount="10"
CompletionListCssClass="autocomplete_list"
CompletionListItemCssClass="autocomplete_listitem"
CompletionListHighlightedItemCssClass="autocomplete_highlighted_listitem">

</asp:AutoCompleteExtender>
<asp:TextBox ID="txtNome" runat="server" CssClass="campoForm" Width="400px" autocomplete="off" />

</div>
</form>
</body>
</html>

Vale algumas dicas:

CompletionSetCount = 10 –> Indica a quantidade de registros que serão exibidos na lista do auto-complete.

EnableCaching = true –> Guarda as ultimas consultas realizadas.

MinimumPrefixLength = 1 –> Indica a partir de quantos caracteres digitados será realizado o auto-complete (o ideal seria 3 a 5)

CompletionListCssClass, CompletionListItemCssClass e CompletionListHighlightedItemCssClass você consegue alterar o estilo (css) do auto-complete.

Uma coisa legal é autocomplete="off" que utilizei no TextBox (txtNome) isso indica que não será utilizado o autocomplete do browser, pois, isso pode confundir o usuário.

Vamos ver o resultado?

image

Espero que tenham gostado... Dúvidas ou sugestões de artigos entrem em contato ;-).

Até a próxima.

Um Grande problema que temos é o trabalho de ficar criando grid com funcionalidades CRUD (Create, Reade, Update e Delete), com este componente e algumas adaptações fica tudo mais fácil.

Um modelo de uma grid personalizada. Notem que na coluna “Descrição” há um marcador vermelho, isto está indicando que foi realizada uma alteração que ainda não foi salva (o mais fantástico é que basta um duplo clique na coluna que ela fica habilitada para edição).

A coluna vencimento é do tipo Data, assim, é possível exibir o DatePicker de uma forma muito simples. Para cada coluna há um “editor. Notem que tudo pode ser configurado (no meu site todas as mensagens estão em portugues, mas, é possível configurar para que recupere a linguagem do cliente e com trabalho mínimo, exibir mensagens em Inglês, Português ou qualquer outro idioma.

Tela de Apresentação da Grid.

Vou colocar o conteúdo HTML necessário para gerar as colunas e editores e alguns itens interessantes, como, Regex para limitar caracteres, agrupadores, enfim, muita coisa bacana, para quem quiser trocar informações, entrem em contato através do email:contato@alexandreminato.com.br

<ColumnModel ID="colModel" runat="server">

<Columns>

<ext:Column ColumnID="IDLancamento" DataIndex="IDLancamento" Header="ID" Width="25" Sortable="true" Hidden="true" />

<ext:Column DataIndex="Vencimento" Header="Vencimento" Width="100" Align="Center" Sortable="true">

<Editor>

<ext:DateField ID="txtVencimento" runat="server" Sortable="true" />

</Editor>

<Renderer Fn="Ext.util.Format.dateRenderer('d/m/Y')" />

</ext:Column>

<ext:Column DataIndex="Descricao" Header="Descricao" Width="210" Sortable="true">

<Editor>

<ext:TextField ID="txtDesc" runat="server" />

</Editor>

</ext:Column>





<ext:GroupingSummaryColumn

ColumnID="Valor"

Header="Valor"

Sortable="true"

DataIndex="ValorPrevisto"

Hideable="false"

SummaryType="Sum">

<Renderer Fn="FormataCor" />

<SummaryRenderer Fn="FormataCor" />

<Editor>

<ext:TextField ID="TextField2" runat="server" MaskRe="/[0-9\,]/" DataIndex="ValorPrevisto" />

</Editor>

</ext:GroupingSummaryColumn>

<ext:CommandColumn ColumnID="Quitar" Width="40" Header="Quitar">

<Commands>

<ext:GridCommand Icon="Accept" CommandName="Quitar" />

</Commands>

</ext:CommandColumn>

</Columns>

</ColumnModel>

Conheçam um dos melhores componentes OpenSource. www.coolite.com

Até a próxima 8-)

Minato

     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

          Surgiu a necessidade em um projeto de criar uma estrutura para criar filtrar entidades dinâmicamente com C#. A idéia era criar uma estrutura onde qualquer desenvolvedor pudesse utilizar a estrutura em qualquer lugar, desde que, a entidade estivesse preenchida. Passando muitas horas apanhando, cheguei em uma solução bem viável, tanto no quesito performance quanto no quesito usabilidade. Neste site encontrei bastante informações úteis.

          Hoje, com a evolução do Linq e Expressões lambdas o trabalho no desenvolver reduziu drasticamente. Para filtrar dados em entidade com Linq e Expressão lambdas poderíamos fazer assim:

   1: IList Resultado  = clientes.Where(p => p.Idade > 5).ToList();

           Aí surgiu o desafio, como criar o “Where” e as Expressões dinamicamente:

          Com uma estrutura bem simples, com dois métodos de extensão (um sobrecarregado), onde o usuário pode passar qual a propriedade deseja filtrar, o Tipo de Comparação e o valor que deseja procurar, com isso, Criei também uma classe que pode ser utilizada para executar diversos filtros.

          Como os estrutura utilizada para filtrar (Lst<Filtroentidade>) ficam em um ViewState, pois, há varios controles que implementam a mesma interface, surgiu um problema quando o filtro deveria ser excluído, como não existia um ID (pois os filtros ainda não estavam na base de dados) para cada nova instancia do objeto guardo o hash do objeto para poder excluir. Sempre que um filtro é aplicado, marco a aplicação através da propriedade FiltroAplicado. Assim, as informações não serão filtradas novamente.

Classe que contem a estrutura que utilizada para filtrar a entidade.

   1: public class FiltroEntidade
   2:    {
   3:        public enum TipoComparacao
   4:        {
   5:            Igual,
   6:            Diferente,
   7:            Contem,
   8:            MaiorIgual,
   9:            MenorIgual,
  10:            Maior
  11:  
  12:        }
  13:  
  14:        public int hashCode
  15:        {
  16:            //Atribui o hash de cada item gerado para caso eu precise remover um determinado item de uma lista de filtros.
  17:            get { return this.GetHashCode(); }
  18:        }
  19:  
  20:        public string NomePropriedade { get; set; }
  21:        public TipoComparacao tipoCompracao { get; set; }
  22:        public string ValorProcurar { get; set; }
  23:        public Type Entidade { get; set; }
  24:        public bool FiltroAplicado { get; set; }
  25:  
  26:  
  27:  
  28:    }

Classe que contém os métodos reponsáveis por filtrar os dados da entidade.

   1: public static class Filtro
   2:     {
   3:         /// <summary>
   4:         /// Aplica uma lista de Filtros em uma entidade.
   5:         /// </summary>
   6:         /// <typeparam name="TEntidade">Instancia da entidade que será filtrada</typeparam>
   7:         /// <param name="entidadeOrigem">entidade que está sendo filtrada</param>
   8:         /// <param name="filtros">Lista de Filtros que deverão ser aplicadas</param>
   9:         /// <returns>Retorna um objeto com a lista das entidades filtradas</returns>
  10:         public static IQueryable<TEntidade> Filtrar<TEntidade>(this IQueryable<TEntidade> entidadeOrigem, List<FiltroEntidade> filtros) where TEntidade : class
  11:         {
  12:             //Pega apenas filtros que ainda não foram aplicados, em caso da lista de filtros ser reutilizada
  13:             List<FiltroEntidade> filtrosAplicar = filtros.Where(p => p.FiltroAplicado == false).ToList();
  14:  
  15:             foreach (FiltroEntidade item in filtrosAplicar)
  16:             {
  17:                 entidadeOrigem = entidadeOrigem.Filtrar(item.NomePropriedade, item.tipoCompracao, item.ValorProcurar);
  18:                 item.FiltroAplicado = true;
  19:             }
  20:  
  21:             return entidadeOrigem;
  22:  
  23:         }
  24:  
  25:  
  26:         /// <summary>
  27:         /// Aplica um unico Filtro
  28:         /// </summary>
  29:         /// <typeparam name="TEntidade">Instancia da Entidade</typeparam>
  30:         /// <param name="entidadeOrigem">objeto que será filtrada</param>
  31:         /// <param name="NomePropriedade">Nome da propriedade</param>
  32:         /// <param name="TipoComparacao">Tipo Comparação</param>
  33:         /// <param name="ValorProcurar">Valor que será filtrado filtrar</param>
  34:         /// <returns>Retorna um objeto com a lista das entidades filtradas</returns>
  35:         public static IQueryable<TEntidade> Filtrar<TEntidade>(this IQueryable<TEntidade> entidadeOrigem, string NomePropriedade, Minato.Entidades.FiltroEntidade.TipoComparacao TipoComparacao, string ValorProcurar)
  36:                                                                 where TEntidade : class
  37:         {
  38:  
  39:             Type type = typeof(TEntidade);
  40:             ConstantExpression procurarFiltro = Expression.Constant(ValorProcurar);
  41:             ParameterExpression parametro = Expression.Parameter(type, "p");
  42:             PropertyInfo propriedade = type.GetProperty(NomePropriedade);
  43:             Expression acesssoPropriedade = Expression.MakeMemberAccess(parametro, propriedade);
  44:  
  45:             Type tipo = Nullable.GetUnderlyingType(propriedade.PropertyType);
  46:  
  47:             if (tipo != null)
  48:             {
  49:                 PropertyInfo valProp = typeof(Nullable<>).MakeGenericType(tipo).GetProperty("Value");
  50:                 acesssoPropriedade = Expression.MakeMemberAccess(acesssoPropriedade, valProp);
  51:             }
  52:             else
  53:             {
  54:                 tipo = acesssoPropriedade.Type;
  55:             }
  56:             if (tipo == typeof(Guid))
  57:             {
  58:                 Guid guid = new Guid(ValorProcurar);
  59:                 procurarFiltro = Expression.Constant(guid);
  60:             }
  61:             else
  62:             {
  63:                 procurarFiltro = Expression.Constant(Convert.ChangeType(ValorProcurar, tipo));
  64:             }
  65:  
  66:  
  67:             MethodInfo startsWith = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
  68:             MethodInfo endsWith = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
  69:             MethodInfo contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
  70:             Expression operacaoRealizar = null;
  71:             switch (TipoComparacao)
  72:             {
  73:                 default:
  74:                 case FiltroEntidade.TipoComparacao.Igual:
  75:                     operacaoRealizar = Expression.Equal(acesssoPropriedade, procurarFiltro);
  76:                     break;
  77:                 case FiltroEntidade.TipoComparacao.Diferente:
  78:                     operacaoRealizar = Expression.NotEqual(acesssoPropriedade, procurarFiltro);
  79:                     break;
  80:  
  81:                 case FiltroEntidade.TipoComparacao.Maior:
  82:                     operacaoRealizar = Expression.GreaterThan(acesssoPropriedade, procurarFiltro);
  83:                     break;
  84:                 case FiltroEntidade.TipoComparacao.MaiorIgual:
  85:                     operacaoRealizar = Expression.GreaterThanOrEqual(acesssoPropriedade, procurarFiltro);
  86:                     break;
  87:  
  88:                 case FiltroEntidade.TipoComparacao.Contem:
  89:                     operacaoRealizar = Expression.Call(acesssoPropriedade, contains, procurarFiltro);
  90:                     break;
  91:  
  92:             }
  93:             var ExpressaoWhere = Expression.Lambda(operacaoRealizar, parametro);
  94:             var ResultadoDaExpressao = Expression.Call(typeof(Queryable), "Where", new Type[] { entidadeOrigem.ElementType }, entidadeOrigem.Expression, ExpressaoWhere);
  95:  
  96:             return entidadeOrigem.Provider.CreateQuery<TEntidade>(ResultadoDaExpressao);
  97:         }
  98:     }

Resultado:

Where

 

 

Para quem estiver preocupado com performance, segue aí o comparativo.

Comparativo

É isso mesmo, foi mais performático do que o modelo tradicional de lambda. (Executei 10 vezes, com dois itens na lista, um com tipo de comparação contains e outro com “maior igual“.

Quer saber como foi realizado o comparativo? Pois, em tecnologia a primeira pergunta é: Mas como foi feita esta comparação?

   1: class Program
   2:     {
   3:         static void Main(string[] args)
   4:         {
   5:             List<ClienteEntidade> clientes = new List<ClienteEntidade>();
   6:             Random rnd = new Random();
   7:  
   8:             int QtdadeRegistrosAdicionar = 1000000;
   9:  
  10:             Stopwatch tempo = new Stopwatch();
  11:  
  12:             tempo.Start();
  13:  
  14:             for (int i = 0; i < QtdadeRegistrosAdicionar; i++)
  15:             {
  16:                 clientes.Add(new ClienteEntidade { NomeCliente = "Cliente" + i, Endereco = "Endereço" + i, Idade = i, Salario = rnd.Next(1000, 10000) });
  17:  
  18:             }
  19:             tempo.Stop();
  20:             Console.WriteLine("Tempo gasto para preencher {0} registros: {1}", QtdadeRegistrosAdicionar, tempo.Elapsed);
  21:             List<FiltroEntidade> filtros = new List<FiltroEntidade>();
  22:             filtros.Add(new FiltroEntidade { NomePropriedade = "NomeCliente", tipoCompracao = FiltroEntidade.TipoComparacao.Contem, ValorProcurar = "Cli" });
  23:             filtros.Add(new FiltroEntidade { NomePropriedade = "Idade", tipoCompracao = FiltroEntidade.TipoComparacao.MaiorIgual, ValorProcurar = "300" });
  24:  
  25:             TimeSpan tempoDinamico, TempoTradicional;
  26:             int NumeroVezesExecucao = 10;
  27:             for (int i = 0; i < NumeroVezesExecucao; i++)
  28:             {
  29:                 tempo.Reset();
  30:                 tempo.Start();
  31:                 IList clientesFiltrados = clientes.AsQueryable().Filtrar(filtros).ToList();
  32:                 tempo.Stop();
  33:                 tempoDinamico = tempo.Elapsed;
  34:                 Console.WriteLine("Filtro Dinâmico    {0}", tempo.Elapsed);
  35:                 
  36:                 
  37:                 tempo.Start();
  38:                 IList clientesLambda = clientes.Where(p => p.NomeCliente.Contains("Cli") && p.Idade >= 300).ToList();
  39:                 tempo.Stop();
  40:                 TempoTradicional = tempo.Elapsed;
  41:                 Console.WriteLine("Filtro tradicional {0}", tempo.Elapsed);
  42:  
  43:                 Console.WriteLine("Comparativo (Dinamico - Tradicional): {0}", tempoDinamico - TempoTradicional);
  44:  
  45:  
  46:             }
  47:  
  48:             Console.ReadKey();
  49:         }
  50:     }

 

Espero que tenham gostado.

Baixem a Solução

Minato , Criado em 25/04/2010, 11:35

        Para abrir meu primeiro post, vou falar um pouco sobre o blog engine.

        Há tempos venho procurando um blog que tenha uma customização fácil e que toda as “firulas” fossem prontas, como ultimos Post’s, administração, usuários e etc.

        Então conheci o BlogEngine uma ferramenta muito prática, simples e de fácil manutenção. Uma das vantagens é a criação de temas. Basta criar um diretório com o tema. Óbvio que há regras que devem ser seguidas, mas, nada muito complicado. Você pode utilizar também o Mega-Pack de themas

        Você pode utiliza-lo com MySQL, SQLLite, SQLServer ou até mesmo através de XML. A implementação é muito simples. Dentro do diretório BlogEngine.Web há uma pasta chamada Setup. Dentro delas há os scripts para criação ou upgrade de versões, basta escolher o banco desejado e pronto, estruturas criadas.

        Após isso, no arquivo web.Config da sua aplicação é necessário alterar a String de Conexão.

<add name="BlogEngine" connectionString="Data Source=SeuServidor;Initial Catalog=BaseDados;Persist Security Info=True;User ID=usuario;Password=senha;" providerName="System.Data.SqlClient"/>

        É necessário tambem alterar o provider:

<blogProvider defaultProvider="DbBlogProvider">

 

Pronto, agora é só fuçar.

 
teste