When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, November 10, 2004

Working with the WebSurvey Control's Results, Part 2

By Steve Stchur


  • Read Part 1

  • In Part 1 of this article we looked at the basics of the SurveyControl. In this second and final part, we'll take a deeper look at the XSL stylesheet responsible for rendering the XML answer file into appropriate HTML markup.

    A Detailed Look at the SurveyResult Control's Stylesheet File


    The SurveyResult control is really a pretty simple control. It works on the basis of simply using XSLT to transform an XML file into HTML suitable for display on a Web page. This transformation is completed by the SurveyResult on the survey, but you really don't even need ASP.NET to accomplish this. Most standards-compliant Web browsers can read an XSL file, and transform the XML associated with it into HTML. The obvious question then, regarding the SurveyResult control, is why did I even bother to create it, if HTML can be created from an XML file without the help of ASP.NET? There are two reasons. The first is that the AnswersFile generated by the WebSurvey control does not contain the questions to the survey. The SurveyFile contains those question, but of course, it does not contain the answers. In order to transform our XML, we need all of it in a single file. Part of what the SurveyResult control does is merge the two XML files together in memory in preparation for XSL transformation.

    The second reason is that some browsers can't handle XSL transformation (or at least not very well). The .NET Framework, however, has some built in classes that handle this XSL transformation, and since it executes server-side, we can rid ourselves of worrying about client-side browser capabilities with regards to XSLT. The only caveat is to make sure we design our stylesheet to render HTML that is compatible with most browsers. XSLT can get very tricky (especially when you start considering looping, recursion, and other programming techniques familiar to most programmers). The problem is that, unlike Java, C++ / C#, Visual Basic, etc, XSLT is not an procedural language. Rather, it is a declarative and functional language, which is relative unknown to programmers of the aforementioned languages.

    One of the key concepts I use in my stylesheet file is that of XSLT recursion. Because I have neither the knowledge nor the ability to explain this in great detail, I'd highly recommend checking out an article written by Jared Jackson of IBM: Use recursion effectively in XSL.

    - continued -

    Below is the code from the stylesheet.xsl file, with an explanation to follow:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method = "html" indent = "yes"/>

      <xsl:template match = "/">  
        <xsl:for-each select = "//SurveyResult/Question">

          <xsl:variable name = "type" select = "@type"/>
          <xsl:if test = "$type = 'mcss'">

            <xsl:variable name = "qid" select = "@id"/>      

            <p><b><xsl:value-of select = "//SurveyResult/Question[@id = $qid]/Statement"/></b></p>

            <xsl:variable name = "null" />
            <xsl:variable name = "total" select = "count(//SurveyResult/Answer[text() != $null and @questionId = $qid])"/>

            <div style = "margin-left: 15px;">  
              <xsl:for-each select = "//SurveyResult/Question[@id = $qid]/Responses/Response">
                <xsl:variable name = "res" select = "."/>
                <xsl:variable name = "sum" select = "count(//SurveyResult/Answer[text() = $res and @questionId = $qid])"/>
                <xsl:variable name = "per" select = "$sum div $total * 100"/>
                <xsl:variable name = "len" select = "round($per * 4)"/>

                <xsl:value-of select = "$res"/>: <xsl:value-of select = "(round($per * 100)) div 100"/>%
                <div><xsl:attribute name = "style">vertical-align: middle; margin-bottom: 15px; border: solid 1px; border-color: #99ccff #99ccff #336699 #336699; background-color: #6699cc; width: <xsl:value-of select = "$len"/>px;</xsl:attribute> </div>

              </xsl:for-each>    
            </div>

            <b>Total Votes: </b><xsl:value-of select = "$total"/>
            <hr/>

          </xsl:if>
        </xsl:for-each>
        
      </xsl:template>  
    </xsl:stylesheet>

    Let's examine this stylesheet in a bit of detail. Since the stylesheet is an XML document in and of itself, it begins with an XML declaration:

    <?xml version="1.0" encoding="ISO-8859-1"?>

    Next, comes the declaration for the XSL stylesheet according to the W3C recommendation:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    Following this, we have a line that indicates that our transformed output should be HTML, and that the HTML should be indented:

    <xsl:output method = "html" indent = "yes"/>

    Next we have <xsl:template match = "/">. The <xsl:template> element contains rules to apply when a specified node is matched. To match the whole document, we simply use match = "/". The next line defines the start of a loop. The <xsl:for-each> element functions exactly as you would guess. For every //SurveyResult/Question node, the code between <xsl:for-each...> and </xsl:for-each> will be executed (i.e. once for every Question).

    The first part of this loop creates a variable named, type and tests to see if its value is mcss (Multiple Choice, Single Selection). Note that when defining a variable, we simply name it in quotes (... name = "type"). However, when referring to the variable in subsequent code, we precede its name with a $, as in <xsl:if test = "$type = 'mcss'">.

    If the question's type is not MCSS, we'll ignore it, because our stylesheet is not setup to handle any other type of question. However, if it is an MCSS question, we'll begin generating the graphical results for that question. The next step is to determine the question's ID and then select it's "Statement" so we can display the question before we generate any graphical results. We do this through the use of the <xsl:value-of...> element. This functions a lot like Response.Write() in ASP.NET.

    Next, we want to determine the total number of results for this question (so we can calculate percentage). However, we want to be sure we avoid any questions that may have been left blank (in the event that you didn't make them required in the first place). To do this, we create a variable named, null and do not assign any value to it. We then create a variable named, total whose value is equal to the number of answer for this question that are not null. Now, we can begin generating the bars that represent each answer. I use a <div> to accomplish this, though it certainly could be done using tables, or images, or whatever. We've already got the total number of answers for this question, but now we need to determine how many people pick each particular response. This is accomplished with another <xsl:for-each...> loop.

    The first variable, res, stands for "Result" and is defined by select = ".". This means to select the value of the current XML node in the <xsl:for-each> loop. In this case, that means the text associated with whichever <Response> tag we're on in the loop. The next variable, sum, is the number of people who picked the current response as their answer for this question. The per variable stands for "Percent" and is simply the sum divided by the total multiplied by 100. Finally, the len variable stands for "Length" is the number of pixels long that the <div> will be to represent this response. I multiple by four (arbitrarily) just to make the bars a bit longer.

    The next two lines actually "print" the <div> to the screen along the various attributes that define it (color, border, size, etc). The <xsl:for-each> loop then ends, and I display the total number of results for this question using the <xsl:value-of> element. Finally, I write a simple <hr/> tag (Horizontal Rule) just to separate the results of each question.

    Conclusion


    If you weren't confused by the above explanation, you should feel very good about yourself. I got confused just writing it because, let's face it, XSLT is not always simple. The stylesheet I've provided for you should give you a good start for using the SurveyResult control, but don't be afraid to research XSLT and try modifying the file to suit your needs. There are endless possibilities beyond what I have developed to gussy up the results page, and I'd love to see what you can come up with.

    Feel free to email your comment, questions, suggestions, or stylesheet files at sstchur@yahoo.com.

    Happy Programming!

  • By Steve Stchur


    Attachments


  • Download the complete sample code (in ZIP format)

    About Me


    Stephen Stchur is a software development engineer with Microsoft and the creator of two open source project's on Microsoft's CodePlex web site: WebSurvey and The Gimme Javascript Library. Feel free to drop him and email with comments or suggestions at sstchur@yahoo.com.



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article