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
<<  agosto 2016  >>
stqqssd
25262728293031
1234567
891011121314
15161718192021
22232425262728
2930311234

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.

Acredito que não usamos nem 30% do que o Banco de Dados SQL Server oferece. Há alguns dias, conversava com alguns amigos e lembrei de uma dica que a ordem de criação dos índices e consultas faz diferença, então, resolvi testar.

Tive uma surpresa, realmente faz diferença, algo que nem sempre nos atentamos, seguem as evidências dos testes.

Tamanho da tabela

clip_image002

Criação do índice (Notem que inclusive há botões, mover para cima e mover para baixo) .

clip_image002[8]

 

clip_image002[10]

clip_image002[12]

Resultado usando o índice corretamente. (CPF, Nome) Resultado usando o índice com a ordem invertida

O que achei mais engraçado é que o execution Plain apontou 50% para cada execução mostrando que o indice CPF_Nome foi acionado, mas, fez diferença no resultado (Select acima). Eu executei muitas vezes, pois, o Sql Server sempre mantém informações das consultas realizadas…

image

Não contente, fiz uma aplicação para comparar a execução e….. surpresa!!! Mesmo com o ExecutionPlain dizer que o custo era igual para ambos, a execução tem o tempo diferente.

image

Se for um cara chato como eu, vai logo pensar… Bom, depende de como foram feitos os testes… então vaí aí como foi realizado o teste.

class Program
{
static void Main(string[] args)
{

string strCnn = System.Configuration.ConfigurationSettings.AppSettings["cnn"];
Stopwatch timer = new Stopwatch();

for (int i = 0; i < 10; i++)
{
timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where cpf like '%317395%' and nome like '%Minato%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utilizando Corretamente o Indice (CPf, Nome) " + timer.Elapsed);
timer.Reset();


timer.Start();

using (SqlConnection cnn = new SqlConnection(strCnn))
{
using (SqlCommand cmd = new SqlCommand("Select * from Clientes where nome like '%Minato%' and cpf like '%317395%'", cnn))
{
cnn.Open();
var reader = cmd.ExecuteReader(CommandBehavior.SingleResult | CommandBehavior.CloseConnection);
}

}
timer.Stop();
Console.WriteLine("Utiliando com a ordem invertida (Nome, CPF) " + timer.Elapsed);
timer.Reset();

}

Console.ReadKey();


}
}

 

Então fica a dica. ;-), vamos usar os indices na sequencia correta.

Sigam @DicaDoNerd.

 

Uma das dúvidas que sempre tive é como uma aplicação de grande porte baseada no LinqToSql se comportaria. Hoje trabalho em um segmento (bolsa de valores) onde performance é extremamente necessário. Sabemos que tabelas com grande concorrência sem a utilização do NOLOCK pode apresentar grandes problemas, este problema ainda não sei como resolver, mas uma coisa que consegui é estudar algumas soluções que podem melhorar a performance no LinqToSql.

A primeira cois foi criar uma tabela com tamanho razoável (Estou utilizando uma tabela de uma aplicação real) para tentar espelhar uma realidade (com tipos Nullables, campos preenchidos, não preenchidos e etc). A tabela contém mais de um milhão de registros (1.015.232) com 16 campos.

Select com a quantidade de registros (1.015.232) mais de um milhão registros
Tipo de dados e campos da tabela
Entidade criada a partir do LinqToSql (dbml)

É importante entender quando podemos desativar as propriedades ObjectTrackingEnabled e DeferredLoadingEnabled.

ObjectTrackingEnabled = Essa propriedade só deve ser desativada quando for uma operação de SELECT, ou seja, qualquer outra ação que manipule informações no banco de dados (INSERT, UPDATE ou DELETE), deve ter a propriedade habilitada, caso contrário, você irá se deparar com a seguinte exception.

Object tracking is not enabled for the current data context instance.

Exception disparada pela falta do ObjectTracking

Quando existe um relacionamento no banco de dados e você cria o ORM com o LinqToSql são criadas relações entre as tableas e você pode ter acesso a tabela relacionadas, por exemplo, se na tabela cliente houvesse uma tabela relacionada com tipo de cliente, porderíamos ter acesso ao tipo desta forma

ClienteEntidade.TipoClienteEntidade.TipoCliente

Ocorre que pode se tornar um problema quando há uma grande quantidade de registros, por uma boa prática devemos desabilitar este objeto e habilitarmos apenas quando necessário. Uma alternativa para carregar apenas a tabela necessária (quando há mais de duas tabelas relacionadas) é utilizar LoadWith .

Para desabilitar o mapeamento de entidades relacionadas é desabilitar a propriedade DeferredLoadingEnabled.

Agora vamos ver na prática se isso funciona mesmo. Vamos aos testes.

Como sou desconfiado de resultados baseado em uma unica execução, eu sempre acho que fica algo em cache, que fica algo na memória, enfim, fiz algumas repetições para garantir.

class Program
{
static void Main(string[] args)
{
Stopwatch timer = new Stopwatch();

int qtde = 0, qtdadeTracking = 0;
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Iteração: " + i);
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

qtde = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades habilitadas {0} qtade {1:N0}", timer.Elapsed, qtde);
timer.Reset();
timer.Start();
using (dbDadosDataContext db = new dbDadosDataContext())
{

db.DeferredLoadingEnabled = false; // Desabilitando as tabelas relacionadas.
db.ObjectTrackingEnabled = false; // Desabilito o tracking
qtdadeTracking = db.ClientesEntidades.ToList().Count;
}
timer.Stop();
Console.WriteLine("Tempo com as propriedades desabilitadas {0} - qtdade {1:N0}", timer.Elapsed, qtdadeTracking);
}
Console.ReadKey();

}
}

 

Resultado do teste: É possível diminuir pela metade o tempo de execução.

Espero que tenham gostado.

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. ;-)

Minato , Criado em 28/05/2011, 17:33

Enviar email por aplicações é bastante eficaz, assim, utilizando a classe MailMessage() podemos fazer isso, vamos ver como funciona?

Não tem qualquer segredo, basta no web.config colocar as chaves:

Email.FROM = seuemail@seuemail.com.br (que será exibido para o destinatário.

Email.SMTP = geralmente: smtp.seudominio.com.br

Email.NetworkCredential.User = Usuário (seu email)

Email.NetworkCredential.PWS = Senha.

public class Email : IDisposable
{
private readonly string Remetente = ConfigurationManager.AppSettings["Email.FROM"];
private readonly string SMTP = ConfigurationManager.AppSettings["Email.SMTP"];
private readonly string Usuario = ConfigurationManager.AppSettings["Email.NetworkCredential.User"];
private readonly string Senha = ConfigurationManager.AppSettings["Email.NetworkCredential.PWS"];

public void EnviarEmail(string Assunto, string Html, string EmailDestinatario)
{

//Cria novo objeto MailMessage
MailMessage mailMessage = new MailMessage();

//Define o remetente
mailMessage.From = new MailAddress(Remetente);

//Define primeiro destinatário
mailMessage.To.Add(EmailDestinatario);

//Define segundo destinatário, note que podemos adicionar infinitos destinatários
//mailMessage.To.Add("E-MAIL DO DESTINATÁRIO");

//Define assunto do e-mail
mailMessage.Subject = Assunto;

//Seta propriedade para enviar email em html como true(verdadeiro)
mailMessage.IsBodyHtml = true;

//Seta o corpo do e-mail com a estrutura HTML gravada na stringbuilder sbBody
mailMessage.Body = Html;

//Cria novo SmtpCliente e seta o endereço
SmtpClient smtpClient = new SmtpClient(SMTP);

//Credencial para envio por SMTP Seguro (APENAS QUANDO O SERVIDOR EXIGE AUTENTICAÇÃO)
smtpClient.Credentials = new NetworkCredential(Usuario, Senha);

// Envia a mensagem
smtpClient.Send(mailMessage);

}


#region IDisposable Members

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

#endregion
}

Para usar:

using(Email email = new Email())
{
email.EnviarEmail("Assunto", "<strong> Olá </strong>", "contato@alexandreminato.com.br");
}

Pronto.

Você gostaria de algum artigo em especial? Entre em contato no formulário ou envie email para contato@alexandreminato.com.br

Até a próxima

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.

Algumas vezes necessitamos “copiar” o conteudo HTML de uma determinada página na web. Existem diversar formas de realizar esta operação.

A mais simples é…

WebClient clnt = new WebClient();
Stream s = clnt.OpenRead("http://www.alexandreminato.com.br");
StreamReader r = new StreamReader(s);
string res = r.ReadToEnd();

Faltando um mês para o casamento e tentando arrumar tempo para colocar alguns posts… Por esse motivo este foi “pobre”.

É comum precisarmos saber quais foram valores alterados, para que as alterações sejam logadas. Há um método GetModifiedMembers() no LinqToSql.  publiquei um artigo que mostra como podemos criar um XML (XElement) com as informações alteradas, com um pouco de criatividade é possível unir as duas funciondalidades.

Espero que gostem.

Até a próxima!

Minato 8-)

Tem alguma sugestão para vídeo aula, artigo? Entre em contato

 
teste