Archiv der Kategorie: JavaScript / HTML /CSS /XML

Heirate mich (ASP.NET und JQuery Accordion)

Was ganz nützlich für die Darstellung von Daten ist, ist das Accordion, welches ja auch bereits im AjaxControlToolkit vorkommt.

Nun die meisten werden das Ganze sicherlich rein nativ client-seitig mit JQuery und der Herumschlagerei von Div-Elementen verwendet haben.

Meine Überlegung war es, in Abhängigkeit zu einer Quelle (IEnumerable) die Accordion-Items generieren zu lassen.

Wie bin ich vorgegangen?

Als erstes habe ich mir das Markup eines Demo-Accordions auf der Webseite von JQuery angesehen.

Da es im .NET die Klasse HtmlGeneric gibt kann man mit dieser mit ein wenig Geschick viel anstellen

Also wir brauchen folgendes:

  • Eine ASP.Seite mit einem Div-Element dass den Container für das Accordion darstellt
  • Als nächstes brauchen wir eine Klasse, die uns den Accordion Markup generiert
  • Und last but not least ein Model Element (Klassenelement aus unserem Modell
  • 1. Erstellen der ASPX Seite

    Der nachfolgende Markup-Code ist keine Magie und ziemlich schlank

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="AspNetWebFromJQueryUI.Index" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
    <link type="text/css" href="Content/themes/base/jquery.ui.all.css" rel="Stylesheet" />	
    <script type="text/javascript" src="/Scripts/jquery-1.6.4.js"></script>
    <script type="text/javascript" src="/Scripts/jquery-ui-1.8.16.js"></script>
    </asp:Content>
    <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <script>
        $(function () {
            $("#Accordion").accordion();
        });
    	</script>
    <div id="ResultDiv" runat="server">
    </div>
    
    </asp:Content>
    

    Einzig das Referenzieren von JQuery und JQueryUI sollte beachtet werden. Hierzu findet man auf JQueryUI die entsprechende Anleitung im Get started Teil.

    2. Den Helper für das Accordion

    Nun kommen wir zum Helper für die Erstellung des Markups für das Accordion, der wie folgt aufgebaut ist:

    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    
    namespace AspNetWebFromJQueryUI.Controls
    {
        /// <summary>
        /// Responsible for creating an accordion base
        /// for using it with JQueryUI
        /// </summary>
        public static class AccordionHelper
        {
            /// <summary>
            /// Returns the complete HTML code for the accordion.
            /// </summary>
            /// <typeparam name="T">The model type.</typeparam>
            /// <param name="datasource">The items to show in the accordion</param>
            /// <see cref="http://jqueryui.com/demos/accordion/"/>
            /// <returns>The HTML Markup for an accordion in JQuery manner</returns>
            public static HtmlGenericControl CreateAccordion<T>(IEnumerable<T> datasource)
            {
                HtmlGenericControl accordion = new HtmlGenericControl {TagName = "div", ClientIDMode = ClientIDMode.Static, ID = "Accordion"};
                accordion.InnerHtml = CreateContentDiv(datasource);
    
                return accordion;
            }
    
            /// <summary>
            /// Creates the content div's for putting the model
            /// content to it.
            /// </summary>
            /// <typeparam name="T">The model item (for example from the businessmodel)</typeparam>
            /// <param name="datasource">The items that are the datasource for the accordion</param>
            /// <returns>The HTML string for the accordion</returns>
            private static string CreateContentDiv<T>(IEnumerable<T> datasource)
            {
                StringBuilder sb = new StringBuilder();
    
                if (datasource != null)
                    for (int index = datasource.Count() - 1; index >= 0; index--)
                    {
                        if (index == 0)
                        {
                            sb.AppendLine(string.Format("<h3><a href=\"#\">{0}</a></h3>", CreateTitleElement(datasource.ToList()[index])));
                            sb.AppendLine(string.Format("<div>{0}</div>", CreateContentItemFromModel(datasource.ToList()[index])));
                        }
                        else
                        {
                            sb.AppendLine(string.Format("<h3><a href=\"#\">{0}</a></h3>", CreateTitleElement(datasource.ToList()[index])));
                            sb.AppendLine(string.Format("<div>{0}</div>", CreateContentItemFromModel(datasource.ToList()[index])));
                        }
                    }
    
                return sb.ToString();
            }
    
            /// <summary>
            /// Foreach property in the model item it will be crated a desing
            /// like label: value of the property.
            /// </summary>
            /// <typeparam name="T">The model object</typeparam>
            /// <param name="model">The model item to treat</param>
            /// <returns>The content for the contentItems in the accordion</returns>
            private static string CreateContentItemFromModel<T>(T model)
            {
                StringBuilder sb = new StringBuilder();
                model.GetType().GetProperties().ToList().ForEach(property => sb.AppendLine(string.Format(@"{0}: {1} <BR/>", property.Name, property.GetValue(model, null))));
    
                return sb.ToString();
            }
    
            /// <summary>
            /// Takes the first property of an object an set's it 
            /// as title.
            /// </summary>
            /// <typeparam name="T">The model object</typeparam>
            /// <param name="model">The model item to treat</param>
            /// <returns>The content for the title in the accordion</returns>
            private static string CreateTitleElement<T>(T model)
            {
                StringBuilder sb = new StringBuilder();
                PropertyInfo info = model.GetType().GetProperties().FirstOrDefault();
                string title = string.Format(@"{0}: {1}", info.Name, info.GetValue(model, null));
    
                return title;
            }
        }
    }
    

    Was macht der Code?

    • Er erstellt das Grundgerüst in welchem die Items reingepflanzt werden.
    • Er erstellt einen Titel aus dem ersten Property des Modells und setzt diesen mit dem Wert des ersten gefundenen Properties von der Modellklasse
    • Ist alles erledigt, gibt er den gesamten erstellen Markup zurück

    3. Die Modellklasse

    Meistens findet ja so ein Accordion bei einem Newsreader Verwendung. Eine solches News-Items hat die folgenden Eigenschaften:

    • Titel
    • Inhalt
    • Veröffentlichungsdatum

    Nachstehend die sehr einfache Modellklasse für ein News-Item.

    using System;
    
    namespace AspNetWebFromJQueryUI.Model
    {
        public class NewsItem
        {
            public string Title { get; set; }
    
            public string Content { get; set; }
    
            public DateTime PublishDate { get; set; }
    
        }
    }
    

    Wirklich ganz trivial…

    4. Einbindung in unsere Seite

    Die Einbindung in die Seite geschieht dann wie folgt:

    • ResultatDiv der Seite mit einem runat=“server“ versehen
    • Im Codebehind dem Control das generierte Accordion hinzufügen.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using AspNetWebFromJQueryUI.Controls;
    using AspNetWebFromJQueryUI.Model;
    
    namespace AspNetWebFromJQueryUI
    {
        public partial class Index : System.Web.UI.Page
        {
            protected void Page_Init(object sender, EventArgs e)
            {
                //Create dummyData
                List<NewsItem> datasource = new List<NewsItem>();
    
                for (int index = 0; index < 10; index++)
                {
                    datasource.Add(new NewsItem() { Content = string.Format(@"Neuigkeitsinhalt {0}", index.ToString()), 
                                                    Title = string.Format(@"Neuigkeit {0}", index.ToString()), 
                                                    PublishDate = DateTime.Now.Subtract(new TimeSpan(index, index, index)) });
                }
                
                this.ResultDiv.Controls.Add(AccordionHelper.CreateAccordion(datasource.OrderByDescending(n => n.PublishDate)));
                
            }
        }
    }
    

    Somit wird beim Starten, alles in einem schönen von JQuery verwalteten Accordion gerendet.

    ACHTUNG: Reflection funktioniert nicht in Abhängigkeit zu den .NET Sicherheitseinstellungen, daher muss zuerst überprüft werden, ob mit Reflection gearbeitet werden kann.

    Für Anregungen und Fragen würde ich mich auf einen Kommentar freuen

    kick it on dotnet-kicks.de

Vorlagen mal anders…

… und die Möglichkeit auch ohne JQuery Templates auszukommen. Wie soll man das verstehen? Nun ganz einfach, wenn wir eine XML basierte Daten getriebene Webanwendung haben, so ist es nicht immer notwendig mit JQuery Templates das Verschönern der Seite vorzunehmen. Hierzu kann XML /XSL(T) ein wahres Traumpaar sein. Anhand von drei Beispielen soll dann auch gezeigt werden wie es mit dem XMLControl von ASP.NET, rein Client seitig und rein Code-behind bewerkstelligt wird.

XML Data Control

Zuerst erstellen wir uns eine neue ASP.NET WebForms Anwendung und fügen in unsere Seite das XML Data Control ein.

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="XMLDataDrivenWeb._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>
        XML Driven Web Development
    </h2>
    <p>
        XML DataControl to apply XSL to the XML Document.

        <asp:Xml ID="XmlContainer" runat="server" 
            DocumentSource="~/App_Data/KontaktAngaben.xml" 
            TransformSource="/App_Data/KontaktFormatierung.xsl" onload="XmlContainer_Load" ></asp:Xml>
    </p>
</asp:Content>

Damit wir das Control mit den notwendigen Daten füttern können, erstellen wir zwei Dateien im App_Code Ordner.

Die XML Datendatei
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="KontaktFormatierung.xsl"?>
<Kontakangaben>
    <Person>
        <Salutation>Herr</Salutation>
        <FirstName>Hans</FirstName>
        <LastName>Müller</LastName>
        <Street>Müllerstrasse 14</Street>
        <City>3205 Müllhausen</City>
        <Phone>0132542254</Phone>
        <Phone>0123655447</Phone>
        <Role>Präsident</Role>
        <EMail href="hans.mueller@muellergmbh.com">Müller Hans</EMail>
    </Person>
    <Person>
        <Salutation>Frau</Salutation>
        <FirstName>Heidi</FirstName>
        <LastName>Müller</LastName>
        <Street>Müllerstrasse 14</Street>
        <City>3205 Müllhausen</City>
        <Phone>0766778939</Phone>
        <Phone>0324546688</Phone>
        <Role>Vizepräsidentin</Role>
        <EMail href="heidi.mueller@muellergmbh.com">Heidi Müller</EMail>
    </Person>
</Kontakangaben>

Die dazugehörige XSL(T) Datei sieht dann wie folgt aus.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <!-- Define the outputtype -->
    <xsl:output method="html"/>
    <xsl:template match="/*">
        <table class="tableStyle">
            <tr>
                <th>Kontaktangaben</th>
                <th>Rolle</th>
                <th>Telefon(e)</th>
                <th>E-Mail</th>
            </tr>
            <xsl:for-each select="Person">
                <tr>
                    <td>
                        <xsl:value-of select="FirstName"/> <xsl:value-of select="LastName"/>
                        <br/>
                        <xsl:value-of select="Street"/>
                        <br/>
                        <xsl:value-of select="City"/>
                    </td>
                    <td>
                        <xsl:value-of select="Role"/>
                    </td>
                    <td>
                        <ul>
                            <xsl:for-each select="./Phone">
                                <li>
                                        <xsl:value-of select="."/>                            
                                </li>
                            </xsl:for-each>
                        </ul>
                    </td>
                    <td>
                        <a>
                            <!-- The xml attribute sets the value for the <a> tag and adds an attribute to it-->
                            <xsl:attribute name="href">mailto:<xsl:value-of select="../Person/EMail/@href"/></xsl:attribute>
                            <xsl:value-of select="../Person/FirstName"/>
                            <xsl:value-of select="../Person/LastName"/>
                        </a>                                                                                                                               
                    </td>
                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
</xsl:stylesheet>

Wichtig ist dass der output mode auf HTML gestellt wird, ansonsten wird es mit dem Link in der E-Mail Adresse schwierig diesen korrekt darzustellen.

Der Ouput wird dann automatisch vom XML Control gerendert.

Reine clientseitige Verarbeitung

Eine weitere Möglichkeit, für alle die JavaScript über alles lieben, ist mit JQuery die Transformation vorzunehmen.

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="XMLDataDrivenWeb.Index" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">    
    <script type="text/javascript" language="javascript">
        var xml = null;
        var xsl = null;

        $(document).ready(function () {

            $.ajax({
                type: "GET",
                url: "XMLData/KontaktAngaben.xml",
                data: "{}",
                contentType: "application/xml; charset=utf-8",
                dataType: "xml",
                success: function (msg) {
                    xml = msg;
                },
                error: function (msg) {
                    alert(msg);
                }
            });

            $.ajax({
                type: "GET",
                url: "XMLData/KontaktFormatierung.xsl",
                data: "{}",
                contentType: "application/xml; charset=utf-8",
                dataType: "xml",
                success: function (msg) {
                    xsl = msg;
                    TransformXml();
                },
                error: function (msg) {
                    alert(msg);
                }
            });
        });

        function TransformXml() {

            $('#ResultDiv')[0].innerHTML = xml.transformNode(xsl);
        }
    </script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div id="ResultDiv">

</div>
</asp:Content>
Code behind Bearbeitung

Zu guter letzt kann alles natürlich mit C# erledigt werden, was eigentlich auch keine Hexerei darstellt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using System.Xml;
using System.Xml.Xsl;
using System.IO;
using System.Text;

namespace XMLDataDrivenWeb
{
    public partial class PureDotNet : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            GetDocument();
        }

        private void GetDocument()
        {
            XmlDocument x = new XmlDocument();
            x.Load(XmlReader.Create(Server.MapPath(@"/App_Data/Kontaktangaben.xml")));

            XslCompiledTransform transform = new XslCompiledTransform();
            transform.Load(XmlReader.Create(Server.MapPath(@"/App_Data/KontaktFormatierung.xsl")));
            transform.Transform(x.BaseURI, Server.MapPath(@"/App_Data/Result.xml"));

            // Loading the generated result file
            string[] xmlResult = File.ReadAllLines(Directory.GetFiles(Server.MapPath(@"/App_Data")).ToList().Where(f => f.ToLower().Contains("result")).FirstOrDefault());
            
            xmlResult.ToList().ForEach(s =>
                                           {
                                               ResultDiv.InnerHtml += s;
                                           });
            
        }
    }
}

Zu guter letzt soll das Ganze auch ein wenig ansprechend aussehen. Dies machen wir wie gewohnt über CSS. Die folgenen Styles werden nachfolgend aufgeführt:

th 
{
    font-size: x-large;
    font-family: cursive;
    background-color: silver;  
    padding: 2px;    
}

td 
{
    font-size:medium;
    font-style: normal;
    border: thin solid black;
}

Wie wir sehen gibt es immer mehrere Wege nach Rom und das Ganze, ach Microsoft will C# etc. abschaffen und nur noch Richtung JavaScript gehen, eher übertrieben. Es kommt immer noch auf uns Entwickler an wie wir eine Lösung zu einem Problem finden.

Anhand dieses einfachen Beispiels sehen wird, dass es möglich ist für ein Problem die eine oder andere Lösung zu wählen, je nachdem was einem beliebt.

Für Anregungen und Kritik bin ich gerne offen

kick it on dotnet-kicks.de

CSS-Praxisbeispiel Teil 01

Damit diese Darstellung realisiert werden konnte habe ich die Div’s in Parent und Subcontainer unterteilt. Damit man sie eindeutig in der CSS ersehen kann, habe ich sowohl die id der Div’s wie auch den Stylesheet Klassenname gleich benennt.

   1: /* Container for the labeledTextBox enddate and the enddate image. */
   2: .EndDateContainer
   3: {
   4:     width:33%;
   5:     float:left;
   6:     margin:1px 1px 1px 1px;
   7: }
   8: /* This container contains the labeledtextbox for the enddate */
   9: .EndDateLabeledTextBoxContainer
  10: {
  11:     width:94%;
  12:     float:left;
  13:     margin:1px 1px 1px 1px;
  14: }
  15: /* Container enddate image. */
  16: .EndDateImageContainer
  17: {
  18:     width:4%;
  19:     float:right;
  20:     margin:1px 1px 1px 1px;
  21: }
  22:  
  23: /* This Container holds a div for the labeledTextbox that is the Step-description */
  24: .StepDescription
  25: {
  26:     width:100%;
  27:     float:left;
  28:     margin:1px 1px 1px 1px;
  29: }
  30: /* This is the contariner for the labeledTextbox that contains the step description */
  31: .StepDescriptionContainerLabeledTextBox
  32: {
  33:     width:66%;
  34:     float:left;
  35:     margin:1px 1px 1px 1px;
  36: }
  37:  
  38: /* Holds tow divs for the step insert and step delete button */
  39: .ButtonContainerOne
  40: {
  41:     width:33%;
  42:     float:right;
  43:     margin:1px 1px 1px 1px;
  44: }
  45: /* Container for the step insert button*/
  46: .ButtonStepInsert
  47: {
  48:     width:48%;
  49:     float:left;
  50:     margin:1px 1px 1px 1px;
  51: }
  52: /* Container for the step delete button*/
  53: .ButtonStepDelete
  54: {
  55:     width:48%;
  56:     float:right;
  57:     margin:1px 1px 1px 1px;
  58: }
  59:  
  60: /* This container holds the containers for the other buttons and the labeledtextbox for tasktype*/
  61: .StepTaskTypeContainer
  62: {
  63:     width:100%;
  64:     float:left;
  65:     margin:1px 1px 1px 1px;
  66: }
  67:  
  68: /* Contains the labeledtextbox for tasktype of the step*/
  69: .StepTaskTypeContainerLabeledTextBox
  70: {
  71:     width:66%;
  72:     float:left;
  73:     margin:1px 1px 1px 1px;
  74: }
  75:  
  76: /* Holds tow divs for the step deactivate and step edit button */
  77: .ButtonContainerTwo
  78: {
  79:     width:33%;
  80:     float:right;
  81:     margin:1px 1px 1px 1px;
  82: }
  83: /* Container for the step deactivate button*/
  84: .ButtonStepDiactivate
  85: {
  86:     width:48%;
  87:     float:left;    
  88:     margin:1px 1px 1px 1px;
  89: }
  90: /* Container for the step edit button*/
  91: .ButtonStepEdit
  92: {
  93:     width:48%;
  94:     float:right;
  95:     margin:1px 1px 1px 1px;
  96: }

Hier noch der CSS-Ausschnitt für die Buttons:

   1: .buttonnormal
   2: {
   3:     width: 98%;
   4:     font-weight:bolder;
   5:     height: 98%;
   6:     cursor: pointer;
   7:     background-image: url('../../../Images/button_large.jpg');
   8:     background-repeat: no-repeat;
   9:     font-family: Arial;
  10:     color: black;
  11:     background-color: White;
  12:     margin: 1px, 1px, 1px, 1px;
  13: }

Es ging leider ein bisschen lange bis ich das Ergebnis zu meiner Zufriedenheit hatte. (Ich weiss jetzt wieso ich nicht wirklich ASP.NET Seiten Designer sein will smile_regular, da hat Silverlight schon mehr zu bieten, aber das ist ja ein anderes Thema). Auf jedenfall denke ich mir dass das Ergebnis nicht schlecht geworden ist.

Design_to_realize

CSS Styles in ListView einbringen

Sicherlich wird der eine oder andere der per Zufall oder Suche auf diesen Eintrag stösst, sagen, dass ist doch klar, aber in der Hitze des Gefechts vergisst man halt so einiges.

Auslgangslage:

Ich habe ein ListView in welchem ich den ItemTemplate etc. schon festgelegt habe. Der Item Template darf ja nur Elemente aufweisen die runat=”server” aufweisen. Hier liegt auch schon der Hund begragen. Sobald ein Control das runat=”server” Attribut aufweist, generiert die ASP.NET-Engine eine eindeutige ClientID. Wenn num im CSS die ID, welche man in der DesignTime festgelegt hat ansprechen will, dann findet er diese nicht.

Als Beispiel:

<div id=”Test” runat=”server></div>

CSS Element-Tag #Test.

In der Laufzeit sieht dann die ID des div’s dann so aus: ctl_test und kann daher auch nicht mehr im CSS gefunden werden.

Lösung: Die einfachste Methode dieses Problems, ist den Style per CSS-Klasse zu hinterlegen und dann dem Control zuzuweisen.

Beispiel:

   1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CommercialOrderStepOverView.aspx.cs"
   2:     Inherits="Buraut.Ais.WebFrontEnd.Auftrag.CommercialOrderStepOverView" %>
   3: <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
   4:  
   5: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   6: <html xmlns="http://www.w3.org/1999/xhtml">
   7: <head id="Head1" runat="server">
   8:  
   9:     <script type="text/javascript">
   1:  
   2:         // This function collpase the headerpanel if a click
   3:         // on it occurs.
   4:         function Collapse(CommercialOrderStepOverView_Header_pnl, CommercialOrderStepOverView_Content_pnl) {
   5:             var contentpanel = document.getElementById(CommercialOrderStepOverView_Content_pnl);
   6:             var headerpanel = document.getElementById(CommercialOrderStepOverView_Header_pnl);
   7:  
   8:             if (contentpanel.style.display == 'block') {
   9:                 contentpanel.style.display = 'none';
  10:             } else {
  11:                 contentpanel.style.display = 'block';
  12:             }
  13:         }
  14:  
  15:     

</script>

  10:  
  11:     <title></title>
  12: </head>
  13: <body>
  14:     <form id="CommercialOrderStepOverView_Form" runat="server">
  15:     <asp:ScriptManager ID="CommercialOrderStepOverView_ScriptManager" runat="server">
  16:     </asp:ScriptManager>
  17:     <asp:ListView ID="CommercialOrderStepOverView_ListView" runat="server" ItemPlaceholderID="CommercialOrderStepOverView_Listview_ItemPlaceHolder"
  18:         DataKeyNames="ProcessHeader" OnItemCreated="CommercialOrderStepOverViewListView_ItemCreated">
  19:         <LayoutTemplate>
  20:             <div id="CommercialOrderStepOverView_Listview_ItemPlaceHolder" runat="server" style="display:block;">
  21:             </div>
  22:         </LayoutTemplate>
  23:         <ItemSeparatorTemplate>
  24:         <hr />
  25:         </ItemSeparatorTemplate>
  26:         <ItemTemplate>
  27:             <asp:Panel ID="CommercialOrderStepOverView_Header_pnl" runat="server" Style="width: 80%;
  28:                 height: 20px;">
  29:                 <%
   1: # string.Format("{0} {1}", ((Step)Eval("Element")).TaskType.Contributor, ((Step)Eval("Element")).TaskType.Number) 

%> <br /></asp:Panel>

  30:             <div >
  31:             <asp:Panel ID="CommercialOrderStepOverView_Content_pnl" runat="server" Style="width: 80%;  display:inherit;">               
  32:                 <div id="StartDateContainer" runat="server" class="StartDateContainer">
  33:                 </div>
  34:                 <div id="EndDateContainer" runat="server" class="EndDateContainer">
  35:                 </div>
  36:                 <div id="RoleContainer" runat="server" class="RoleContainer">
  37:                 </div>
  38:             </asp:Panel>
  39:             </div>
  40:         </ItemTemplate>
  41:     </asp:ListView>
  42:     </form>
  43: </body>
  44: </html>

Und hier noch das CSS:

   1: .StartDateContainer
   2: {
   3:     width:33%;
   4:     float:left;
   5:     background-color:Aqua;
   6:     height:100px;
   7:     position:relative;
   8: }
   9:  
  10: .RoleContainer
  11: {
  12:     width:33%;
  13:     float:right;
  14:     background-color:Blue;
  15:     height:100px;
  16:     position:relative;
  17: }
  18:  
  19: .EndDateContainer
  20: {
  21:     width:33%;
  22:     float:left;
  23:     background-color:Fuchsia;
  24:     height:100px;
  25:     position:relative;
  26: }