Tabla de Contenidos

Aplicaciones web con ASP.NET

Acceso a datos

Desde VS2008 podemos crear controles enlazados a datos simplemente arrastrando tablas desde el administrador de servidores al editor de ficheros .dbml y utilizar este como fuente de datos.

Entre los siguiente posteriormente nombrados también existe el LinqDataSource.

Carpeta App_Data

Podemos crear también una base de datos propia de nuestro proyecto, para ello agregaríamos el fichero .MDF y el .LDF (si tratamos con DB SqlServer) a la carpeta App_Data de la aplicación web. Este directorio es una ruta segura ya que su contenido no se hace público, puede sernos útil también para almacenar ficheros XML. A las cadenas de conexión para DB locales agregaremos la propiedad AttachDbFileName, que especifica la ubicación del fichoer de DB. Si a la ruta |DataDirectory| esto se sustituirá por el path del directorio App_Data en tiempo de ejecución. La propiedad User Instance=true indica que cada usuario ejecutará un nuevo proceso para atacar a la DB.

"server=(local)\SQLExpress;AttachDbFileName=|DataDirectory|MyDatabase.mdf;Integrated Security=true;User Instance=true"

SqlDataSource

El control SqlDataSource representa una conexión a DB que puede ser utilizada por los demás controles en la página.
Por ejemplo, en el siguiente código, un GridView sse enlaza a un SqlDataSource:

<form runat="server">
<asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" runat="server"/>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
  SelectCommand="SELECT [au_id], [au_lname], [au_fname] FROM [authors]"
  ConnectionString="<%$ ConnectionStrings:Pubs %>" />
</form>

Propiedades útiles:

UpdateCommand o DeleteCommand deben contener parámetros de substitución para cada valor que pasarán otros controles enlazados a él, podemos especificar también una colección de UpdateParameters o DeleteParameters para establecer las propiedades de cada parámetro, tales como el tipo de parámetro, la dirección de entrada/salida o el valor por defecto.

<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:Pubs %>"
    SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors]"
    UpdateCommand="UPDATE [authors] SET [au_lname] = @au_lname, [au_fname] = @au_fname, [state] = @state WHERE [au_id] = @au_id"
    DeleteCommand="DELETE FROM [authors] WHERE [au_id] = @au_id"/>

Para realizar un filtrado de datos necesitamos agregar objetos DataParameter, estos permiten asociar valores externos a operaciones, variables o porpiedades.

<asp:DropDownList ID="DropDownList1" ... runat="server"/>
...
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
    ConnectionString="<%$ ConnectionStrings:Pubs %>" 
    SelectCommand="SELECT [au_id], [au_lname], [au_fname], [state] FROM [authors] WHERE [state] = @state">
       <SelectParameters>
          <asp:ControlParameter Name="state" ControlID="DropDownList1" PropertyName="SelectedValue" />
       </SelectParameters>
</asp:SqlDataSource>

Los tipos que podemos asignar como parámetros son:

ObjectDataSource

El ObjectDataSource enlaza objetos de forma similar a que el SqlDataSource, tiene una propiedad TypeName que indica el tipo de objeto que realizará las operaciones y a la vez tiene propiedades como el SelectMethod, el UpdateMethod, el InsertMethod y el DeteMethod para realizar las tareas que describen. Es decir, una clase como la siguiente:

public class MyProductsLayer {
      public DataView GetProducts();
      public DataView GetProductsByCategory(String categoryName);
      public DataView GetProductsByID(int recordID);
      public int UpdateProduct(int recordID, String recordData);
      public int DeleteProduct(int recordID);
      public int InsertProduct(int recordID, String recordData);
      public UpdateProduct (Product p);
}

Puede enlazarse con un ObjectDataSource de la siguiente forma:

<asp:ObjectDataSource TypeName="MyProductLayer" SelectMethod="GetProductss" UpdateMethod="UpdateProduct" DeleteMethod="DeleteProduct" InsertMethod="InsertProduct" runat="server"/>

XmlDataSource

Enlace de datos, Eval & Bind

Podemos utilizar los <%# %> para enlazar datos en una página .aspx, todas las expresiones de enlace de datos deben ir incluidas entro de estos carácteres. Ejemplos:

Eval

Eval es una función estática de System.Web.UI.DataBinder que puede ser utilizada en archivos de code-inline dentro de un entorno de enlace de datos. Por ejemplo en un GridView, en un TemplateField añadimos el nombre completo de una persona concatenando distintos campos de un registro:

<asp:TemplateField HeaderText="Name">
  <ItemTemplate>
    <%# Eval("TitleOfCourtesy") %> <%# Eval("FirstName") %> <%# Eval("LastName") %>
  </ItemTemplate>
</asp:TemplateField>

Eval también permite formatear el resultado que va a mostrar:

<%# Eval("BirthDate", "{0:MM/dd/yy}") %>
...
<ItemTemplate>
  <br />
  <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%# Eval("PhotoID", "PhotoFormViewPlain.aspx?ID={0}") %>'>
  <asp:Image ID="Image1" Runat="server" ImageUrl='<%# Eval("FileName", "images/thumbs/{0}") %>' /></asp:HyperLink>
  <br />
  <asp:Label ID="CaptionLabel" runat="server" Text='<%# Eval("Caption") %>' />
</ItemTemplate>
...
<%# Eval("Price", "Special Offer {0:C} for Today Only!") %>

Eval equivale a utilizar:

<%# DataBinder.Eval(Container.DataItem, "field_name") %>

El cual también puede ser usuado como (El método getEstatName devuelve un string):

<%# codegest.app_incidencies.incidencies.getEstatName(((ATMDB.ATM_INCIDENCIES_HISTORIC)Container.DataItem).estat) %>

Bind

Bind realiza la misma función que Eval, pero se diferencia con este en que no solo es de lectura sino también de actualización, es decir, muestra los datos y si es necesario también los actualiza en el servidor. Si sólo queremos mostrar los datos utilizaremos Eval pero si también queremos que se actualicen Bind:

<asp:TemplateField HeaderText="Name">
    <EditItemTemplate>
    <asp:TextBox ID="txtName" Text='<%# Bind("Name")%>' runat="server"></asp:TextBox>
    </EditItemTemplate>
</asp: TemplateField>
 
<asp:TemplateField HeaderText="User Id">
    <ItemTemplate>
    <asp:Label ID="lblUserId" Text='<%# Eval("UserId") %>' runat="server">
    </ItemTemplate>
</asp: TemplateField>

Sólo puede utilizarse con los controles GridView, DetailsView y FormView.

Controles de gestión de acceso a datos

Masters details y el Details View

La táctica del master-detail consiste en selección de un control como “master” que indicará qué detalles se mostrarán en el\los details.
El GridView contiene la propiedad SelectedValue que indica la fila seleccionada, está contiene el primer valor del campo especificado en la propiedad DataKeyNames. Para permitir la selección la propiedad AutoGenerateSelectButton ha de estar a true o la ShowSelectButton del CommandField también como true. Una vez hecho esto la propiedad SelectedValue puede ser asociada a un ControlParameter y mostar los detalles en el control Details View, que presenta los valores de un registro de forma tabulada.

DetailsView

El control DetailsView también soporta la edición, para ello se utilizan las propiedades AutoGenerateEditButton o CommandField.ShowEditButton y la fuente de datos tendrá que soportar la actualización.

Para que, por ejemplo, el DetailsView se enlace a un hipervínculo (QueryStringParameter) añadiremos un objeto HyperLinkField al grupo de columnas del GridView, la propiedad Text de este será el texto a mostrar y la NavigateUrl la dirección a la que se irá tras clickar en el enlace. Esto generaría una url estática comú para todos los campos, por lo que mejor sería especificar un NavigateUrlFields, su propiedad NavigateUrlFormatString espcifica el formato de la url donde {0} y {1} se sustituyen por los valores del campo.

<asp:GridView ID="GridView1" AllowSorting="True" AllowPaging="True" Runat="server"
  DataSourceID="SqlDataSource1" DataKeyNames="au_id" AutoGenerateColumns="False">
   <Columns>
    <asp:BoundField ReadOnly="true" HeaderText="ID" DataField="au_id" SortExpression="au_id" />
    <asp:BoundField HeaderText="First Name" DataField="au_fname" SortExpression="au_fname" />
    <asp:BoundField HeaderText="Address" DataField="address" SortExpression="address" />
    <asp:HyperLinkField HeaderText="View Details..." Text="View Details..." DataNavigateUrlFields="au_id"
            DataNavigateUrlFormatString="DetailsView_cs.aspx?ID={0}" />
   </Columns>
...

Una propiedad que tiene el DetailsView que no tiene el GridView es que puede insertar, para ello el DataSource enlazado debe permitir la inserción, la propiedad AutoGenerateInsertButton a true o añadir al grupo de campos del DetailsView un CommandField con el ShowInsertButton establecido a true. Para excluir un campo en el modo de Inserción, tendremos que establecer la propiedad InsertVisible del campo a false.

Control GridView

Propiedades

Tipos de columnas

<asp:hyperlinkfield datatextfield="lang2" datanavigateurlfields="id" datanavigateurlformatstring="AddElement.aspx?id={0}" />

Ejemplos

Ejemplo de GridView enlazado a datos:

<asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" runat="server">
      <Columns>
        <asp:BoundField HeaderText="First Name" DataField="au_fname" />
        <asp:BoundField HeaderText="Address" DataField="address" />
        <asp:BoundField HeaderText="Zip Code" DataField="zip" />
        <asp:CheckBoxField HeaderText="Contract" DataField="contract" />
      </Columns>
</asp:GridView>

Ejemplo de GridView que se enlazará a una lista de objetos con una propiedad nombre y que mostrará también un combo. Este DropDownList no cambia, pero podría hacerlo si se escribe el evento OnRowCreated y cada vez que se ejecute se coge este control DropDownList y se cambia en código.

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
    <Columns>
        <asp:BoundField DataField="nombre" HeaderText="nombre" />
        <asp:TemplateField HeaderText="apellido">
            <ItemTemplate>
                <asp:DropDownList runat="server">
                    <asp:ListItem>Activo</asp:ListItem>
                    <asp:ListItem>Inactivo</asp:ListItem>
                </asp:DropDownList>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Como: GridView

Coger el identificador de una fila en un campo no visible

Para ello deberemos utilizar la pripiedad DataKeyNames del GridView para indicar los campos clave:

<asp:GridView DataKeyNames="ID" ID="GridView1″ ...

Luego, de los campos definidos como Visible=“false” podremos recoger su id mediante:

int id = GridView1.DataKeys["ID"].Value;
int id = GridView1.DataKeys[0].Value;
int id = GridView1.DataKeys[e.RowIndex].Value;

Llenar un GridView con objetos de tipo anónimo

Es decir, teniendo un código del estilo:

GridView1.DataSource = from c in customers, o in c.Orders, 
  total = o.Total where total >= 2000 select new {id = c.CustomerID, 
  order = o.OrderID, total = total};
GridView1.DataBind();

En el RowCreated no podremos hacer algo así:

object element = e.Row.DataItem;
int id = element.id;

Lo que deberíamos hacer sería crear una clase, aunque sea interna:

public class GridType
{
    public int Id {get; set;}
    ...
}

Al agregarlo, en este caso con Linq:

... select new GridType {id = ...}

Y en el RowCreated:

GridType element = e.Row.DataItem as GridType;
int id = element.Id;

Indicar estilos

El siguiente grid tiene:

<asp:GridView id="grid" runat="server" Width="100%" AutoGenerateColumns="false" BorderColor="#8FA2B7" AllowSorting="true">
  <Columns>
    <asp:hyperlinkfield datatextfield="lang1" datanavigateurlfields="id" datanavigateurlformatstring="AddElement.aspx?id={0}" ControlStyle-CssClass="gridElement"  HeaderStyle-CssClass="gridHeader"/>
    <asp:hyperlinkfield datatextfield="lang2" datanavigateurlfields="id" datanavigateurlformatstring="AddElement.aspx?id={0}" ControlStyle-CssClass="gridElement"  HeaderStyle-CssClass="gridHeader"/>
    <asp:BoundField HeaderText="errors" DataField="errors" ItemStyle-CssClass="gridElement" HeaderStyle-CssClass="gridHeader" SortExpression="errors"/>
    <asp:BoundField HeaderText="rights" DataField="rights" ItemStyle-CssClass="gridElement" HeaderStyle-CssClass="gridHeader"/>  
  </Columns>
</asp:GridView>

Pequeños

Repeater

Es un control al cual se le pasa como DataSource una lista de elementos y este las muestra, no en formato tabla como el grid sino en el formato que elija el programador. Para ello hay que rellenarlo con objetos Template siguientes:

Por ejemplo un repeater que muestra en una tabla los elementos de una lista asignada como DataBound:

<asp:Repeater runat="server" ID="myRepeater">
    <HeaderTemplate>
        <table>
    </HeaderTemplate>
    <ItemTemplate>
        <tr>
            <td>
            <%#DataBinder.Eval(Container.DataItem, "nombre") %>
            </td>
        </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

Para crear Repeaters anidados, es decir, uno dentro del ItemTemplate de otro. En el primero definiríamos el evento OnItemDataBound o el OnItemCreated y en su método buscaríamos el Repeater anidado dentro del objeto creado:

private void rCanciones_ItemDataBound(object sender, System.Web.UI.WebControls.RepeaterItemEventArgs e)
{
   if(e.Item.ItemType == ListItemType.Item ||
      e.Item.ItemType == ListItemType.AlternatingItem)
   {
      string disco = (string)e.Item.DataItem;
      string[] canciones = new string[]{"canción 1","canción 2","canción 3"};
 
      Repeater rCanciones = (Repeater)e.Item.FindControl("rCanciones");
 
      rCanciones.DataSource = canciones;
      rCanciones.DataBind();
   }
}

Si quisieramos recoger los datos que se han mostrado en un control repeater podremos acceder a su colección de Items:

foreach (var item in this.Repeater1.Items)
{
    AtmUsers.AtmUserAppConf conf = (AtmUsers.AtmUserAppConf)((RepeaterItem)item).DataItem;
    DropDownList lPags = (DropDownList)((RepeaterItem)item).FindControl("pagList");
    HiddenField id = (HiddenField)((RepeaterItem)item).FindControl("idPag");
}

Controles

General

Propiedad AutoPostBack

Es la propiedad que permite al control lanzar eventos mediante PostBack (recargando la página).

Propiedad Command

Podemos enviar un comando desde un control anidadado y que después será interceptado por el método OnCommand; por ejemplo en un Repeater, desde un LinkButton anidado que en vez de cargar una página utilizando sus propiedades CommandName y la CommandArgument se indicará que se ha elegido, la primera propiedad es para indicar un string identificador del comando y la segunda para el argumento.
En el siguiente ejemplo se envia el comando LineEdition con el identificador del DataItem cargado dentro del Repeater .

<asp:Repeater ID="linesRepeater" runat="server" OnItemCommand="Repeater1_ItemCommand" OnItemCreated="Repeater2_ItemCreated">
...
<ItemTemplate>
    <tr>
        <td class="littleTextCell"><%# DataBinder.Eval(Container.DataItem, "linia") %></td>
        <td><asp:LinkButton ID="LinkButton1" runat="server" CssClass="textButtonCell" CommandName="LineEdition" 
                CommandArgument="<%# (codegest.panelgest.show_panel_temp.innerGridItem)Container.DataItem).id %>">Editar linia</asp:LinkButton>
        </td>
    </tr>
</ItemTemplate>

Luego, desde el método Repeater1_ItemCommand del code-behind:

if (e.CommandName == "LineEdition") {
    int codi = Int32.Parse(e.CommandArgument.ToString());
    ...

Si vamos a agregar elementos de la base de datos a nuestro control DropDownList, a parte de los introducidos manualmente, tendremos que utilizar la propiedad AppendDataBoundItems, sino estos se eliminarán.

<asp:DropDownList runat="server" Width="50px" ID="tipus" AppendDataBoundItems="true">
  <asp:ListItem Value="0" Text="Todos" />
</asp:DropDownList></td>

Controles Web de usuario

Un Web User Control es un control que crea el programador y que se puede reutilzar en las páginas asp.net, son clases que heredan de System.Web.UI.UserControl que a su vez hereda de System.Web.UI.TemplateParser y se guardan en archivos .ascx.

Para utilizar el control en nuestro formulario web de usuario primero tendremos que registrarlo a partir de la tag Register. La propiedad TagPrefix de esta indica un espacio de nombres único para el control dentro del .aspx, la TagName es el nombre por el cual nos referiremos a él y la Src es la página donde se encuentre el fichero.

<%@ Register TagPrefix="Acme" TagName="Message" Src="pagelet1.ascx" %>

Luego podremos utilizar el control haciendo: <namespace:nombre_control runat=“server” />:

<Acme:Message runat="server" />

Al igual que los web forms los user controls pueden contener code-behind. Si en este ponemos propiedades públicas podremos acceder a ellas a partir de las tags en el .aspx:
Código en el .ascx:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="WebApplication7.WebUserControl1" %>
<asp:Label ID="lbl" runat="server"></asp:Label>

Código en el .ascx.cs:

public partial class WebUserControl1 : System.Web.UI.UserControl
{
  public String texto = "";
 
  public String Text
  {
    set
    {
      this.text = value;
    }
    get
    {
      return this.text;
    }
  }
 
  protected void Page_Load(object sender, EventArgs e)
  {
    this.lbl.Text = this.texto;
  }
}

Código en el .aspx:

...
%@ Register TagPrefix="ctrl" TagName="webcontrol" Src="WebUserControl1.ascx" %>  
...
<ctrl:webcontrol runat="server" texto="hasodfasdf" />
...

Luego también podremos agregarlo a partir del código:

Control c1 = LoadControl("userctrl7.ascx");
((UserCtrl7)c1).Category = "business";
Page.Controls.Add(c1);

El tipo El nombre de la clase generada por el control lo podemos definir a partir de la propiedad ClassName de la tag Control:

<%@ Control ClassName="UserCtrl7" %>

Como...

Pequeños 'howto'

Construir una url relativa:

<script type="text/javascript" src="<%= ResolveClientUrl("~/jscripts/libs/jquery.js") %>"></script>

Descarga dinámica de ficheros

:!: Podemos crear un fichero a tiempo real y hacer que el usuario lo reciba:

Response.Clear();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment;filename=" + nameFile + ".xls");
Response.Charset = "";
Response.ContentType = "application/vnd.ms-excel";
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
gv.RenderControl(hw);
string style = @"<style> .textmode { mso-number-format:\@; } </style>";
Response.Write(style);
Response.Output.Write(sw.ToString());
Response.Flush();
Response.End();
Response.Clear();
Response.ContentType = "application/vnd.ms-excel";
Response.WriteFile("c:\\a.xls");
Response.Flush();
File.Delete("c:\\a.xls");
Response.End();

Ejemplos de cabeceras:

Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("Content-Length", file.Length.ToString());
Response.AddHeader("Content-Disposition", "inline;filename=temp.txt");
Response.AddHeader("content-disposition", "attachment;filename=" + nameFile + ".xls");

Combinar JavaScript

Aunque ASP.NET permite el uso de HTML standard, utilizarlo no aprovecha al máximo las capacidades de la tecnología de MS pero sin él nos es más complejo agregar código JavaScript.

Enviar o no un formulario

Tenemos la siguiente función JavaScript que valida si el campo firtName tiene algún valor introducido. Si es así retornará true si no mostrará un mensaje y retornará false.

<script language="javascript" type="text/javascript">
    function validate() {
        var doc = document.forms[0];
        if (doc.firstName.value == "") {
            alert("No pasarás!");
            return false;
        }
        return true;
    }
</script>
...
<asp:TextBox ID="firstName" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />

El siguiente código agrega código a la función OnClick que devuelve lo que la función validate que acabamos de ver retorna:

protected void Page_Load(object sender, EventArgs e)
{
    this.Button1.OnClientClick = "return validate()";
}

Otra forma de hacer lo mismo desde el code behind:

this.Button1.Attributes.Add("onClick", "return validate()");

Un textbox que se escribe a la vez que otro

Ejemplo parecido al anterior. Tenemos una función JavaScript que copia el contenido de un textbox a otro:

function copy() {
    var doc = document.forms[0];
    doc.lastName.value = doc.firstName.value;
}

El código html\asp:

<asp:TextBox ID="firstName" runat="server"></asp:TextBox>
<asp:TextBox ID="lastName" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />

Y el código que agrega el keypress:

this.firstName.Attributes.Add("onkeyup", "copy()");

Agregar el evento desde JavaScript

<body onload="load_body()">

Y esa función sería:

function load_body() {
   var doc = document.forms[0];
   doc.firstName.onkeypress = copy;
}

O…

function load_body() {
    var doc = document.forms[0];
    doc.firstName.onkeypress = function copy2() {
        var doc = document.forms[0];
        doc.lastName.value = doc.firstName.value;
    }
}

Con jQuery

<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
    $(document).ready(function() {
        $('#firstName').keypress(function() {
            var doc = document.forms[0];
            doc.lastName.value = doc.firstName.value;
        });
    });
</script>

PostBack desde JavaScript

ASP.NET crea código en JavaScript que realiza la acción de PostBack, de esta forma los controles web pueden llamarlo. Desde código no gestionado por ASP.NET podemos llamarlo de la siguiente forma: el nombre de la función que hace el PostBack es __doPostBack y se le pasan dos argumentos: quien hace el PostBack y con qué argumento:

__doPostBack('__Page', 'MyCustomArgument');

Aún así no es recomendable llamar a este método directamente, lo mejor es hacer que ASP.NET genere su llamada, para ello podríamos crear en el code behind una variable protegida:

protected string strPostBack;

Y en el mismo método PageLoad podremos crear la llamada dinámicamente a partir del método: Page.ClientScript.GetPostBackEventReference.

this.strPostBack = Page.ClientScript.GetPostBackEventReference(this, "argumentoTest");

Y en el código JavaScript\jQuery:

<script type="text/javascript">
    $(document).ready(function() {
        $('#clickmeupdate').click(function() {
            <%= strPostBack %>;
        });
    });
</script>

Luego podremos capturar este argumento de la siguiente forma:

if ((Page.IsPostBack) && (Request["__EVENTARGUMENT"] == "argumentoTest"))
    this.clickmeupdate.InnerText = "Se ha hecho PostBack";

Id's y páginas maestras

Para saber la id de un control posicionado en una página que utiliza master pages usaremos la propiedad ClientID del control:

var edited = $('#<%= fieldEddited.ClientID %>').val();

Otros

Code Snippets

LinkButton de eliminar con confirmación en cliente

<asp:LinkButton ID="lbtnDelete" runat="server" CommandName="DeleteItem" 
      Text="Delete" OnClientClick="return confirm(’Are you sure you want to\ndelete this item?’);">
</asp:LinkButton>

Clase HttpHandler que devuelva una imágen dinámicamente

public class Handler1 : IHttpHandler {
    public void ProcessRequest(HttpContext context) {
        context.Response.ContentType = "image/jpeg";
        System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 100);
        System.Drawing.Brush b = new System.Drawing.SolidBrush (System.Drawing.Color.FromArgb(255, 0,0));
        System.Drawing.Graphics.FromImage(bmp).FillRectangle(b, new System.Drawing.Rectangle(0, 0, 100, 100));
        bmp.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
    }
 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

Aunque también lo podrías poner en una página:

<%@ Page ContentType = "image/jpeg"%>
<%@ Import Namespace = "System.Drawing" %>
<%@ Import Namespace = "System.Drawing.Imaging" %>

<Script Runat = "Server">
Bitmap bmp = new Bitmap(100, 100);
Brush b = new SolidBrush (System.Drawing.Color.FromArgb(255, 0,0));
Graphics.FromImage(bmp).FillRectangle(b, new Rectangle(0, 0, 100, 100));
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
</Script>

Notas

System.Data.DataTable table = new System.Data.DataTable();
table.Rows.Add(table.NewRow());
this.Repeater1.DataSource = table;
this.Repeater1.DataBind();