Does it work?

ASP.NET MVC: Panel HtmlHelper extension methods with using syntax

1. Introduction
2. Description
2.1. Implementation
2.2. Strongly Typed Extension Methods
3. Full Code
3.1. PanelExtensions
3.2. MvcPanel
3.3. TestView
3.4. StronglyTypedView
3.5. ViewModel
3.6. Controller

1. Introduction

This is the porting of an old post I originally wrote in 2010 in my old blog.

In this article I will describe, step by step, my implementation of a simple Panel widget to be used in ASP.NET MVC web applications.

The goal is to be able to wrap some markup in a content area with a describing title at the top.

The usual way to wrap logically related content when writing Html is to encapsulate it in a <div> tag, so my Panel will be little more than this.

I will try to underline the few main concepts I used in my implementation so that anyone could implement a similar helper method.

→ top of post

2. Description

2.1. Implementation

I liked very much the smart use the ASP.NET MVC team made of IDisposable to support the using syntax in the BeginForm() helper method.

So I downloaded and browsed the source code trying to understand how the whole thing works.

I found the following two classes:

mvc3\src\SystemWebMvc\Mvc\Html\FormExtensions.cs

mvc3\src\SystemWebMvc\Mvc\Html\McvForm.cs

The FormExtensions class provides two extension methods:

Html.BeginForm() (with a bunch of overloads) and Html.EndForm()

Both the methods write directly to the HttpResponse output instead of returning an MvcHtmlString.

In fact all the BeginForm() overloads return an instance of MvcForm (more about it below) and the EndForm() method is void.

We can invoke the two methods at the right moment, at the beginning and at the end of the form, to render an opening <form> tag and a closing </form>

<% Html.BeginForm() %>
   ...form contents
<% Html.EndForm() %>

But what I like most is the possibility to invoke the BeginForm method in an inline code block like the following:

<% using (Html.BeginForm()) { %>
...form contents
<% } %>

This syntax is supported thanks to the fact that the BeginForm() methods return an instance of MvcForm.

This class implements IDisposable in a very particular way:

The body of the MvcForm.Dispose(bool disposing) method writes to the HttpResponse output a closing </form> tag as the FormExtensions.EndForm() does.

We can invoke the BeginForm() method inside a using block relying on the .Net Framework to invoke the Dispose() method on the instance of MvcForm returned by BeginForm() and so be sure that the form will be closed.

Based on what you read this far, we will create a PanelExtensions class in which we will add our
extension methods and a MvcPanel class that will be the analogous of the MvcForm class seen above.

The core functionality of the extension methods will make use of the TagBuilder utility class to output Html.

That class allows us to build an html tag with an object oriented syntax.

Our method won’t return an MvcHtmlString but will write directly to the HttpResponse and it will return an instance of the MvcPanel class.

The MvcPanel class will implement IDisposable.

The following code is an example of what the body of an extension method could be (with hardcoded values at the moment), based on the assumption that it will render a <div> for the title of the panel and a <div> for the content:

// title panel
TagBuilder titleTagBuilder = new TagBuilder("div");
titleTagBuilder.MergeAttributes(new RouteValueDictionary(new {title="title tooltip text"}));
titleTagBuilder.AddCssClass("titleClass");
titleTagBuilder.SetInnerText("This is the title");

// content panel
TagBuilder tagBuilder = new TagBuilder("div");
tagBuilder.MergeAttributes(new RouteValueDictionary(new {title="content tooltip text"}));
tagBuilder.AddCssClass("contentClass");

HttpResponseBase httpResponse = helper.ViewContext.HttpContext.Response;
httpResponse.Write(titleTagBuilder.ToString(TagRenderMode.Normal));
httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag));

return new MvcPanel(httpResponse);

As we can see the TagBuilder class exposes a constructor accepting a string with the name of the tag to write.

We then make use of the MergeAttributes() method. Such method accepts an IDictionary<string, object> that is supposed to contain pairs of key/values representing html attributes applicable to the tag specified in the constructor.

To invoke the MergeAttributes() method we create an instance of an anonymous type and then we pass it to a RouteValueDictionary class.

In this way we rely on it to parse correctly the anonymous type instance and to use it to initialize its own contents.

We then use the RouteValueDictionary instance as the IDictionary to pass as parameter to the MergeAttributes() method.

The AddCssClass() method accepts the name of a css rule to apply to the rendered tag and, eventually, the SetInnerText() method accepts some text that will be contained between the open and close tag.

When it’s time to write in the HttpResponse we use the ToString() method of the TagBuilder class.

Such method accepts a parameter of type: TagRenderMode.

TagRenderMode is an enumeration used to specify any of the four ways in which we can write a tag:

  • Normal: an open tag, some content and a close tag:
    some content
  • StartTag: only the open tag:
  • EndTag: only the closing tag:
  • SelfClosing: a tag without content:

We write to the HttpResponse the complete title div but only the opening tag of the content div.

Here you can see how the preceding code will be rendered:

<div class="titleClass" title="title tooltip text">
This is the title</div>
<div class="contentClass" title="content tooltip text">

The very last row in the code snippet returns to the caller an instance of the MvcPanel class, passing to the MvcPanel constructor an instance of the current HttpResponse.

MvcPanel accepts an HttpResponse in the constructor in order to be able, later, to write the closing tag to the response at the right moment. MvcPanel, in fact, implements the IDisposable interface. Its Dispose() method invokes a call to the HttpResponse.Write() method writing a div’s closing tag:

protected virtual void Dispose(bool disposing)
{
  if (!this.disposed)
  {
    this.disposed = true;
    this.httpResponse.Write("</div>
");
  }
}

If invoking the extension method with the using statement, the .Net framework runtime, upon exiting the using block, will call Dispose() on the MvcPanel instance returned by the extension method. We will then obtain the end of the panel (i.e.: a closing div tag).

In the PanelExtensions class we provide many overloaded extension methods accepting different sets of arguments and all of them, in the end, will call the overload really doing the job.

Such overload is the one with the greatest number of arguments.

The body of this extension method will be analogous to the code snippet above but with the arguments passed to the TagBuilder class arriving as method parameters passed from callers of the method:

public static MvcPanel BeginPanel(this HtmlHelper helper,
                  string title,
                  string titleCssClass,
                  string panelCssClass,
                  IDictionary<string, object> titleHtmlAttributes,
                  IDictionary<string, object> panelHtmlAttributes)
{
  // title panel
  TagBuilder titleTagBuilder = new TagBuilder("div");
  titleTagBuilder.MergeAttributes(titleHtmlAttributes);
  titleTagBuilder.AddCssClass(titleCssClass);
  titleTagBuilder.SetInnerText(title);

  // content panel
  TagBuilder tagBuilder = new TagBuilder("div");
  tagBuilder.MergeAttributes(panelHtmlAttributes);
  tagBuilder.AddCssClass(panelCssClass);

  HttpResponseBase httpResponse = helper.ViewContext.HttpContext.Response;
  httpResponse.Write(titleTagBuilder.ToString(TagRenderMode.Normal));
  httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag));
  return new MvcPanel(httpResponse);
}

The arguments represent:

  • the title displaied in the title panel
  • the css class rule to apply to the title panel
  • the css class rule to apply to the content panel
  • a dictionary containing additional html attributes for the title div
  • a dictionary containing additional html attributes for the content div

For each method accepting one or more IDictionary as parameters, an overload is provided accepting an instance of type object.

This is just a convenience to facilitate invoking the desired overload taking advantage of anonymous types.

In fact we gain the ability to pass the list of html attributes as an anonymous type like the following:new { title = "panel's tooltip" }

In the last paragraph you can find the full Code for PanelExtensions and MvcPanel.

As you can see in the code, the first and simplest extension method accepts only a parameter: the title to be displaied in the title panel.

All the overloads not explicitly accepting css class rules as parameters, rely on the view using them to define a couple of default css class rules (whose names you can find in constants inside the PanelExtensions class).

The default css class rules names are:

  • panelTitle
  • panel

An example of usage is:

<% using (Html.BeginPanel("About"))  { %>
    <span>Put content here.</span>
<% } %>

You must remember to import your namespace in the view that will use your extension methods:

<%@ Import Namespace="MvcPanelExample.Models.Html" %>

Alternatively you can import the namespace for the whole web application adding it to the web.config:

<pages>
      <namespaces>
        <add namespace="MvcPanelExample.Models.Html"/>
      </namespaces>
</pages>

At the paragraph 3.3. TestView you can find the code for a view rendering three panels obtained by invoking three different overloaded extension methods contained in the PanelExtensions class.

The view contains also the definition of two css class rules with the default names plus a pair of custom named css class rules:

  • customPanelTitle
  • customPanel

here is the result displaied by the browser:

→ top of paragraph

→ top of post

2.2. Strongly Typed Extension Methods

Eventually I added the strongly typed version of my extension methods.

I didn’t find much help on how to do it on the web, apart from this topic:

Is it possible to create a custom ASP.NET MVC strongly typed HTML Helper?.

Then I downloaded the ASP.NET MVC 3 beta version source code and had a look at how the strongly typed methods: TextBoxFor and LabelFor were implemented.

I had a look at the ModelMetadata class as well.

Starting from there I wrote my own strongly typed html helper method.

The content of the simplest overload is the following (as you can see at paragraph 3.1. PanelExtensions):

ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
return BeginPanel(html, metadata.Model as string);

The Asp.Net Mvc framework ModelMetaData class provides the utility method: FromLambdaExpression that parses the lambda expression passed as argument and returns an instance of ModelMetadata containing information about the model property returned by the lambda expression.

The Model property of ModelMetaData contains the value contained in such property, cast as object.

As we can see, the strongly typed overload delegates the actual rendering to the non strongly typed overload: BeginPanel.

We can than feel safe about XSS attacks because the values contained in the model are sanitized.
In fact the model property will be output in the html via the TagBuilder’s method: SetInnerText() and such method encodes the argument passed.

From my views I can then call any of the strongly typed versions of the Panel extension methods:

<% using (Html.BeginPanelFor(m => m.Title, "bluePanel"))
{ %>
<div>
    Content of my strongly typed panel.</div>
<% } %>

At the paragraph 3.4. StronglyTypedView you can find the code for a view rendering two panels obtained by invoking two different strongly typed extension methods contained in the PanelExtensions class.

here is the result displaied by the browser:


Obviously both the views in this article are accessible as long as I have the appropriate Controller action methods returning them as result.

For the strongly typed view I expect to have an action method like the following:

public ActionResult StronglyTypedView()
{
  PanelModel model = new PanelModel();
  model.Title = "Fake Title";
  model.SomeOtherText = "SomeOtherText Property Content";
  return View(model);
}

you can find the full code for the controller at the paragraph 3.6. Controller.

→ top of paragraph

→ top of post

3. Full Code

You can download the full source code here or copy the code in the following paragraphs.

3.1. PanelExtensions

namespace MvcPanelExample.Models.Html
{
    using System.Web;
    using System.Web.Mvc;
    using System.Collections.Generic;
    using System.Web.Routing;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq.Expressions;
    using System;

    public static class PanelExtensions
    {
        #region Constants

        const string TitleCssClass = "panelTitle";
        const string PanelCssClass = "panel";

        #endregion

        #region Methods

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title)
        {
            return BeginPanel(helper, title, new RouteValueDictionary(), new RouteValueDictionary());
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          object htmlAttributes)
        {

            return BeginPanel(helper, title, new RouteValueDictionary(), new RouteValueDictionary(htmlAttributes));
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          IDictionary<string, object> htmlAttributes)
        {

            return BeginPanel(helper, title, new RouteValueDictionary(), htmlAttributes);
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          object titleHtmlAttributes,
                                          object htmlAttributes)
        {

            return BeginPanel(helper, title, TitleCssClass, PanelCssClass, new RouteValueDictionary(titleHtmlAttributes), new RouteValueDictionary(htmlAttributes));
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          IDictionary<string, object> titleHtmlAttributes,
                                          IDictionary<string, object> htmlAttributes)
        {

            return BeginPanel(helper, title, TitleCssClass, PanelCssClass, titleHtmlAttributes, htmlAttributes);
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          string panelCssClass)
        {

            return BeginPanel(helper, title, TitleCssClass, panelCssClass);
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          string titleCssClass,
                                          string panelCssClass)
        {

            return BeginPanel(helper, title, titleCssClass, panelCssClass, new RouteValueDictionary(), new RouteValueDictionary());
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          string titleCssClass,
                                          string panelCssClass,
                                          object titleHtmlAttributes,
                                          object panelHtmlAttributes)
        {
            return BeginPanel(helper, title, titleCssClass, panelCssClass, new RouteValueDictionary(titleHtmlAttributes), new RouteValueDictionary(panelHtmlAttributes));
        }

        public static MvcPanel BeginPanel(this HtmlHelper helper,
                                          string title,
                                          string titleCssClass,
                                          string panelCssClass,
                                          IDictionary<string, object> titleHtmlAttributes,
                                          IDictionary<string, object> panelHtmlAttributes)
        {
            // title panel
            TagBuilder titleTagBuilder = new TagBuilder("div");
            titleTagBuilder.MergeAttributes(titleHtmlAttributes);
            titleTagBuilder.AddCssClass(titleCssClass);
            titleTagBuilder.SetInnerText(title);

            // content panel
            TagBuilder tagBuilder = new TagBuilder("div");
            tagBuilder.MergeAttributes(panelHtmlAttributes);
            tagBuilder.AddCssClass(panelCssClass);

            HttpResponseBase httpResponse = helper.ViewContext.HttpContext.Response;
            httpResponse.Write(titleTagBuilder.ToString(TagRenderMode.Normal));
            httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag));
            return new MvcPanel(httpResponse);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, new RouteValueDictionary(), new RouteValueDictionary(htmlAttributes));
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, new RouteValueDictionary(), htmlAttributes);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             object titleHtmlAttributes,
                                                             object htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, TitleCssClass, PanelCssClass, new RouteValueDictionary(titleHtmlAttributes), new RouteValueDictionary(htmlAttributes));
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             IDictionary<string, object> titleHtmlAttributes,
                                                             IDictionary<string, object> htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, TitleCssClass, PanelCssClass, titleHtmlAttributes, htmlAttributes);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             string panelCssClass)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, TitleCssClass, panelCssClass);
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             string titleCssClass,
                                                             string panelCssClass)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, titleCssClass, panelCssClass, new RouteValueDictionary(), new RouteValueDictionary());
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             string titleCssClass,
                                                             string panelCssClass,
                                                             object titleHtmlAttributes,
                                                             object panelHtmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, titleCssClass, panelCssClass, new RouteValueDictionary(titleHtmlAttributes), new RouteValueDictionary(panelHtmlAttributes));
        }

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcPanel BeginPanelFor<TModel, TValue>(this HtmlHelper<TModel> html,
                                                             Expression<Func<TModel, TValue>> expression,
                                                             string titleCssClass,
                                                             string panelCssClass,
                                                             IDictionary<string, object> titleHtmlAttributes,
                                                             IDictionary<string, object> panelHtmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            return BeginPanel(html, metadata.Model as string, titleCssClass, panelCssClass, titleHtmlAttributes, panelHtmlAttributes);
        }

        public static void EndPanel(this HtmlHelper helper)
        {
            HttpResponseBase httpResponse = helper.ViewContext.HttpContext.Response;
            httpResponse.Write("</div>
");
        }

        #endregion
    }
}

→ top of paragraph
→ top of post

3.2. MvcPanel

namespace MvcPanelExample.Models.Html
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Web;

    public class MvcPanel : IDisposable
    {
        #region Fields

        private bool disposed;
        private readonly HttpResponseBase httpResponse;

        #endregion

        #region CTOR

        public MvcPanel(HttpResponseBase httpResponse)
        {
            if (httpResponse == null)
            {
                throw new ArgumentNullException("httpResponse");
            }
            this.httpResponse = httpResponse;
        }

        #endregion

        #region Methods

        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]
        public void Dispose()
        {
            Dispose(true /* disposing */);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                this.disposed = true;
                this.httpResponse.Write("</div>
");
            }
        }

        public void EndPanel()
        {
            Dispose(true);
        }

        #endregion
    }
}

→ top of paragraph
→ top of post

3.3. TestView

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<!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 id="Head1" runat="server">
    <title>Example View</title>
    <style type="text/css">
    /* Top bar containing the title of the panel */
    .panelTitle
    {
      height:30px;
      background-color:#333366;
      color:white;
      border-top:solid 1px #666699;
      border-right:solid 1px #666699;
      border-left:solid 1px #666699;
      padding:5px 0px 0px 20px;
      font-weight:bold;
      font-size:large;
      overflow:auto;
    }

    /* Section of the screen containing omogeneous information */
    .panel
    {
      border:solid 1px #666699;
      background-color:white;
      padding:10px;
      overflow:auto;
    }

    .customPanelTitle
    {
      height:30px;
      background-color:#333366;
      color:white;
      border-top:solid 1px #666699;
      border-right:solid 1px #666699;
      border-left:solid 1px #666699;
      padding:5px 0px 0px 20px;
      font-weight:bold;
      font-size:large;
      overflow:auto;
      width:300px;
    }

    .customPanel
    {
      border:solid 1px #666699;
      background-color:Blue;
      color:White;
      border:solid 1px Blue;
      padding:10px;
      overflow:auto;
      width:300px;
    }
    </style>
</head>
<body>
<div>
    <% using (Html.BeginPanel("This is my first Panel"))
       { %>
<div>
            Content of my first panel.</div>
<% } %>

<% using (Html.BeginPanel("Panel with non default style class",
                              "customPanelTitle",
                              "customPanel"))
       { %>
<div>
            Content of my second panel.</div>
<% } %>

<% using (Html.BeginPanel("Panel obtained with all parameters",
                              "customPanelTitle",
                              "customPanel",
                              new { title = "panel's tooltip" },
                              null))
       { %>
<div>
            Content of my panel obtained specifying all parameters.</div>
<% } %></div>
</body>
</html>

 

→ top of paragraph
→ top of post

3.4. StronglyTypedView

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcPanelExample.Models.PanelModel>" %>

<!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>StronglyTypedView</title>
     <style type="text/css">
    /* Top bar containing the title of the panel */
    .panelTitle
    {
      height:30px;
      background-color:#333366;
      color:white;
      border-top:solid 1px #666699;
      border-right:solid 1px #666699;
      border-left:solid 1px #666699;
      padding:5px 0px 0px 20px;
      font-weight:bold;
      font-size:large;
      overflow:auto;
      width:300px;
    }

    /* Section of the screen containing omogeneous information */
    .panel
    {
      border:solid 1px #666699;
      background-color:white;
      padding:10px;
      overflow:auto;
      width:300px;
    }

    .bluePanel
    {
      border:solid 1px #666699;
      background-color:Blue;
      color:White;
      border:solid 1px Blue;
      padding:10px;
      overflow:auto;
      width:300px;
    }
    </style>
</head>
<body>
    <% using (Html.BeginPanelFor(m => m.Title))
    { %>
<div>
            Content of my first panel.</div>
<% } %> 

<% using (Html.BeginPanelFor(m => m.SomeOtherText, "bluePanel"))
    { %>
<div>
            Content of my second panel.</div>
<% } %>
</body>
</html>

→ top of paragraph
→ top of post

3.5. ViewModel

This is the view model used by the view in the previous paragraph:

namespace MvcPanelExample.Models
{
    public class PanelModel
    {
        public string Title { get; set; }

        public string SomeOtherText { get; set; }
    }
}

→ top of paragraph
→ top of post

3.6. Controller

namespace MvcPanelExample.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using MvcPanelExample.Models;

    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult TestView()
        {
            return View();
        }

        public ActionResult StronglyTypedView()
        {
            PanelModel model = new PanelModel();
            model.Title = "Title From the Model";
            model.SomeOtherText = "Some other text from the model";
            return View(model);
        }
    }
}

→ top of paragraph
→ top of post

Leave a Reply

%d bloggers like this: