Code Sprouts

Wholesome Bits Of Tech

ASP.Net MVC ActionButton + AjaxHelper jQuery Extension(s)

clock April 28, 2009 08:24 by author ColinW

Download the Code

I’ve been working on quite a few different projects using ASP.Net MVC 1.0, and I frequently encounter two things that aren’t directly supported with the first release:

  • The lack of an ActionButton
  • The inability to send a view back asynchronously that includes javascript + html

The first issue is somewhat trivially solved by including an AjaxHelper Form wrapped around an input, but that is more code than I care to write for dozens of buttons scattered about my project(s)*.

For the second issue, the workaround I used to use is to include all the javascript that I need included in the base page.  It works, but it makes for a rather cluttered mess, and you needlessly include javascript that some users may never use ( or possibly include javascript for features a user shouldn’t have access to ).

A New AjaxHelper With jQuery

By replacing the existing AjaxHelper with a new one that utilizes jQuery, I’ve been able to implement both an ActionButton AND a way to return Views that include both javascript + html.

I’ve included a sample project containing the new helper extensions & the necessary javascript file used by the new form methods.  If you run the sample project, I’ve included a small example of the ActionButton & asynchronous javascript/html return:

image 

In order to use the project files in a different project, you’ll need to do a few things.

1) Include the files located in the sample project under AjaxHelperHarness.UI.Ajax somewhere in a dll referenced by your application:

image

2)  Include the jquery.ajaxform.js file into your web application:

image

3) Replace the current System.Web.Mvc.Ajax namespace with the one that houses the new AjaxHelper files ( in this case AjaxHelperHarness.UI.Ajax):

<namespaces>
    <add namespace="System.Web.Mvc"/>
    <add namespace="System.Web.Mvc.Html"/>
    <add namespace="AjaxHelperHarness.UI.Ajax"/>
    <add namespace="System.Web.Routing"/>
    <add namespace="System.Linq"/>
    <add namespace="System.Collections.Generic"/>
</namespaces>

4) Reference the jquery.ajaxform.js script file in either your master page or the content page:

<script type="text/javascript" src="<%= Url.Content("~/Scripts/jquery.ajaxform.js") %>">
</script>

And you’re ready to roll!

The signatures are the same as the old AjaxHelper, so you won’t need to alter any code in order to utilize the enhanced extension methods.

Future Enhancements

While the current version of the AjaxHelper works well, I’m still debating a better way to handle form functions currently added thorugh the external javascript file.  I’d love to be able to have them automatically injected into the page, but I haven’t come up with a clean way to do so.  I’d love to use something similar to RegisterScriptInclude like we see in WebForms.

- Colin

 

* – On a more philosophical thought, I’ve been puzzled why there’s an ActionLink in the AjaxHelper and not an ActionButton.  In an ajax-enabled world, I view a link as something that will actually cause a page referesh, and a button as something that will cause an action to occur on a current page ( sans refresh ).  I’m guessing that the inclusion of the ActionLink in the AjaxHelper is just to keep it consistent with the HtmlHelper, but perhaps there’s a better one than that.



ASP.Net MVC DataList Extension

clock April 21, 2009 07:40 by author ColinW

The first release of Asp.Net MVC lacks an implementation of the DataList, so I’ve created a simple extension to the framework that provides basic DataList support.

I’ve based the DataList off of the excellent Grid extension found in the MvcContrib project.

Get the project files Here

Getting Started

In order to use the DataList extension you’ll need to reference an assembly that contains the DataList code, and add the correct namespace to the web.config.

In my sample solution, I’ve created a single project that contains the DataList code inside of an Asp.Net MVC Web project:

image

I reference the namespace in my Views/web.config:

<namespaces>
  <add namespace="DataListExtension.UI"/>
</namespaces>

Using the DataList

Once you’ve included the DataList into your project and setup the namespace, you’ll have a new extension method available in the HtmlHelper instance in your views.

The DataList uses the same pattern the MvcContrib Grid for constructing the actual DataList.  The HtmlHelper extension method returns an instance of the DataList<>, as do all the member functions in the DataList<> class.  This allows you to ‘chain’ calls to the DataList and configure it through the front-end code:

<% Html.DataList(Model.TestValues).Columns(3).Item(item =>
    {
        item.Template( dataItem => {
        %>
            <div style="border: solid 1px green;">
                This is number: 
                <span style="font-weight: bold;"><%= dataItem %></span>
            </div>  
        <%        
        });
    }).Render(); %>

What this code is doing is creating a DataList around the TestValues property of the model, settings the number of columns to 3, and then specifying the template to use for each item rendered:

image 

The call to render is required when using the <% %> syntax, and it will write directly to the current response buffer. Optionally, you can use the <%= %> syntax,  which will return a string ( which will eventually be rendered to the current response buffer ).

Along with using templates, you can also optionally use a partial view by calling item.PartialView(…).

Future Enhancements

The current version works for its initial intent of displaying vertically expanding list of items, however there’s an almost limitless amount of possible enhancements that can be added.  Some of the features I’ll be adding in the future include:

  • Paging
  • Adding the ability to pass attributes to the parent container
  • Adding Horizontal Repeating
  • Custom Renders to better control the HTML output

- Colin



Socket-Based Silverlight / WPF Chat Server

clock April 13, 2009 10:44 by author ColinW

One of the features introduced with Silverlight 2 is socket-based network communications.   While there are a few differences between Silverlight Sockets and WPF ( or Win Form ) Sockets, the code is surprisingly portable between the frameworks.

In an effort to show-case both the power of Silverlight 2 Sockets and their similarity to the standard .Net 3.5 Sockets ( used by WPF, Win Forms, Consoles, etc. ), I’ve created an example chat system that can host a front-end written in Silverlight or WPF.  The backend can be run by any .Net 3.5 application framework, and I’ve included both a WPF version as well as a Console version.

Download the Solution Here

Chat System Components

The core of the system is found in the ‘Chat’ project.  The core classes are:

  • Client – Handles a client connection and logic ( consumable by Silverlight and WPF )
  • Server – Manages all client connections and logic
  • UserSocket – Wraps all socket communication asynchronously, and is used by both the client & server
  • Message – All activity between client(s) and the server is done through messages.  Message contains methods to construct byte packets, or ( given a byte stream ) create a Message.

Silverlight-Specific Components

Silverlight connections cannot be made to a server unless the server you’re connecting to provides a valid policy file.  The way Silverlight handles this is by making a request to port 943 with the text “<policy-file-request/>”.  You must respond to the request with a valid AccessPolicy XML file that explicitly declares what URI’s may access the port you’re trying to connect to, and on what port you will accept connections.

I have wrapped all the functionality needed to serve an access policy into the class ‘PolicyServer’.  This class will listen on port 943, and respond to policy requests with either the default embedded policy file, or with any byte[] array you pass in ( it will need to be an access policy file that allows the port you’re trying to connect to, otherwise Silverlight will not connect ).

One of the restrictions to the Socket system in Silverlight 2 is that connections must be made on ports between 4502-4532. In order for the client to connect, the server must be running within that range, and the access policy file must specify the port(s) allowed.  The embedded example access policy file allow connections to any of the ports in that range.

Silverlight-Specific Code

All core components in the system are shared between Silverlight & WPF ( All files in the ChatSL project are linked to their counterparts in the Chat project ).  While almost all of the code is identical, there are two different sections of code that need to be different between clients.  I’m able to keep all the code in the same file by using # directives to check which library is being compiled.

The first directive conditional is in the creation of the EndPoint in the Client class:

#if SILVERLIGHT
endPoint = new DnsEndPoint(_host, _port);
#else
IPHostEntry hostEntry = Dns.GetHostEntry(_host);

IPAddress address = (from a in hostEntry.AddressList
where a.AddressFamily == AddressFamily.InterNetwork
select a).First();
endPoint = new IPEndPoint(address, _port);
#endif
 
For the Silverlight piece of this code, the Silverlight-only DnsEndPoint class is used to build the correct endpoint.  The non-Silverlight code does have the DnsEndPoint class, and we need to do a bit of work to retrieve a valid address for the host.  You might be tempted to just use code similar to the following:
 
endPoint = new IPEndPoint(IPAddress.Any, _port);

However on my machine, this pulls back the IPV6 protocol for the endpoint, which won’t work with our socket ( which we specify as IPV4 ).  To handle the issue, I query the address list for the correct protocol and select the first match.
 
Included Projects

The included solution contains 7 projects:

  • Chat – Contains the core classes of the chat system
  • ChatSL – Silverlight library that LINKS to the files in the Chat project.
  • ConsoleServer – Runs the Chat server in a console
  • SilverlightUI – Runs the client as a Silverlight Application
  • SilverlightUI.Web – Host the Silverlight Client Application
  • WPFServer – Runs the Chat server as a WPF application
  • WPFUI – Runs the client as a WPF Application

I’m contemplating adding this solution to Code Project, and to periodically make updates to the Server & Client to support new functionality.  If you have any questions with the code, or if you’d like to see new features added, leave a comment or feel free to email me at colinw@codesprouts.com

- Colin



Creating A DatePicker Extension In ASP.Net MVC

clock April 7, 2009 06:35 by author ColinW

Selecting a date comes up frequently in web applications, so in this post I’m going to be walking us through building an extension method that will generate a jQuery datepicker input that will seamlessly bind with model data on an action post-back. So let’s get started!

* Download the complete project here:

Downloading DatePicker Component & Setting Up References

The jQuery DatePicker script is not part of the core download.  It’s a separate download from the jQuery UI, so you’ll need to download that first.  The download page is structured as a package download for the various components, but you can successfully get everything you need by un-checking all but the datepicker component and clicking the download button:

image

Download the file and extract it into a (clean) directory. 

While the download does contain a custom javascript file to include the components you downloaded, we’re going to skip that script file and extract out only the datepicker components we need.

From the directory you extracted the download file, navigate to ‘development-bundle\themes\base’, and copy the contents of that directory into the ‘Content’ directory of your solution ( do this through visual studio and it’ll automatically include the files into the project):

image

Now navigate over to ‘development-bundle\ui’ in the directory you extracted the files to and copy ‘ui.core.js’ & ‘ui.datepicker.js’ into the ‘Scripts’ folder in your solution:

image

No Calendar is complete without an icon, so go ahead and grab this one:

http://www.codesprouts.com/Images/calendar.gif

And copy it into your ‘Content/images’ folder in the project:

image

That’s it for resources, but we need to setup references in order to use jQuery & access the css files.  I’m using a brand-new project and will be working in the ‘Views/Home/Index.aspx’ view, so I can add script & css references ot the ‘Views/Shared/Site.Master’ file and they will be available in the Index.aspx page.  I added the following references into the <head> element of the master:

<link href="../../Content/ui.all.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="../../Scripts/jquery-1.3.2.js"></script>
<script type="text/javascript" src="../../Scripts/ui.core.js"></script>
<script type="text/javascript" src="../../Scripts/ui.datepicker.js"></script>

And with that, we should be able to start playing with our DatePicker extension!

Working With The DatePicker

Before we can wrap everything up into an extension method, we need to get the base functionality working first.  If you’re using a brand new project, navigate to your Index page, and include the following code in the view:

Date: <input type="text" id="date" />
    
<script type="text/javascript">
     $(function() {
          ("#date").datepicker();
     });
</script>

Run the project, and you should now see a functioning date picker when you click in the textbox:

image 

Excellent!

In our date input above, we’re using the defaults for all options of the datepicker, so let’s change that a bit before we wrap this up into a helper extension.

I’d rather have the datepicker show immediately, and have an image button for the popup, so we’ll need to change the call to .datepicker() to the following:

$("#date").datepicker({
                showOn: 'button',
                buttonImage: '/Content/images/calendar.gif',
                duration: 0 
                });

You can find the description for all options at http://docs.jquery.com/UI/Datepicker .

If you run the site to view the lastest changes, you’ll now see a pretty calendar icon that can be clicked to popup the calendar:

image

Perfect!

Creating A Helper Extension

Now that we’ve created a working date picker, let’s put it into an extension method in order to clean up the UI & allow us to use it elsewhere.

Create a new folder in the root of the project, called ‘UI’, and create a new class called ‘HtmlHelperExtensions.cs’ in our new folder:

image

In the new class, add the following method to the class ( You’ll need to add a using clause to System.Web.MVC & System.Text ):

public static string DatePicker(this HtmlHelper helper, string name, string imageUrl, object date)
{
    StringBuilder html = new StringBuilder();
 
    // Build our base input element
    html.Append("<input type=\"text\" id=\"" + name + "\" name=\"" + name + "\"");
 
    // Model Binding Support
    if (date != null)
    {
        string dateValue = String.Empty;
 
        if (date is DateTime? && ((DateTime)date) != DateTime.MinValue)
            dateValue = ((DateTime)date).ToShortDateString();
        else if (date is DateTime && (DateTime)date != DateTime.MinValue)
            dateValue = ((DateTime)date).ToShortDateString();
        else if (date is string)
            dateValue = (string)date;
 
        html.Append(" value=\"" + dateValue + "\"");
    }
 
    // We're hard-coding the width here, a better option would be to pass in html attributes and reflect through them
    // here ( default to 75px width if no style attributes )
    html.Append(" style=\"width: 75px;\" />");
 
    // Now we call the datepicker function, passing in our options.  Again, a future enhancement would be to
    // pass in date options as a list of attributes ( min dates, day/month/year formats, etc. )
    html.Append("<script type=\"text/javascript\">$(document).ready(function() { $('#" + name + "').datepicker({ showOn: 'button', buttonImage: '" + imageUrl + "', duration: 0 }); });</script>");
 
    return html.ToString();
}

Since we’ve added it as an extension method, if we add the namespace into the view web.config, we’ll be able to call our new method from the HtmlHelper of the view. 

To add the namespace, open up your web.config ( you can either use the web.config in the ‘View’ folder, or go to the root and add it there ), and add the following into the namespaces element:

<add namespace="YourProjectNameGoesHere.UI"/>

In my example project, the name of the project is ‘DatePickerHarness’, so my complete namespaces element looks like the following:

<namespaces>
    <add namespace="System.Web.Mvc"/>
    <add namespace="System.Web.Mvc.Ajax"/>
    <add namespace="System.Web.Mvc.Html"/>
    <add namespace="System.Web.Routing"/>
    <add namespace="System.Linq"/>
    <add namespace="System.Collections.Generic"/>
    <add namespace="DatePickerHarness.UI"/>
</namespaces>

Now that we’ve got that in there, we can access the extension method from within our view:

image

Now that we have the abillity to call our extension method, let’s make this a bit more exciting and wrap everything into a form that will post back to an action in our controller.  First we’ll need to add our complete form to the frontend:

<% using (Html.BeginForm("AddAWeek", "Home"))
   { %>
<table>
    <tr>
        <td>
            Date:
        </td>
        <td>
            <%= Html.DatePicker("Date", "/Content/images/calendar.gif", 
                                this.ViewData["TheDate"]) %>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="submit" value="Add A Week" />
        </td>
    </tr>
</table>
<% } %>

And a bit of code to our controller:

public ActionResult AddAWeek(DateTime? Date)
{
    if (Date == null)
        Date = DateTime.Now;
 
    Date = Date.Value.AddDays(7);
 
    this.ViewData["TheDate"] = Date;
    
    return View("Index");
}

And then we can run it:

image

Changing the date to May 18th, and hitting ‘Add A Week’ displays the following after the post-back:

image

Fantastic!

The Wrap-Up

With just a little bit of work, we’ve now created a reusable component for displaying & capturing dates with the ASP.Net MVC Framework. Any view we create within the project now has the new DatePicker extension method available, significantly cutting down on the time it would take to code it from scratch each time.

The include project contains everything we’ve done here, and also includes a few overloads to make the DatePicker extension a bit more robust.  If you’re not happy with the way the datepicker works or how it looks, feel free to change the rendered script in the extension method(s) as you see fit.

Till next time!

- Colin



Welcome to BlogEngine.NET 1.5.0

clock April 1, 2009 20:00 by author Administrator

If you see this post it means that BlogEngine.NET 1.5.0 is running and the hard part of creating your own blog is done. There is only a few things left to do.

Write Permissions

To be able to log in to the blog and writing posts, you need to enable write permissions on the App_Data folder. If you’re blog is hosted at a hosting provider, you can either log into your account’s admin page or call the support. You need write permissions on the App_Data folder because all posts, comments, and blog attachments are saved as XML files and placed in the App_Data folder. 

If you wish to use a database to to store your blog data, we still encourage you to enable this write access for an images you may wish to store for your blog posts.  If you are interested in using Microsoft SQL Server, MySQL, VistaDB, or other databases, please see the BlogEngine wiki to get started.

Security

When you've got write permissions to the App_Data folder, you need to change the username and password. Find the sign-in link located either at the bottom or top of the page depending on your current theme and click it. Now enter "admin" in both the username and password fields and click the button. You will now see an admin menu appear. It has a link to the "Users" admin page. From there you can change the username and password.  Passwords are hashed by default so if you lose your password, please see the BlogEngine wiki for information on recovery.

Configuration and Profile

Now that you have your blog secured, take a look through the settings and give your new blog a title.  BlogEngine.NET 1.4 is set up to take full advantage of of many semantic formats and technologies such as FOAF, SIOC and APML. It means that the content stored in your BlogEngine.NET installation will be fully portable and auto-discoverable.  Be sure to fill in your author profile to take better advantage of this.

Themes and Widgets

One last thing to consider is customizing the look of your blog.  We have a few themes available right out of the box including two fully setup to use our new widget framework.  The widget framework allows drop and drag placement on your side bar as well as editing and configuration right in the widget while you are logged in.  Be sure to check out our home page for more theme choices and downloadable widgets to add to your blog.

On the web

You can find BlogEngine.NET on the official website. Here you'll find tutorials, documentation, tips and tricks and much more. The ongoing development of BlogEngine.NET can be followed at CodePlex where the daily builds will be published for anyone to download.

Good luck and happy writing.

The BlogEngine.NET team



MVC View Master Pages

clock March 25, 2009 06:47 by author ColinW

Once you’ve installed the MVC framework, there are two different types of Master Pages you can add to a project: the original Web Forms Master Page, and the new and improved ‘MVC View Master Page’.  You can use either one in an MVC application, but it’s highly recommended that you use the new MVC View Master Page in your projects:

image

The improved MVC version comes with MVC-related capabilities that you won’t get with a Web Forms version.  Of those numerous capabilities, some of the important ones are:

  • Support for typed Models
  • Ajax, Html, and Url Helpers
  • No default code-behind

If you already have existing Web Form Master Pages, you can change them to MVC View Master Pages by changing the inherits attribute of the Master Tag:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

Typically you’ll have a CodeBehind attribute with a Web Form Master Page, and it’s perfectly acceptable to leave it in there ( assuming you have custom code in that file ).

- Colin



ASP.Net MVC 1.0 Released

clock March 18, 2009 07:00 by author ColinW

Now Available for download!

Looks like the only real change since RC2 is that it comes with jQuery 1.3.2. Other than any possible incompatibility problems with the new jQuery version, your RC2 projects should compile and function properly.

- Colin



A Simple Javascript Timer

clock March 17, 2009 14:53 by author ColinW

While browsing through the jQuery javascript file, I noticed an interesting function used to return the number of milliseconds since January 1st, 1970:

function now() {
    return +new Date;
}

What’s interesting about this function is the use of the + sign before the new operator.  If you omit the + sign, you’ll end up with a regular date object, but including it returns just the number of milliseconds.  It is equivalent to creating a new Date instance and calling getTime() on that instance:

function now() {
    return new Date().getTime();
}

While both function achieve the same result, I really like the simplicity/elegance of the jQuery approach.  And since all elegant code needs a purpose, I decided to wrap it up in a simple javascript timer class:

function Timer() {
    this._start = 0;
    this._end = 0;
    this._isRunning = false;
}

Timer.prototype = {

    start: function() { 
            this._start = this._currentTime(); 
            this._isRunning = true;
        },
            
    stop: function() { 
            this._end = this._currentTime(); 
            this._isRunning = false;
        },
            
    elapsed: function() { 
            if ( this._isRunning )
                return this._currentTime() - this._start;
            else
                return this._end - this._start;
        },

    _currentTime: function() {
            return +new Date; 
        }
};

A simple way to use it would be to time a long running operation:

var timer = new Timer;
timer.start();

// Insert long running operation here...
timer.stop();

alert('Operation took ' + timer.elapsed() + ' ms.');

While our timer is relatively simple, it does allow the option to get the elapsed time while the Timer is still running:

var timer = new Timer;

timer.start();

// Insert an operation here...

var elapsedTimeA = timer.elapsed();

// Insert a second operation here....

timer.stop();

alert('First Operation took ' + elapsedTimeA + ' ms. Total operation took ' + timer.elapsed() + ' ms.');

And that’s all there is to using our simple Timer object!

Some possible enhancements that might be useful:

  • Adding a constructor parameter to specify auto-starting the Timer,
  • Implementing split times ( which would turn it more into a stopwatch class )
  • Providing different measurements for the return value ( Minutes, Seconds, Hours, etc. )

I’ll be revisiting the timer in future posts, so we may be seeing those features soon!

- Colin



Intro MVC Project - Code Camp Blogger

clock March 14, 2009 17:36 by author ColinW

I made a promise that I’d put up a detailed overview of what I discussed and built today before the end of the day, so let’s see if I can keep my word and get this post out before midnight.  Let’s get started!

As I mentioned in the talk, to get started developing ASP.Net MVC applications you’ll need to to have two things installed, Visual Studio 2008 and the latest version of the MVC Framework.  In order to get started with implementing the application the talk, you’re going to need to have a SQL Server database available.  One very cool thing I didn’t mention during the talk is that along with the MVC Framework being 100% free, it also works quite well with both the free version of Visual Studio 2008 ( Express ), and the free version of SQL Express 2008 ( also denoted ‘Express’ ).

You can download the complete project solution here

**Note**  If you use the solution file, you’ll still need to create a database with the blog table, and you’ll also need to change the web.config connection string to point to your database.

Creating the Project

The first order of business we’ll want to take care of is actually creating the MVC projects, which we achieve by adding a new project of the type ‘ASP.Net MVC Web Application’ from inside the Visual Studio add project screen:

 New MVC Project

We’ll be calling ours ‘CodeCampBlogger2009’, so enter that in and click on OK to get on to the next screen, the ‘Create Unit Test Project’:

image

As I mentioned in the talk, making a test project is really up to your development methodology.  Some folks are die-hard Test-Driven-Development fan, and some aren’t.  What’s great about the MVC framework is that it really lends itself well to Test Driven Development, but you don’t lose any features of the framework if you choose not to develop using TDD.

I’m opting to skip creating a Test Project in order to keep this more of an intro to MVC, so just select the ‘No…’ option and click OK. 

After the dialog, the project will be created for you and you’ll have the skeleton of a full-fledged MVC application:

image

While the skeleton created is actually a working application ( feel free to run it to check it out ), we’re going to delete the items created and start from scratch.  So go ahead and delete the items under the Controller, the Account & Shared folders from under Views, and also the Error.aspx & LogOnUserControl.ascx from under the Views/Shared folder:

image

Open up the Site.Master file and strip out all of the pre-canned HTML and turn it into something that looks like the following:

image

And now we’ve got a clean slate to start building the sample app!

Creating The Database

Let’s again start by building our database up, so add a new database server through Visual Studio:

image

After clicking OK, navigate the connection to the Tables Node and add a new table with the following:

image

Save the table as ‘Blog’ ( I find it easiest to just close the window and have it prompt you to save changes ), and we’ll now be ready to create our Entity Model for access to the database:

image

Create a folder called ‘Data’ in the root of the project, and then right-click, Add Item, and select ADO.NET Entity Data Model.  You can name the file anything you’d like, I chose ‘CodeCampBlogger2009Entities.edmx’:

image

The next screen after clicking ‘Add’ will bring you to the ‘Choose Model Contents’ screen where you can decide to either generate a model based upon an existing database, or create a new empty model.  We want to generate our model based upon our Blog data we just created, so go and select ‘Generate from database’ and click Next.

The next screen you’ll encounter will be the Choose Data Connection dialog, which will prompt you for which data connection you’d like to use.  Go ahead and select the connection you created in the database creation step above, and check the option to include sensitive data in the connection string ( not what you want to do on a production machine! ):

image

Make sure the entity connection settings in Web.config is checked, and then click Next.

The next dialog of the wizard will pop up, and it’s the dialog that will access your data connection and look for tables, views, and procedures to model.  Since we have only one table we’re going to be generating, go ahead and check the tables checkbox and click finish:

image

After a few moments, the wizard will have created out entity model, and we’ll now be ready to start adding in our MVC components.

Adding The Models

We’ll be needing two models for our sample application, one for holding a blog entry, and one to hold all blog entries and also to house our data calls to the entity framework.

Add two classes to the Models directory, one called BlogEntry.cs, and one called BlogData.cs. 

For the BlogEntry class, we’ll want it to be defined as follows:

public class BlogEntry
{
    public int BlogID { get; set; }
    public DateTime Posted { get; set; }
    public string PostedBy { get; set; }
    public string Text { get; set; }
    public string Title { get; set; }
}

And for BlogData, we’ll want it to look like this:

public class BlogData
{
    // Public property to return all blog entries inside of our strongly-typed views
    public BlogEntry[] AllBlogEntries
    {
        get { return GetBlogEntries(); }
    }

    // If we need to persist a BlogEntry across multiple post-backs ( say for instance validation 
    // fails and we want the user to fix an issue ), we'll need space in the model to hold the 
    // current object being worked with
    public BlogEntry BlogEntry
    {
        get;
        private set;
    }

    public BlogData()
        : this(new BlogEntry())
    {

    }

    public BlogData(BlogEntry blogEntry)
    {
        this.BlogEntry = blogEntry;
    }

    // We'll use a simple linq query to pull all blog entries out of the database and return them 
    // instances of our custom blog entry class
    public static BlogEntry[] GetBlogEntries()
    {
        using (Data.CodeCampBlogger2009Entities entities = new Data.CodeCampBlogger2009Entities())
        {
            var allBlogEntries = from blog in entities.Blog
                                 orderby blog.Posted descending
                                 select new BlogEntry() { 
                                     BlogID = blog.BlogID, 
                                     Posted = blog.Posted, 
                                     PostedBy = blog.PostedBy, 
                                     Text = blog.Text, 
                                     Title = blog.Title };

            return allBlogEntries.ToArray();
        }
    }

    // Inserts will be as simple as possible, we'll take our custom class and cram the data into a Blog Entity 
    // and save it to the database
    public static void InsertBlogEntry(BlogEntry newBlogEntry)
    {
        using (Data.CodeCampBlogger2009Entities entities = new Data.CodeCampBlogger2009Entities())
        {

            newBlogEntry.Posted = DateTime.Now;

            var blogEntry = new Data.Blog() { 
                Posted = newBlogEntry.Posted, 
                PostedBy = newBlogEntry.PostedBy, 
                Text = newBlogEntry.Text, 
                Title = newBlogEntry.Title };

            entities.AddToBlog(blogEntry);
            entities.SaveChanges();
        }
    }
}
 
And that’s all we’ll need to do with our models, and we can move on to the next piece of the application.
 
Adding The Controller
 
Right-click on the Controllers directory and select ‘Add Controller’ from the menu.  You’ll be prompted to name your controller, I called mine ‘BlogController’ and clicked ‘Add’:
 
image
 
Once you’ve got your BlogController class up and open, we’ll want to changed the class to have the two action methods we’ll need for our applciation, the ‘Index’ action and the ‘AddBlogEntry’ action:
 
public class BlogController : Controller
{
    public ActionResult Index()
    {
        return View(new BlogData());
    }

    public ActionResult AddBlogEntry(BlogEntry blogEntry)
    {
        blogEntry.Posted = DateTime.Now;

        BlogData.InsertBlogEntry(blogEntry);

        return View("Blog", new BlogData());
    }
}
 
For the Index action, I’d like to reiterate what I talked about during the presentation, and that is the fact that the MVC framework will infer the View name for you automatically if you do not specify a view name when calling the View method on the controller.  What happens in that case is that the framework will take a look at the current action ( in our case, Index ), and search for a view named Index in the default view paths ( /Views/{Controller Name}/{ViewName}  and /Views/Shared/{ViewName} ).  If it doesn’t find one, it’ll throw an exception.
 
In the talk, I started this controller with a regular post-back, and you might remember that instead of returning the View named ‘Blog’ in the AddBlogEntry Action, I was instead re-directing back to the Index method.  I’m going to skip the regular post-back here and just make our controller hand back what we’ll be needing for the Ajax functionality, and that is a partial-view that contains the blog entries and the blog form.
 
Adding The Views
 
We need two views to complete the sample application, so let’s go ahead and create them.  For the ‘Index’ view,  click anywhere within the Index action and select ‘Add View’:
 
image
 
We want this view to be our base page that shows all the content and submission form, so we’d like to have it use the Master page, and be strongly-typed to our BlogData model.  Go ahead and click ‘Add’ and have it create the view.
 
If you look at what happens when you click ‘Add’, you’ll see that Visual Studio creates a sub-folder under views named ‘Blog’, and adds our view to that directory.  Had we not initiated adding our view by clicking inside the Index action method, we' would have had to manually create the Blog folder and add the view directly to that folder.
 
Our new Index view should be filled out with the following HTML:
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Code Camp Blogger 2009
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Code Camp Blogger 2009</h2>
    
    <div id="blogData">
        <% Html.RenderPartial("Blog"); %>
    </div>

</asp:Content>

Note that we’re wrapping the partial view inside of a named div so that when we return the results of adding a blog post via ajax, we can replace what’s inside the div with what’s returned to us from the action ajax calls.

Now we’ll add our ‘Blog’ partial-view, but this time we’ll need to add it into our Blog sub-folder by right clicking and selecting Add –> View. This view will be rendered as a component of another view, so we’ll need to check ‘Create a partial view’ and skip selecting a master page ( it’ll be disabled while partial view is checked ).  Don’t forget to strongly type our view to our BlogData model:

image

Once we’ve go the ‘Blog’ partial-view created, we’ll want to add in the following HTML:

<table>
    <tr>
        <td style="vertical-align: top; width: 50%;">
            <fieldset>
                <legend><span style="font-weight: bold;">Entries:</span> </legend>
                <% foreach (CodeCampBlogger2009.Models.BlogEntry blogEntry in this.Model.AllBlogEntries)
                    { %>
                <h4>
                    <%= blogEntry.Title %></h4>
                <%= blogEntry.Text %>
                <h5>
                    Posted By
                    <%= blogEntry.PostedBy %>
                    @
                    <%= blogEntry.Posted.ToShortDateString() %>
                    <%= blogEntry.Posted.ToShortTimeString() %></h5>
                <hr />
                <% } %>
                
                <% if (this.Model.AllBlogEntries.Length == 0)
                    { %>
                    No Current Blog Entries
                <% } %>
            </fieldset>
        </td>
        <td style="vertical-align: top; width: 50%;">
            <fieldset>
                <legend><span style="font-weight: bold;">Add Post:</span> </legend>
                <% using ( Ajax.BeginForm("AddBlogEntry", "Blog",
new AjaxOptions() { UpdateTargetId = "blogData" }) ) { %> <table> <tr> <td style="vertical-align: top;"> Title: </td> <td> <%= Html.TextBox("Title") %> </td> </tr> <tr> <td style="vertical-align: top;"> Posted By: </td> <td> <%= Html.TextBox("PostedBy") %> </td> </tr> <tr> <td style="vertical-align: top;"> Text: </td> <td> <%= Html.TextArea("Text", new { rows = 8, cols = 40 }) %> </td> </tr> <tr> <td colspan="2" style="text-align: right;"> <input type="submit" value="Add Post" /> </td> </tr> </table> <% } %> </fieldset> </td> </tr> </table>

And that’s it for the views!

Adding Ajax Scripts And A Default Route

For Ajax functionality, we’ll need to add in the MVC javascript includes ( they’re left out of the master page header section by default on a new project ).  Open up the Site.Master file and add the following two script includes to the head section of the document:

<script src="<%= Url.Content("~/Scripts/MicrosoftAjax.debug.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/MicrosoftMvcAjax.debug.js") %>" type="text/javascript"></script>
                                    

One last thing we’ll need to do before we run our application is change the default route to point to our new BlogController and not the HomeController it’s set to when the project is first created.  To change the default route, open up the Global.asax.cs file in the root of the project, and change the ‘routes.MapRoute(…..’ method to be:

routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Blog", action = "Index", id = "" }  // Parameter defaults
            );

After making the last few changes above, we’re finished with our sample MVC application and we can see it in action.

The Final Result

If we now run the application, we’ll be able to see the results of all the work we’ve done up to this point.

Initial Page Load:

image

After a few posts:

image

 

A Few Thoughts

While the sample application implements the very core components of an MVC framework application, there are two pieces missing that are keeping it form being what I consider a proper demo application.  These missing components are Data Validation & Security, both of which are provided by the core MVC framework runtime. I’ll be covering these topics here on the blog in the coming weeks, so check back later on to see how I’ll be implementing them in this application.

And that’s it for recapping today’s presentation!

- Colin



We bid you farewell, Atlanta Code Camp 2009

clock March 14, 2009 15:18 by author ColinW

I’d like to give a big THANK YOU! to everyone that came out today to both my presentation as well as the entire Code Camp 2009 Event.  For my session the turn-out was rather remarkable, as I’d been predicting 15-20 people for the session and I ended up with more than 50! 

Along with thanking the attendees I’d also like to thank everyone that worked so hard to make the Code Camp possible, all of our sponsors, and of course, Georgia Gwinnett Collegefor providing an absolutely exceptional facility. 

I look forward to seeing you all next year!

- Colin