Archive for the ‘ASP.Net’ Category.

ASP.Net MVC: Single Page Application (SPA) from Scratch; A How To Step by Step

During my presentation to the Denver Visual Studio Users Group on Asp.net SPA on March 25, 2013, I created an example SPA application from scratch using Visual Studio 2012 with Asp.Net and MVC. The intent was to provide an understanding of the tools and technologies needed to build an Asp.net spa. The following are the steps I used to create the basic SPA entitled UserGroupSPA in VS2012.

The following technologies and templates were used or provided inspiration in creating the example:

Pre Setup

  1. Using Microsoft Web Platform Installer 4.5, install  Asp.Net and Web Tools 2012.2 Update.
    1. Note that as of this writing that update will be included in the future Visual Studio 2012 Update #2.
    2. In the Microsoft Web Platform installer it is listed as Asp.net and Web Tools 2012.2

Visual Studio Project

  1. Create new project
    1. ASP.Net MVC 4 Web Application named UserGroupSPA
      1. Choose Basic Asp.net MVC4 Project Template with Razor view engine
  2. Package Manager Console (NUGET)
    1. Get-Package –Update
      1. Update-Package jQuery
      2. … (update all packages which can be updated as reported)
      3. Update-Package knockoutjs
    2. Install-Package Breeze.Webapi

Create Viewable Web Page in MVC at Root

  1. In the Views Folder
    1. Delete Shared folder and all of its contents
    2. Delete the view _View Start.cshtml
    3. Create DVSUG directory in the Views folde
    4. Add the view index.cshtml into the DVSUG directory
  2. This is the code for index.cshtml
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
    
        @Styles.Render("~/Content/css")
    </head>
    <body>
        <h1>Denver Visual Studio Users Group Main</h1>
    </body>
    </html>
  3. Global.asax
    1. Comment out the line RegisterBundles such as:
      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      //BundleConfig.RegisterBundles(BundleTable.Bundles);
  4. App_Start folder
    1. Add C# class DVSUGConfig
      using System.Web.Optimization;
      
      [assembly: WebActivator.PostApplicationStartMethod(
          typeof(UserGroupSPA.App_Start.DVSUGConfig), "PreStart")]
      
      namespace UserGroupSPA.App_Start
      {
          public class DVSUGConfig
          {
              public static void PreStart()
              {
                  BundleConfig.RegisterBundles(BundleTable.Bundles);
              }
          }
      
      }
    2. Add C# class DVSUGRouteConfig
      using System.Web.Mvc;
      
      [assembly: WebActivator.PreApplicationStartMethod(
          typeof(UserGroupSPA.App_Start.DVSUGRouteConfig), "RegisterDVSUGPreStart", Order = 2)]
      
      namespace UserGroupSPA.App_Start
      {
          ///<summary>
          /// Inserts the DVSUGRouteConfig SPA sample view controller to the front of all MVC routes
          /// so that the DVSUGRouteConfig SPA sample becomes the default page.
          ///</summary>
          ///<remarks>
          /// This class is discovered and run during startup
          /// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
          ///</remarks>
          public static class DVSUGRouteConfig
          {
      
              public static void RegisterDVSUGPreStart()
              {
      
                  // Preempt standard default MVC page routing to go to DVSUG 
                  System.Web.Routing.RouteTable.Routes.MapRoute(
                      name: "DVSUGMvc",
                      url: "{controller}/{action}/{id}",
                      defaults: new
                      {
                          controller = "DVSUG",
                          action = "Index",
                          id = UrlParameter.Optional
                      }
                  );
              }
          }
      }
  5. Controllers directory
    1. Add controller DVSUGController (Empty MVC Controller)
  6. Run the application in IE (not chrome) in debug (F5)

Adding Toastr For Notifications

  1. Package Manager Console
    1. install-package toastr
  2. App_Start
    1. BundleConfig.CS change to:
      using System.Web;
      using System.Web.Optimization;
      
      namespace UserGroupSPA
      {
          public class BundleConfig
          {
              // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
              public static void RegisterBundles(BundleCollection bundles)
              {
                  bundles.IgnoreList.Ignore("*.intellisense.js");
                  bundles.IgnoreList.Ignore("*-vsdoc.js");
      
                  bundles.Add(
                              new ScriptBundle("~/scripts/UGScripts")
                              .Include("~/Scripts/jquery-{version}.js")
                              .Include("~/scripts/knockout-{version}.debug.js")
                              .Include("~/scripts/toastr.js")
                              .Include("~/scripts/Q.js")
                              .Include("~/scripts/breeze.debug.js")
                              );
      
                  bundles.Add(
                              new StyleBundle("~/Content/css")
                             .Include("~/Content/bootstrap.css")
                              .Include("~/Content/toastr.css")
                              );
              }
          }
      }
  3. Views\DVSUG\index.cshtml
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
    
        @Styles.Render("~/Content/css")
    
    </head>
    
    <body onload="toastr.info('Loading Main Body')">
    
        @Scripts.Render("~/scripts/UGScripts")
    
        <h1>Denver Visual Studio Users Group Main</h1>
    
    </body>
    
    </html>
  4. Run in IE again and verify the toast event “Loading Main Body” comes up. such as:DVSUG

Install Durandal

  1. Package Manager Console
    1. install-package Durandal
    2. install-package Durandal.Router
    3. install-package Durandal.Transitions
    4. install-package Twitter.Bootstrap
  2. App_Start\BundleConfig.CS
    using System.Web;
    using System.Web.Optimization;
    
    namespace UserGroupSPA
    {
        public class BundleConfig
        {
            // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
            public static void RegisterBundles(BundleCollection bundles)
            {
                bundles.IgnoreList.Ignore("*.intellisense.js");
                bundles.IgnoreList.Ignore("*-vsdoc.js");
    
                bundles.Add(
                            new ScriptBundle("~/scripts/UGScripts")
                            .Include("~/Scripts/jquery-{version}.js")
                            .Include("~/scripts/knockout-{version}.debug.js")
                            .Include("~/scripts/sammy-{version}.js")
                            .Include("~/scripts/toastr.js")
                            .Include("~/scripts/Q.js")
                            .Include("~/scripts/breeze.debug.js")
                            .Include("~/scripts/bootstrap.js")
                            .Include("~/scripts/moment.js")
                            );
    
                bundles.Add(
                            new StyleBundle("~/Content/css")
                    .Include("~/Content/ie10mobile.css")
                    .Include("~/Content/bootstrap.css")
                    .Include("~/Content/bootstrap-responsive.css")
                    .Include("~/Content/durandal.css")
                    .Include("~/Content/toastr.css")
                    .Include("~/Content/app.css")
    
                            );
    
            }
        }
    }

Work With Views as a SPA using Durandal

  1. UserGroupSPA\App directory (created by Durandal)
    1. Add the folders
      1. viewmodels
      2. views
    2. Add the javascript file main.js
      require.config({
          paths: { "text": "durandal/amd/text" }
      });
      
      define(['durandal/app', 'durandal/viewLocator', 'durandal/system', 'durandal/plugins/router'],
          function (app, viewLocator, system, router) {
      
             system.debug(true); // Outputs to the console the process, turn off for production
      
             app.start().then(function () {
                 toastr.options.positionClass = 'toast-bottom-right';
                 toastr.options.backgroundpositionClass = 'toast-bottom-right';
      
                 router.handleInvalidRoute = function (route, params) {
                     toastr.info('No Route Found: ' + route);
                 };
      
                 router.useConvention();
                 viewLocator.useConvention();
      
                 // Our three views
                 router.mapNav('home');
                 router.mapNav('speakers');
                 router.mapNav('events');
      
                 app.adaptToDevice(); // Touch devices to adapt to
      
                 app.setRoot('viewmodels/shell', 'entrance'); //Show the SPA at its root
             });
      
        });
  2. Views\DVSUG\index.cshtml changed to
    @using System.Web
    @using System.Web.Optimization
    <!DOCTYPE html>
    <html>
    <head>
        <title>Denver Visual Studio Users Group</title>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black" />
        <meta name="format-detection" content="telephone=no"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
        @Styles.Render("~/Content/css")
    </head>
    
        <body onload="toastr.info('Loading Main Body')">
    
            <div id="applicationHost">
            </div>
    
            @Scripts.Render("~/scripts/UGScripts")
    
            @if(HttpContext.Current.IsDebuggingEnabled) {
                <script type="text/javascript" src="~/App/durandal/amd/require.js" data-main="@Url.Content("~/App/main")"></script>
            } else 
            {
                <script type="text/javascript" src="~/App/main-built.js"></script>
            }
    
        </body>
    </html>
  3. App\viewmodels
    1. add event.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Events View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Events View Activated');
              return true;
          }
      });
    2. add home.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Home View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Home View Activated');
              return true;
          }
      });
    3. add shell.js
      define(['durandal/system', 'durandal/plugins/router'],
          function (system, router) {
              var shell = {
                  activate: activate,
                  router: router
              };
      
              return shell;
      
              function activate() {
                  return boot();
              }
      
              function boot() {
      
                  toastr.info('DVSUG Boot Loaded!');
                  return router.activate('home');
              }
      
          });
    4. add speakers.js
      define(function () {
          var vm = {
              activate: activate,
              title: 'Speakers View'
          };
      
          return vm;
      
          function activate() {
              toastr.info('Speakers View Activated');
              return true;
          }
      });
  4. App\views
    1. add events.html
      <section>
          <h2 class="page-title" data-bind="text: title"></h2>
      </section>
    2. add footer.html
      <nav class="navbar navbar-fixed-bottom">
          <div class="navbar-inner navbar-content-center">
              <span class="pull-left"><a href="http://omegacoder.com" target="_blank">OmegaCoder.com </a></span>
              <span class="pull-right"><a href="http://www.denvervisualstudio.net/" target="_blank">Denver Visual Studio User Group</a></span>
          </div>
      </nav>
    3. add home.html
      <section>
      
          <table>
              <tr><td><h2 class="page-title" data-bind="text: title"></h2></td></tr>
      
              <tr><td><h3>Next: Asp.Net SPA</h3></td></tr>
      
                    <tr><td><p>Come and find out all about it in this introduction to building Single Page Applications (SPA) which allow for rich client side interactions using JavaScript, HTML 5, and CSS in the ASP.NET world.</p>
                      <p>This introduction will familiarize you, the developer, to the templates available as well as an overview of the components which make up SPAs and how to put them together. This talk is a general how-to on being able to come up to speed on the technology and will focus on actually creating a page and cover the interactions of ASP.Net MVC with basic data transport.</p>
                      <p>You will see how ASP.NET Single-Page Applications (SPA) enable cutting-edge web developers to deliver rich, engaging web applications that surface information and interactivity in fresh and exciting new ways.</p>
            </td></tr>        
      
          </table>
      
      </section>
    4. add nav.html
      <nav class="navbar navbar-fixed-top">
          <div class="navbar-inner">
              <a class="brand" href="/">
                  <span class="title">Denver Visual Studio Users Group</span> 
              </a>
              <div class="btn-group" data-bind="foreach: router.visibleRoutes">
                  <a data-bind="css: { active: isActive }, attr: { href: hash }, text: name" 
                      class="btn btn-info" href="#"></a>
              </div>
      
          </div>
      </nav>
    5. add shell.html
      <div>
          <header>
              <!--ko compose: {view: 'nav'} --><!--/ko-->
          </header>
          <section id="content" class="main container-fluid">
              <!--ko compose: {model: router.activeItem, 
                  afterCompose: router.afterCompose, 
                  transition: 'entrance'} -->
              <!--/ko-->
          </section>
          <footer>
              <!--ko compose: {view: 'footer'} --><!--/ko-->
          </footer>
      </div>
    6. add speakers.html
      <section>
      
          <table>
              <tr><td><h2 class="page-title" data-bind="text: title"></h2></td></tr>
      
              <tr><td><h3>William Wegerson</h3></td></tr>
      
              <tr><td>
                      <p>William Wegerson is an Architect Developer who has been working here in Denver for over 20 years.</p>
                      <p>He has presented to the group before on such topics as Silverlight and Visual Studio releases (VS 2010 and 2012) as well as teaching labs to the members.</p>
                      <p>He has been awarded Microsoft MVP status since 2009 as a result of his community work such as this presentation as well as his blog and as an active resource on the MSDN forums and StackOverflow, answering questions of all types.</p>
             </td></tr>        
      
          </table>
      
      </section>
Share

Asp.Net C#: Upgrading Website to Latest Version Gotchas and How to Resolve

iStock_000015438998XSmallOne can get lost in the weeds when upgrading an asp.net .Net 1 website to anything from .Net 2 through .Net 4.  One of the biggest problems after the initial conversion is that when built each page or control throws a build error:

Could not load type ‘MyNamespace.myWebPage’.

The issue stems from the fact that things changed, made life easier for new projects, and namespaces which were required were removed and certain attributes changed. To resolve the above issue follow these steps presented in this article.

In the code behind file

  1. Remove the namespace scope. In the example below remove the highlighted lines:
    namespace MyNamespace
    {
        public class myWebPage : System.Web.UI.Page {}
    }
  2. Add the modifier partial to the class definition to make it look like this
    public partial class myWebPage : System.Web.UI.Page { ... }

In the aspx File

Which initially looks like this:

<%@ Page language="c#" AutoEventWireup="false"
                       Codebehind="myWebPage.aspx.cs"
                       Inherits="MyNamespace.myWebPage" %>
  1. Change the attribute Codebehind to CodeFile.
  2. Remove the namespace from the value found in the attribute Inherits.

The result should look like this:

<%@ Page language="c#" AutoEventWireup="false"
                       Codefile="myWebPage.aspx.cs"
                       Inherits="myWebPage" %>
Share

Problems One May Encounter When trying to use the Ajax Control Toolkit for the first time.

Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure “AjaxControlToolkit.Properties.Resources.resources” was correctly embedded or linked into assembly “AjaxControlToolkit” at compile time, or that all the satellite assemblies required are loadable and fully signed.

System.Resources.MissingManifestResourceException: Could not find any resources appropriate for the specified culture or the neutral culture.  Make sure “AjaxControlToolkit.Properties.Resources.resources” was correctly embedded or linked into assembly “AjaxControlToolkit” at compile time, or that all the satellite assemblies required are loadable and fully signed.

AjaxControlToolkit requires ASP.NET Ajax 4.0 scripts. Ensure the correct version of the scripts are referenced. If you are using an ASP.NET ScriptManager, switch to the AjaxScriptManager in System.Web.Ajax.dll, or use the ToolkitScriptManager in AjaxControlToolkit.dll.

The latest incarnation (3.6.097 as of this writing) of the asp.net Ajax Control Toolkit needs to be setup using its script manager and not the standard asp.net Script Manager.

So one way to avoid the above issues is to drag the ToolkitScriptManager (found in the control toolkit) onto the Form tag and then use any other Ajax Controls. Here is an example of it using the TabContainer:

<form id="form1" runat="server">
<asp:ToolkitScriptManager ID="ToolkitScriptManager2" runat="server"/>
<div>
    <asp:TabContainer ID="TabContainer1" runat="server" ActiveTabIndex="1">
        <asp:TabPanel runat="server" HeaderText="TabPanel1" ID="TabPanel1">
            <ContentTemplate><p>Hello</p></ContentTemplate>
        </asp:TabPanel>
        <asp:TabPanel ID="TabPanel2" runat="server" HeaderText="TabPanel2">
        <ContentTemplate><p>Hello2</p></ContentTemplate>
     </asp:TabPanel>
    </asp:TabContainer>
</div>
</form>
Share

Asp.Net C# Creating an Excel Document From Data without using Office Interops

Excel It is a common misconception that one needs the office interops to create Excel documents for the user to download when dealing with Asp.net on a server. Well this article shows one how to create an Excel document from just the data at hand, say from Linq entities, without being tied to any control nor having to use office interops!

How Does one Send the Data to the User?

Here is the shell which will encompass the code which creates the document. Basically one uses the response class to send the data back to the user, say on a button click or some other relevant event. By specifying that this object will be a ms-excel file and it has an .xls extension, that is half the battle right there.

Response.Clear();
Response.Buffer      = true;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader( "Content-Disposition", "attachment;filename=registrationData.xls" );
this.EnableViewState = false;

//  Code here to generate the xls document.
...

HttpContext.Current.Response.Write( /* String Buffer to Here */ );
HttpContext.Current.Response.End();

That was pretty easy to send the information to the user using a few C# classes which are globally exposed to the code-behind of our web page.

Generate an Excel document from Data in C#

Our goal is not to be tied to any control on the web page, though some controls such as the GridView have the ability to easily export a table which can be passed on. But for our purposes we want to simply extract the data from a linq query and generate the document. By keeping this example generic, one can adapt it to many different situations.

// We will be creating html on the fly via the
// string and html writers to create a table on the fly.
using ( StringWriter sw = new StringWriter() )
    using ( HtmlTextWriter htw = new HtmlTextWriter( sw ) )
    {
        //  Create a table to contain the grid
        Table table = new Table();

        //  Gridlines to box the cells
        table.GridLines = System.Web.UI.WebControls.GridLines.Both;

        // We are going to add the header row to the data first.
        List columns = new List() {
        "Sent", "Sent Date", "User Name", "Last Name", "First Name",
        "Work Phone", "Work Email",    "Number of Tickets", "Street Address 1",
        "Street Address 2",    "City", "State", "Zip Code" };

        // Each row will need to be declared upfront and cells added later.
        TableRow tRow = new TableRow();
        string value;

        foreach ( string name in columns )
            tRow.Cells.Add( new TableCell() { Text = name } );

        table.Rows.Add( tRow ); // Done! Header row added.


        // Note TableCell has a property BackColor. Once can set the
        // that within the cell such as BackColor = System.Drawing.Color.Blue
        // and that will carry into the Excel Document!

        // UserData is our Linq entity list and we will enumerate it to fill the into cells of the row
        // and subsequently into the table.
        foreach ( var usr in UserData )
        {
            tRow = new TableRow();

            value = ( usr.ticketsSent == null ) ? "N" : usr.ticketsSent.Value.ToString();
            tRow.Cells.Add( new TableCell() { Text = value } );

            value = ( usr.ticketsSentDate == null ) ? string.Empty : usr.ticketsSentDate.Value.ToShortDateString();

            tRow.Cells.Add( new TableCell() { Text = value } );
            tRow.Cells.Add( new TableCell() { Text = usr.userName } );
            tRow.Cells.Add( new TableCell() { Text = usr.lastName } );
            tRow.Cells.Add( new TableCell() { Text = usr.firstName } );
            tRow.Cells.Add( new TableCell() { Text = usr.workNumber } );
            tRow.Cells.Add( new TableCell() { Text = usr.workEmail } );
            tRow.Cells.Add( new TableCell() { Text = usr.ticketsRequested.Value.ToString() } );
            tRow.Cells.Add( new TableCell() { Text = usr.address1 } );
            tRow.Cells.Add( new TableCell() { Text = usr.address2 } );
            tRow.Cells.Add( new TableCell() { Text = usr.city } );
            tRow.Cells.Add( new TableCell() { Text = usr.state } );
            tRow.Cells.Add( new TableCell() { Text = usr.zipCode } );

            table.Rows.Add( tRow ); // We add each row to the table.
        }

        //  Translate/Render the table into the htmlwriter
        table.RenderControl( htw );

        //  Response needs the Htmlwriter
        HttpContext.Current.Response.Write( sw.ToString() );
        HttpContext.Current.Response.End();
       }

By simply creating a mock table we can load it and ship it out to the user as an automatic download.

Full Code

Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader( "Content-Disposition", "attachment;filename=registrationData.xls" );
this.EnableViewState = false;

using ( StringWriter sw = new StringWriter() )
    using ( HtmlTextWriter htw = new HtmlTextWriter( sw ) )
    {
        //  Create a table to contain the grid
        Table table = new Table();

        //  Gridline to box the cells
        table.GridLines = System.Web.UI.WebControls.GridLines.Both;

        List columns = new List() {
        "Sent", "Sent Date", "User Name", "Last Name", "First Name",
        "Work Phone", "Work Email",    "Number of Tickets", "Street Address 1",
        "Street Address 2",    "City", "State", "Zip Code" };

        TableRow tRow = new TableRow();
        string value;

        foreach ( string name in columns )
            tRow.Cells.Add( new TableCell() { Text = name } );

        table.Rows.Add( tRow );


        // BackColor = System.Drawing.Color.Blue
        foreach ( var usr in UserData )
        {
            tRow = new TableRow();

            value = ( usr.ticketsSent == null ) ? "N" : usr.ticketsSent.Value.ToString();
            tRow.Cells.Add( new TableCell() { Text = value } );

            value = ( usr.ticketsSentDate == null ) ? string.Empty : usr.ticketsSentDate.Value.ToShortDateString();

            tRow.Cells.Add( new TableCell() { Text = value } );
            tRow.Cells.Add( new TableCell() { Text = usr.userName } );
            tRow.Cells.Add( new TableCell() { Text = usr.lastName } );
            tRow.Cells.Add( new TableCell() { Text = usr.firstName } );
            tRow.Cells.Add( new TableCell() { Text = usr.workNumber } );
            tRow.Cells.Add( new TableCell() { Text = usr.workEmail } );
            tRow.Cells.Add( new TableCell() { Text = usr.ticketsRequested.Value.ToString() } );
            tRow.Cells.Add( new TableCell() { Text = usr.address1 } );
            tRow.Cells.Add( new TableCell() { Text = usr.address2 } );
            tRow.Cells.Add( new TableCell() { Text = usr.city } );
            tRow.Cells.Add( new TableCell() { Text = usr.state } );
            tRow.Cells.Add( new TableCell() { Text = usr.zipCode } );

            table.Rows.Add( tRow );
        }

        //  Htmlwriter into the table
        table.RenderControl( htw );

        //  Htmlwriter into the response
        HttpContext.Current.Response.Write( sw.ToString() );
        HttpContext.Current.Response.End();
   }
Share

Tribal Knowledge: Asp.net ListView Template Which Contains a CheckBox. How to Get an ID from its OnChange Event in C#

TribalKnowledge

This article discusses the use of the attribute DataKeyNames of the ListView which can hold individual IDs for each row and how when using a CheckBox and processing an individual click, to get that info during the OnChange event. That ID is not directly supplied to the OnChange event as easily as done when directly working with ListView events. That ID, specific to the actual data is needed so an can update  of status can be sent to the target database item which it represents.

Setup

In setting up the ListView’s HTML I provide a quick example from a previous article here (Quick Asp.Net ListView Template). The item to take out of it is the property DataKeyNames.  For this article I have a Nationalities (USA, France, Argentina…) table which has two boolean fields of Archive and Exempt which are represented as checkboxes within each row of the ListView. The ListView will know that each row’s ID maps to the column NatID field which is the primary key id of the data item. Here is the html which specifies “NatID” as the id column in the data during the databind:

<asp:ListView ID="lvNationalities"
              DataKeyNames="NatID"
              runat="server"
              OnPagePropertiesChanged="UpdateNationalities"
              ItemPlaceholderID="PlaceHolder1">

In the code behind my dynamic Linq entity looks like this and the dynamic entity contains a NatID which is the items primary key id and it has our properties for the checkbox states for archived and exempt.

var natgeos = from nat in _gtdc.Nationalities
              select new
              {
                  NatID    = nat.Nationality_ID,
                  Archived = nat.Archived,
                  Exempt   = nat.Exempt,
                  Name     = nat.Text
              };

lvNationalities.DataSource = natgeos;
lvNationalities.DataBind();

So NatID is pulled from the database item as shown above. Visually we will have to CheckBoxes for Archived and Exempt as mentioned for the user to change. Each change needs to be handled by the checkbox and the database immediately updated.  Here is a screen shot:

NationalitiesListView

CheckBox

Below is the code which specifies the specific CheckBox event. Looking at the archive CheckBox we handle an OnCheckedChanged event:

<asp:CheckBox ID="cbArchived"
              runat="server"
              AutoPostBack="true"
              Checked='<%# Eval("Archived") %>'
              OnCheckedChanged="ArchiveChanged"
              Text="Archived"/>

Because we are not using the command methodology of the ListView, when we handle the event of the checkbox we don’t have direct access to which row data has been checked. To do that we will dereference some items which will get us to the NatID of row item which was clicked. Here is the code to do that:

protected void ArchiveChanged( object sender, EventArgs e )
{

CheckBox target = sender as CheckBox;

if ( target != null )
{
    ListViewDataItem di = target.NamingContainer as ListViewDataItem;

    if ( di != null )
    {
        int id = (int)lvNationalities.DataKeys[di.DataItemIndex]["NatID"];

        _gtdc = new GTCDataContext( Utilities.GetConnectionString() );

        var item = _gtdc.Nationalities.Where( it => it.Nationality_ID == id ).FirstOrDefault();

        if ( item != null )
        {
            item.Archived = target.Checked;
            _gtdc.SubmitChanges();
        }
    }
}

}

The first step is to convert the sender object to a CheckBox. Once that is successful we use the NamingContainer property off of the CheckBox. That property allows us to access the parent item which is the ListViewDataItem. That is our link back to the ListView. With that we can divine the ID needed from the ListViewDataItem’s index into the DataKeys cache for that row’s ID. Once gotten we can update the database as shown by setting the Archive attribute for the Nationalities for the user selected item.

Share

Quick Asp.Net ListView Template

I find that I seem to make the same Asp.Net ListView and internal table html code over and over. The below code contains that table where each row of data corresponds will be bound to each row of data in a database. Here is that template with blank values which will need to be filled in:

<asp:ListView ID="lvXXXXX"              
              DataKeyNames=""              
              runat="server"              
              ItemPlaceholderID="PlaceHolder1">
    <LayoutTemplate>    
        <table class="">        
            <thead>            
                <tr>                
                    <th>Header 1</th>            
                </tr>        
            </thead>        
            <tbody>
                <asp:PlaceHolder runat="server" ID="PlaceHolder1" />
            </tbody>    
        </table>
    </LayoutTemplate>
    <ItemTemplate>    
        <tr>        
            <td><asp:Label ID="lblYYYY"                
                     runat="server"                
                     Text='<%# Eval("") %>'/>        
            </td>    
        </tr>
    </ItemTemplate>
</asp:ListView>
Share

Asp.Net DropDownList bound with Linq Data Example

I recently answered a post where the user was using ASP .Net 3.5 but not binding data to a DropDownList but instead using hard coded data to load the drop down in the HTML. So I created this quick example to show Linq-To-Xml (Linq) loading of an Asp.Net DropDownList and handling the automatic postback selection changes.

As mentioned example shows how to bind data to a dropdown list using Linq, specifically Linq-To-Xml, but you could use any other of the Linq methodologies because we are using the anonymous types to work with the needs of the DropDownList.

First we will place the Asp.Net DropDownListon our page along with a label which will used to show state changes.

<asp:DropDownList ID="ddlMain"
      runat="server"
      AutoPostBack="true"
      OnSelectedIndexChanged="SelectionMade" />

<asp:Label ID="lblWhat" runat="server"/>

Nothing earth shattering here, we have AutoPostBack because we want the control to tell our code behind on the server when a selection has been made. Following that we have the method which will be called for that event of SelectionMade.

The next thing is to load the DropDownList with values to choose from. We will use an XElement loaded dynamically to show how to do do this in Linq. The other thing of note is that we only want load this once, so we check the postback status. If its a postback, we do not reload.

protected void Page_Load( object sender, EventArgs e )
{

if ( Page.IsPostBack == false ) // Only do this once
{
   // Simulate an Linq-To-SQL call with an Linq-To-SQL
   // So anyone can use this demo.
   XElement dataForList
       = new XElement(
           "DropDownData",
             new XElement( "Node",
                 new XElement( "Text", "Selection 1"  ),
                 new XElement( "Data", "1Selection" ) ),
             new XElement( "Node",
                 new XElement( "Text", "Selection 2" ),
                 new XElement( "Data", "2Selection"  ) ));


   var toTheScreen = from x in dataForList.Descendants( "Node" )
                 select new
                 {
                     Text = x.Descendants( "Text" ).First().Value,
                     Value = x.Descendants( "Data" ).First().Value
                 };

    ddlMain.DataTextField = "Text";
    ddlMain.DataValueField = "Value";
    ddlMain.DataSource = toTheScreen;
    ddlMain.DataBind();
}

}

As shown above we work through all the elements named Node. For each of those nodes we will create a new entity with two properties Text and Value. Text will be shown to the user and value is what is associated with the selection. In the binding operations we specify the Text and Value field for the DropDownList to use and simply bind it to our anonymous projection.

Now all we have to do is handle the selection change event

protected void SelectionMade( object sender, EventArgs e )
{
    DropDownList target = sender as DropDownList;

    if ( target != null )
        lblWhat.Text =
            string.Format(
                "Item {0} selected with Value of {1}",
                target.SelectedItem.Text,
                target.SelectedValue );
}

Nothing magical here, we get the DropDownList from the sender argument, divine the selection and update the label. Here is what it should look like after a change:

ddl

Share

C# Ajax Control Toolkit CascadingDropDown Step By Step

This breaks down step by step what to do to create a cascading drop down using the Ajax Control toolkit in C# and Asp.net 3.5.

  1. Create a web service outside the html page. Decorate the class with the below attribute
  2. // To allow this Web Service to be called from script,
    // using ASP.NET AJAX, uncomment the following line.
    [System.Web.Script.Services.ScriptService]
    public class AjaxServices : System.Web.Services.WebService
    { ... }
  3. Now create a webmethod with any name. Note if the webmethod resides on the same page (a page level service method) as your html page, make it a static method. But normally if you are creating an asmx web page, do not make it static.  This example is not creating a page level service method. The return is a CascadingDropDownNameValue [] type which is found in the ajax toolkit. The parameters of the webservice method has to be as

    string knownCategoryValues
    string category.

    Those parameters have to be in that case and format; do anything else and a general ajax failure will occur when ajax calls the method. The below shows a Linq db call returning the proper object, and handling the category and the value which will be sent. See the notes in the code about what ajax actually sends. .

  4. [WebMethod]
    public CascadingDropDownNameValue[] GetExportTypes   ( string knownCategoryValues, string category )
    {
    
    GTCDataContext gtdc       = new GTCDataContext( Utilities.GetConnectionString() );
    IEnumerable<CascadingDropDownNameValue> vals = null;
    int targetID = 0;
    
    // The data comes in as "XXXX:1" where XXXX is the originating
    // control category, which is *not* the same as the category.
    // The 1st control will pass in nothing (empty) for the
    // knownCategory while 2+ control will pass in
    // the originating category and its ID with a :.// Remove the XXXX: and extract an integer value.
    if (string.IsNullOrEmpty( knownCategoryValues ) == false)
        int.TryParse(
            Regex.Match( knownCategoryValues,
                         @"(\d+)",
                         RegexOptions.Compiled).Groups[0].Value,
                         out targetID);
    
    switch ( category )
    {
        case "Type" : // 1st Initiating DropDownList
            vals = from tp in gtdc.Type_Exports
                   select new CascadingDropDownNameValue
                   {
                       name = tp.Text,
                       value = tp.Export_Type_ID.ToString(),
                   };
            break;
    
        case "SubType": // Next/child DropDownList
            vals = from tp in gtdc.Type_Export_SubTypes
                   where tp.ID_Type_Export == targetID
                   select new CascadingDropDownNameValue
                   {
                       name = tp.Text,
                       value = "0",
                   };
    
            break;
    }
    
    return vals.ToArray<CascadingDropDownNameValue>();
    
    }
  5. Test your webservice before trying to hook it up! Very important.
  6. Add an ajax ScriptManager  to the target html page.
  7. Add an ajax UpdatePanel to the page.
  8. Within the panel add the DropDownLists, two in this example.
  9. Add a CascadingDropDown control extender to the page for each dropdownlist which will be used. Set the TargetControlID to the id of desired dropdown to work with.
  10. <asp:DropDownList ID="DropDownList1" runat="server" />
    <cc1:CascadingDropDown ID="CascadingDropDown1"
                           runat="server"
                           Category="Type"
                           TargetControlID="DropDownList1"
                           LoadingText="Acquiring ..."
                           PromptText="Select Type Of Export"
                           ServiceMethod="GetExportTypes"
                           ServicePath="~/AjaxServices.asmx"
    
                           >
    </cc1:CascadingDropDown>
    
    <asp:DropDownList ID="DropDownList2"
                      runat="server"
                      AutoPostBack="false">
    </asp:DropDownList>
    <cc1:CascadingDropDown ID="CascadingDropDown2"
                           runat="server"
                           Category="SubType"
                           TargetControlID="DropDownList2"
                           ParentControlID="DropDownList1"
                           LoadingText="Acquiring ..."
                           PromptText="Select End Use"
                           ServiceMethod="GetExportTypes"
                           ServicePath="~/AjaxServices.asmx"
    
                           >
    </cc1:CascadingDropDown>
  11. If your last drop down is doing a postback on the page tag turn off event validation or a postback javascript error will be thrown. (The above example has AutoPostBack turned off so no need to do step 7). EnableEventValidation=”false”
  12.  
     
     
Share