miércoles, 31 de marzo de 2010
En el caso de que se encuentren con aplicaciones en las cuales haya un exceso de dato en los controles desplegables, una posible solución es paginar dicho control al estilo de un grid con las opciones de "Primera", "Anterior", "Siguiente" y "Última". Én este artículo muestro como se puede llegar a paginar dichos controles.
Definición del control:
Lo primero será definir el control derivado a partir del DropDownList genérico que estamos acostumbrados a usar.
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Data;
namespace WebControlLibrary
{
public class DDLPaginado : DropDownList
{
//Member to hold PagedDataSource
private PagedDataSource pds;
//flag variable to indicate if we should go to the last page
private bool goToLastPage=false;
/// <summary>
/// DataSource
/// </summary>
public override object DataSource
{
get
{
// Nos aseguramos que el PageddataSource ha sido creado
EnsureDataSource();
return pds;
}
set
{
// Si el DataSource es un DataReader no podemos usar este control porque
// no nos indica el nmero de elementos.
if(value is IDataReader)
throw new ArgumentException("DataReader isn't supported as data source","DataSource");
// El DataSource debe ser un DataTable, un DataView o un IEnumerable.
if(!((value is DataTable) || (value is DataView) || (value is IEnumerable)))
throw new ArgumentException("Data source not supported","DataSource");
// Nos aseguramos que el PageddataSource ha sido creado
EnsureDataSource();
// En caso de que el DataSource sea DataTable usamos DefaultView(DataView).
if(value is DataTable)
pds.DataSource =((DataTable)value).DefaultView;
// Si el DataSource es un DataView lo usamos directamente.
if(value is DataView)
pds.DataSource =(DataView)value;
// Si el DataSource es un IEnumerable lo usamos directamente.
if(value is IEnumerable)
pds.DataSource=(IEnumerable)value;
// Se asigna mediante la funcin de la clase base DataSource
base.DataSource =pds;
}
}
/// <summary>
/// "Page index" se usa en la paginacin.
/// </summary>
public int CurrentPageIndex
{
get
{
object o=ViewState[this.UniqueID + "CurrentPageIndex"];
return(o==null)? 0 : (int)o;
}
set
{
ViewState[this.UniqueID + "CurrentPageIndex"]=value;
}
}
/// <summary>
/// Mtodo para incrementar el "Page index".
/// </summary>
public void GoToNextPage()
{
EnsureDataSource();
if(!pds.IsLastPage)
CurrentPageIndex++;
}
/// <summary>
/// Mtodo para decrementar el "Page index".
/// </summary>
public void GoToPreviousPage()
{
EnsureDataSource();
if(CurrentPageIndex > 0)
CurrentPageIndex--;
}
/// <summary>
/// Mtodo para acceder a la primera pgina del DDL.
/// </summary>
public void GoToFirstPage()
{
CurrentPageIndex = 0;
}
/// <summary>
/// Mtodo para acceder a la ltima pgina del DDL.
/// </summary>
public void GoToLastPage()
{
goToLastPage = true;
}
/// <summary>
/// Nmero de elementos por pgina.
/// </summary>
public int PageSize
{
get
{
EnsureDataSource();
return pds.PageSize;
}
set
{
//El nmero de elementos no puede ser menor de 1.
if(value < 1 )
throw new ArgumentException("El nmero de elementos no puede ser menor de 1.");
EnsureDataSource();
pds.PageSize = value;
}
}
/// <summary>
/// Mtodo para asegurarnos que el PagedDataSource ha sido creado.
/// </summary>
private void EnsureDataSource()
{
if(pds ==null)
{
pds=new PagedDataSource();
pds.AllowPaging =true;
pds.PageSize = 10;
}
}
protected override void OnDataBinding(System.EventArgs e)
{
EnsureDataSource();
// Se cuenta el nmero mximo de pginas existentes.
// Slo se puede usar si existe DataSource.
if(base.DataSource == null)
throw new ArgumentException("DataSource no puede estar vaco.");
int countableindex;
try
{
// ndice es el DataSourceCount por PageSize
countableindex=(int)(pds.DataSourceCount / pds.PageSize);
// Si el mdulo es 0, el ndice se debe decrementar en 1
if(pds.DataSourceCount % pds.PageCount == 0)
{
countableindex--;
}
}
catch(Exception ex)
{
countableindex=0;
}
// Se verifica que el ndice no sobrepasa la ltima pgina existente
if(CurrentPageIndex > countableindex || goToLastPage == true)
{
pds.CurrentPageIndex = countableindex;
// Se asigna la propiedad CurrentPageIndex para que refleje el ndice correcto
CurrentPageIndex=countableindex;
}
else
{
// El ndice toma el valor del CurrentPageIndex
pds.CurrentPageIndex = CurrentPageIndex;
}
// Llamada de la clase base del OnDataBinding
base.OnDataBinding(e);
}
}
}
Uso del control:
El control funcionará igual que otros controles a los cuales se les asocia datos mediante un DataSource (data-bound control in ASP.NET).
PARTE VISUAL (.ASCX)
<asp:Button ID="btnPrimera" Runat=server Text="Primera" />
<asp:Button ID="btnAnterior" Runat=server Text="Anterior" />
<asp:Button ID="btnSiguiente" Runat=server Text="Siguiente" />
<asp:Button ID="btnUltima" Runat=server Text="Última" />
<br/>
<cc1:DDLPaginado id=DDLPaginado1 runat="server" PageSize="10"></cc1:DDLPaginado>
CÓDIGO (.ASPX.CS)
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
BindDDL();
}
}
//Test data
private void BindDDL()
{
SortedList sl=new SortedList();
for(int i=1;i<51;i++)
sl.Add(i,i);
PagedDDL1.DataSource = sl;
PagedDDL1.DataValueField ="Value";
PagedDDL1.DataTextField ="Key";
PagedDDL1.DataBind();
}
private void btnFirst_Click(object sender, System.EventArgs e)
{
PagedDDL1.GoToFirstPage();
BindDDL();
}
private void btnPrevious_Click(object sender, System.EventArgs e)
{
PagedDDL1.GoToPreviousPage();
BindDDL();
}
private void btnNext_Click(object sender, System.EventArgs e)
{
PagedDDL1.GoToNextPage();
BindDDL();
}
private void btnLast_Click(object sender, System.EventArgs e)
{
PagedDDL1.GoToLastPage();
BindDDL();
}
Con este código ya estaría funcionando el DropdownList con paginación en vuestra aplicación.
lunes, 29 de marzo de 2010
Muchas veces me he encontrado con la necesidad de insertar código javascript dinámicamente, por ejemplo, añadiendo eventos a algunos controles de mi página aspx. Para hacerlo siempre había utilizado el método Page.ClientScript.RegisterStartupScript(...). Funcionaba correctamente hasta que el script hacía referencia a un control que se encontraba dentro de un UpdatePanel. El código javascript no se ejecutaba.
Si te ocurre esto, la solución es substituir la llamada Page.ClientScript.RegisterStartupScript(...) por ScriptManager.RegisterStartupScript(...) y todo funcionará correctamente.
sábado, 27 de marzo de 2010
Para invocar a los Web Services propios de Exchange 2007 es necesario crear un usuario de servicio que cuente con permisos de impersonación sobre las mailbox databases. Para cada entorno se creará el usuario llamado ExchangeWSuser (sin incluir en ningún grupo de seguridad, únicamente en “domain user” ) y desde el servidor de Exchange 2007 se deberán ejecutar estas dos sentencias en PowerShell. Estos comandos será preciso ejecutarse cada vez que se cree una BBDD de Exchange 2007.
1.------------------------------------------------------------------
Get-MailboxDatabase | ForEach-Object {Add-ADPermission -Identity $_.DistinguishedName -User ExchangeWSUser -ExtendedRights ms-Exch-EPI-May-Impersonate}
2.------------------------------------------------------------------
Add-ADPermission -Identity (get-exchangeserver -Identity SERVER_NAME).DistinguishedName -User (Get-User -Identity ExchangeWSUser | select-object).identity -extendedRight ms-Exch-EPI-Impersonation
ExchangeServiceBinding esb = new ExchangeServiceBinding();private static string usuarioEx;
// summary
// Crear el Objeto de enlace con Exchange 2007
// summary
// param name="UserName" Nombre de Usuario
// param name="Domain Dominio del usuario
// param name="Url" Url De Exchange. Parámetro opcional. Se cogerá la Url por defecto en caso de estar vacío.
private void CreateExchangeServiceBinding(string UserName, string Domain, string Url)
{
usuarioEx = UserName;
esb.ExchangeImpersonation = new ExchangeImpersonationType();
ServicePointManager.ServerCertificateValidationCallback = delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
// Replace this line with code to validate server certificate.
return true;
};
esb.ExchangeImpersonation.ConnectingSID = new ConnectingSIDType();
esb.ExchangeImpersonation.ConnectingSID.PrimarySmtpAddress = UserName + "@" + Domain;
if (Url != null && Url != string.Empty)
{
esb.Url = Url;
}
else
{
esb.Url = ConfigurationManager.AppSettings["EWSUrl"].ToString();
}
// Se identifica el enlace del servicio y el usuario Exchange que reealizará la impersonación
esb.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["ExchangeWsUser"].ToString(), ConfigurationManager.AppSettings["ExchangeWsPass"].ToString(), ConfigurationManager.AppSettings["ExchangeWsUserDomain"].ToString());
}Como se ve se accede al config en algunos casos:
ConfigurationManager.AppSettings["ExchangeWsUser"]
Obteniendo el username, pass y domain al que pertenece el usuario impersonador.
Espero les sirva de ayuda.
martes, 23 de marzo de 2010
Los parámetros que espera un informe y que dependen de la referencia cultural, por ejemplo, los parámetros de fechas, se interpretan según el idioma del explorador. Es decir, aunque el servidor de informes tenga la configuración regional que se desea, si un usuario tiene el explorador establecido en otra configuración regional distinta, los valores de los parámetros que se envíen al servidor se interpretarán de forma incorrecta.
Para evitarlo, se puede especificar mediante el parámetro rs:ParameterLanguage añadido en la url el idioma con el que se debe interpretar, independientemente de la configuración regional que tenga el explorador del usuario que lo ejecute.
Ejemplo: http://[Nombre _servidor]/ReportServer?/Reports/NombreReport&rs:Command=Render&Fecha=10/03/2010&rs:ParameterLanguage=es-ES
Fuente: http://msdn.microsoft.com/es-es/library/ms155064.aspx
lunes, 22 de marzo de 2010
<html>
<head>
<title>Untitled Document</title>
<script language="JavaScript">
function maximiza()
{
window.moveTo(0,0);
if (document.all)
{
top.window.resizeTo(screen.availWidth,screen.avail Height);
}
else if (document.layers||document.getElementById)
{
if (top.window.outerHeight<screen.availHeight||top.window.outerWidth<screen.availWidth){
top.window.outerHeight = screen.availHeight;
top.window.outerWidth = screen.availWidth;
}
}
</script>
</head>
<body>
<button onclick=maximiza()>Maximizar</button>
</body>
</html>
Al pulsar el botón la ventana se maximiza. Si se desea que la ventana se maximize al entrar , sólo hace falta poner <body onload=maximiza()>
viernes, 19 de marzo de 2010
Cargar objetos con el EF.
Desde el framework puedes cargar relaciones usando
include. Por cada uno, el framework añadirá un
join que puede originar consultas complejas, y que sean devueltas grandes cargas de datos. A este tipo de cargas se les llama "
Eager loading".
Sin embargo, esto puede ser evitado usando el método
Load para hacer "
lazy or
explicit loading”, que abre una conexión a base de datos para recuperar la información necesaria.
Así nos aseguramos de que la consulta sólo ejecutará una petición a un objeto relacionado mediante una petición explícita.
Por ejemplo:
// Get a specified customer by contact ID.
Contact customer = context.Contact.Where("it.ContactID = @customerId",
new ObjectParameter("customerId", customerId)).First();
// Load the orders for the customer
if (!customer.SalesOrderHeader.IsLoaded)
{
customer.SalesOrderHeader.Load();
}
Filtra los datos.
Cuando se usa este tipo de carga (con
Load o con
LoadWith) se reciben todos los datos asociados con la clave primaria, pero la mayoría de los casos se deben aplicar filtros adicionales. Para ello se usa el método
DataLoadOptions.AssociateWith, que toma el criterio al cargar los datos como parámetro y aplica la consulta.
using (DataContext context = new DataContext())
{
DataLoadOptions options = new DataLoadOptions();
options.AssociateWith(cat=> cat.Products.Where(prod => !prod.Discontinued));
context.LoadOptions = options;
}
jueves, 18 de marzo de 2010
Muchas son las veces que hemos detecado que el SiteMap que nos ofrece Microsoft, se nos queda corto cuando deseamos crear dinamismos en función de variables, como podría ser un QueryString o un varible de sessión.
En este post, vamos a descubrir como crear un SiteMap customizable en función de varias variables, en nuestro ejemplo utilizaremos una QueryString.
Imaginemos que queremos cambiar el título de un nodo, en función de su QueryString como en este caso:
<siteMapNode title="Departamentos" url="~/Dpto/Default.aspx" >
<siteMapNode title="Nuevo Departamento" editTitle="Editar Departamento" url="~/Dpto/Dpto.aspx" queryStringToInclude="EntId" ></siteMapNode>
</siteMapNode>
Como vemos en este ejemplo, queremos cambiar el nodo de Nuevo Departamento en función del QueryString en el caso que exista EntId para que quede de la siguiente manera
Departamentos > Nuevo Departamento
o
Departamentos > Editar Departamento
En primer lugar, crearemos un proyecto de Biblioteca de clases que lo llamaremos ExtendedSiteMapProvider donde crearemos el siguiente código
Imports System.Collections.Specialized
Imports System.Web
Namespace Configuration
Public Class ExtendedSiteMapProvider
Inherits XmlSiteMapProvider
Public Overrides Sub Initialize(ByVal name As String, ByVal attributes As NameValueCollection)
MyBase.Initialize(name, attributes)
Dim resolveHandler As New SiteMapResolveEventHandler(AddressOf SmartSiteMapProvider_SiteMapResolve)
AddHandler Me.SiteMapResolve, resolveHandler
End Sub
Function SmartSiteMapProvider_SiteMapResolve(ByVal sender As Object, ByVal e As SiteMapResolveEventArgs) As SiteMapNode
If (SiteMap.CurrentNode Is Nothing) Then Return Nothing
Dim this As New XmlSiteMapProvider
Dim temp As SiteMapNode
temp = SiteMap.CurrentNode.Clone(True)
Dim u As Uri = New Uri(e.Context.Request.Url.ToString())
Dim tempNode As SiteMapNode = temp
While Not tempNode Is Nothing
Dim qs As String = GetQueryString(tempNode, e.Context)
If Not qs Is Nothing Then
If Not tempNode Is Nothing Then
tempNode.Url += qs
tempNode.Title = tempNode("editTitle")
Return tempNode
End If
End If
tempNode = tempNode.ParentNode
End While
Return temp
End Function
Private Function GetQueryString(ByVal node As SiteMapNode, ByVal context As HttpContext) As String
If node("queryStringToInclude") Is Nothing Then Return Nothing
Dim values As NameValueCollection = New NameValueCollection
Dim vars() As String = node("queryStringToInclude").Split(",".ToCharArray())
Dim s As String
For Each s In vars
Dim var As String = s.Trim()
If context.Request.QueryString(var) Is Nothing Then Continue For
values.Add(var, context.Request.QueryString(var))
Next
If values.Count = 0 Then Return Nothing
Return NameValueCollectionToString(values)
End Function
Private Function NameValueCollectionToString(ByVal col As NameValueCollection) As String
Dim parts(col.Count - 1) As String
Dim keys() As String = col.AllKeys
For i As Integer = 0 To keys.Length - 1
parts(i) = keys(i) & "=" & col(keys(i))
Next
Dim url As String = "?" & String.Join("&", parts)
Return url
End Function
End Class
End Namespace
Si nos fijamos, utilizamos el método
GetQueryString que nos evaluará si existe el atributo
queryStringToInclude del nodo y si existe, nos validará que esté informado ese atributo en el QueryString.
Ya por último, nos queda por decirle a nuestro Web.Config que utilize nuestro proveedor de SiteMap en lugar del estándar de Microsoft.
Para ello pondremos en nuestro web.config lo siguiente
<siteMap defaultProvider="ExtendedSiteMapProvider" enabled="true">
<providers>
<clear />
<add name="ExtendedSiteMapProvider" type="OpenSky.UI.Utils.Configuration.ExtendedSiteMapProvider" siteMapFile="web.sitemap" securityTrimmingEnabled="true" />
</providers>
</siteMap> jueves, 18 de marzo de 2010
En noviembre de 2009 Microsoft nos deleita con la CTP (Community Technology Preview) de su herramienta de "usuario" para la creación de informes, Report Builder, en su versión 3.0. Esta nueva versión viene con novedades importantes relacionadas a nuevas funcionalidades de SQL Server 2008 y Sharepoint, como las ReportParts (trozos reutilizables de informes, la posibilidad de disponer las listas de Sharepoint como origen de datos (SharePoint List Data Extension), la posibilidad de representar datos geoespaciales sobre un mapa dentro del informe, etc.
Podeies ver una lista completa de las novedades en http://technet.microsoft.com/en-us/library/ee633667(SQL.105).aspx
viernes, 12 de marzo de 2010
Pequeños ficheros EDMX.
El DataContext representa una unidad de trabajo, no toda la base de datos.
El tamaño del esquema .xml es proporcional al número de tablas de la base de datos desde el que se genera, y como el tamaño del archivo aumenta, también aumentará el tiempo que tarda en crear el modelo en memoria para este metadata. Además el diseñador del Visual Studio también comienza a navegar en la peor eficiencia cuando el número de entidades es muy alta.
Si tienes un modelo muy interconectado puedes tener problemas de rendimiento, y hay que pensar en la posibilidad de dividirlo en varios archivos edmx, cuando tienes del orden de 100 entidades.
La desventaja de tener varios pequeños modelos de datos es que tendrás que mantener varios .edmx sincronizados cuando haces cambios en la base de datos.
miércoles, 10 de marzo de 2010
Microsoft ya ha anunciado la fecha de lanzamiento de SharePoint 2010 & Office 2010. Stephen Elop, Presidente de la División de Negocios de Microsoft, hará público el lanzamiento a las 11:00am del 12 de Mayo del 2010. Igualmente comunican que el RTM estará disponible en el mes de Abril.
Siguiente >>