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. UserGroupSpa\Views (project folder in solution)
    1. Delete Shared folder and all of its contents
    2. Delete the view _View Start.cshtml
    3. Create DVSUG directory
      1. Add the view index.cshtml into the DVSUG directory
        @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> 
  2. 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);
  3. 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
                      }
                  );
              }
          }
      }
  4. Controllers directory
    1. Add controller DVSUGController (Empty MVC Controller)
  5. 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

Tags: , ,

C#: Combine Two Values Together From a List Into Pairs

Sometimes in C# .Net (see Notes section on usage for before .Net 4)  one might have a list of items and want to make them into pairs. Possibly to take those pairs and place the them into a dictionary. If the items are in a list, that list is linear by nature and using linq is not an option when using the extension ToDictionary.

I have created extension methods to create paired values from any list and those methods are named AsPairs and AsPairsSafe. If the list is odd in length, the final number will be combined with the system default for that type as handled in the method AsPairs. If that is not desired then call AsPairsSafe which will skip the last odd value.

Here is the usage of the AsPairs extension on integers and strings:

var ints = new List<int> { 1, 2, 3, 4, 5 };

IEnumerable<Tuple<int,int>> asTuplePairs = ints.AsPairs();

/* asTuplePairs looks like this(Note value 5 is paired with a default value of 0)
1,2 
3,4
5,0
*/

var strings = new List<string> { "Alpha", "Beta", "Gamma", "Delta", "Omega" };

IEnumerable<Tuple<string,string>> asTuplePairsStrings = strings.AsPairs();

/* asTuplePairsStrings (note Omega is paired a default value of Null)
Alpha, Beta 
Gamma, Delta
Omega, NULL

Whereas the call to AsPairsSafe would return without Omega:
Alpha, Beta 
Gamma, Delta
*/

Here are the extension methods:

public static class MyExtensions
{
   // Create Pairs from a list. If the list is odd add a default value for the final pair. 
   public static IEnumerable<Tuple<T, T>> AsPairs<T>(this List<T> list)
   {
      int index = 0;

      while (index < list.Count())
      {
         if (index + 1 > list.Count())
            yield break;

         if (index + 1 == list.Count())    
            yield return new Tuple<T,T>(list[index++],  default(T));
         else
            yield return new Tuple<T,T>(list[index++],  list[index++]);
      }
   }

   // Create Pairs from a list. Note if the list is not even in count, the last value is skipped.
   public static IEnumerable<Tuple<T, T>> AsPairsSafe<T>(this List<T> list)
   {
      int index = 0;

      while (index < list.Count())
      {
         if (index + 1 >= list.Count())
            yield break;

         yield return new Tuple<T,T>(list[index++],  list[index++]);
      }

   }
}

Notes

Tuple is a .Net 4 item. If you are using a previous version of .Net use the KeyValuePair structure instead.

Share

Tags:

Hyper-V: Creating and Managing a Minimal 2012 installation from Windows 7

This is the process I used to create and install a Hyper-V 2012 minimal installation on my network on its own machine and manage it from a different machine running Windows 7. This installation targets two machines on a Workgroup network. Below are the steps taken for each of the machines.

Step Server Client Summary/Links
1 Install Hyper-V on the Server Install Windows 7
2 Rename (Option 2) Name your server something meaningful. I named my “HyperV”. Smile
3 Get IP Address Add Server IP to Hosts file
Windows\System32\Drivers\etc
Allows for quicker access during remote desktop. Note this step may ignored if the Server gets a dynamic IP. If that is the case the suggested option is to change the DNS server/gateway/router or whatever it is on the system to make it static.
4 Enable Remote Desktop for all clients
7) Remote Desktop
Remote to Server If you have issues at this point, pinging the server won’t tell you anything. The server is behind its firewall. See the below section miscellaneous commands to turn off the firewall.
5 5) Do Windows Update Full Get the latest and greatest
6 Download / Install
Remote Server Administration Tools For Windows 7 With Service Pack 1
(link)
This allows you to create VMs from windows 7. Verify that there is not a new version since this blog was written.
7 Follow install instruction’s on the download page (or the W7 Remote admin tools window which pops up after install) to turn on the newly installed windows 7 feature:
Remote Server Administration Tools
Control Panel –> Programs –> Programs and Features –> Turn Windows features on or off –> Remote Server Administration tools –> Select the tools needed (Hyper-V tools)
8 3) Create Local Admin Account the step #3 on the sconfig.cmd shell. This account will be used to access the sever either during remote sessions. Note you might want to use the name of the client computer’s account for the name/password. Otherwise you will need to do an additional step #11 on the client.
9 -Install On Server- -Install on Client- Hyper-V Remote Management Configuration Utility
This is helps minimize the number of operations needed to configure a hyper-v setup on both the server and a remote server operation. Note this will help deduce any problems which might occur. This utility was made for a previous version of Hyper-V and for Windows 7. As of this writing it is being updated to be used on Windows 8. We will be following the 10-second guide on its page for “Client and Server both workgroup” but skipping steps no longer needed since this version of Hyper-V server has some of those commands offered on the initial boot screen.
9 cscript hvremote.wsf /add:{account name used in #8} This step prepares for the remote admin access for hyper-v.
10 cscript hvremote.wsf
/anondcom:grant
Check the script for “Granted Remote DCOM Access to Anonymous Logon” for success
11 cmdkey
/add:servername
/user:servername\account
/pass
This step is only needed if either the client’s account  or even just the password is different than what is done.  (/pass is the actual, though in some situations to save the password use /pass:{actual passsword}
12 cscript hvremote.wsf
/show
/target:{clinetComputerName}
cscript hvremote.wsf
/show
/target:{ServerName}
Identifies problems on either the client or the server.
13 Run Hyper-V manager
Connect to Server
If there is a problem here and one cannot connect to the server got step 12 and investigate.
14 (Hyper-V Manager)
Virtual Network Manager
Setup networking for victuals to access the network. See next step in Virtual Network Manager (VNM)
15 (Hyper-V Manager – VNM )
Add “External” virtual network
Just name the network to use with Virtuals and accept that pending changes may disrupt the network. This may take awhile..so give it time.
16 (Hyper-V Manager)
Create a Virtual!
Dust off that old ISO and create a virtual to remember. Just realize that the browse operation is from the server and not your client.

 

Misc commands

NET SHARE Images=D:\Images /GRANT:Everyone,FULL Create a Share drive.
Get-VMSwitch Lists all the virtual switches
netsh advfirewall set allprofiles state off Turn off the firewall. Note that the firewall on a Hyper-V machine will block any ping from your internal network.
Share

Tags: ,

Fear and Loathing in Visual Studio 2012 at 256 Colors

While working with the Visual Studio 2012 RC I experienced the downside to the new color scheme. When I Citrix-ed into my workstation I adjusted the colors to the 256 color set for better performance over the wire. That was not good.

Now I normally work with the dark color theme and discovered that the appropriately shaded colors simply ran together to the point where text editor window became almost as one the other windows.

Ok, a picture is worth a thousand bug reports, ahem issue reports, so let me show you.

Dark Theme

256 Colors Full Color
256 colors dark True Colors Dark

Light Theme

I decided to switch to the light theme, not that my specific editor colors which work on the dark, do not play well in the light and that has nothing to do with this situation, but notice how in 256 colors the golden rod color becomes prominent in the windows.

256 Colors Full Colors
256 colors Light True Colors Light

VS2010

How did VS 2010 look in 256 Colors? Just fine thank you!

Ahh nothing ages well like a good editor; Chateau De Studio 2010!

256 VS 2010

Summary

Long story short don’t use the non true color setting when remoting in to use Visual Studio 2012!

Share

Tags: ,

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

Tags: , ,

Silverlight (How To): Manipulation of Dynamic Selection Rubber Band in C#

Large group of women exercising in the parkOne of the basic tenets of WYSIWYG is to be able to create a rubber band region using the mouse to give the user the ability to create a selectable region. This article demonstrates how to do that in any version of Silverlight and the user is given the tools to create such a bounding rectangle in any circumstances via the usage of some basic building blocks in Silverlight.

The below picture (not the one on the left) shows our goal, the dynamic creation of a bounding rubber band (a rectangle control for this demonstration) in a canvas. The below canvas is drawn in black (visually grey due to the opacity set) while the bounding rectangle shows itself in red.

RubberBanding

The user starts the process with an initial click which designates a start location of an upper left point for our bounding box with a mouse click.  Once the click happens the mouse cursor changes to a hand (if moving to the lower right) as a visible indicator that the process has started. While user continues to move to the lower right the band grows and while the mouse button has yet to be let go or released. For the demo a bounded number is shown is which relates the actual dynamic change in the X (horizontal) position. That is done for this article only and the above picture shows the rectangle with an X size of 123 pixels and can still be grown as shown by the hand icon.

Initial Xaml

In the page’s xaml we add a grid and a canvas as such this:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="33*" />
        <RowDefinition Height="267*" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="32*" />
        <ColumnDefinition Width="568*" />
    </Grid.ColumnDefinitions>
    <Canvas x:Name="cMain"
            MouseLeftButtonDown="cMain_MouseLeftButtonDown"
            MouseLeftButtonUp="cMain_MouseLeftButtonUp"
            MouseMove="cMain_MouseMove"
            Grid.Row="1"
            Grid.Column="1">
        <TextBlock Width="100"
                    Height="30"
                    Text="{Binding XValue, ElementName=userControl}"
                    />
        <Rectangle Fill="Black"
                    Width="550"
                    Height="200"
                    Opacity=".25"/>
    </Canvas>
</Grid>

The things to note are that the canvas is in a grid which is offset from the initial by 30 pixels in both axis. We then place a rectangle on the canvas which is our area which will be our strike zone where we will intercept the messages mouse events. We have a strike zone because individual controls in a canvas, such as texblocks have whitespace between them and the canvas will not get the mouse clicks and we want to be able to capture the process at all locations on the canvas; hence we fill up the canvas with our strike zone as to be ensured that the events are consumed properly by the code. 

When the user clicks within the strike zone the process starts as signified by a cursor change to the hand, as the user moves within the zone the rubber banding increases and when the mouse button is let up the process ends and the cursor is returned to its previous state.

Initial Properties

Here are the three variables we will use during this process:

private int _xValue;

public int XValue
{
    get { return _xValue; }
    set
    {
        _xValue = value;
        OnPropertyChanged( "XValue" );
    }
}

private Point OriginatingPostionOnCanvas { get; set; }

private Rectangle RubberBandBox { get; set; }

Since the XValue property is reflected on the screen in a text box it reports all changes by using  INotifyPropertyChanged which our class handles in a standard way for notify property (not shown). Followed by that is our initial click location named OriginatingPostionOnCanvas which will dictate the upper left (or lower right) location of the rubber band rectangle which we will dynamically create. The last variable is the actual dynamic rectangle control named RubberBandBox which will be created and modified during the process.

Event Mouse Left Button Down

We will handle three events during this process. The first is the when the user clicks and begins to hold down the mouse button:

private void cMain_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
{
    OriginatingPostionOnCanvas = e.GetPosition( cMain );

    RubberBandBox = new Rectangle() { Width = 1, 
                                      Height = 1, 
                                      Fill = new SolidColorBrush( Colors.Red ), 
                                      Opacity = .1
                                    };

    RubberBandBox.SetValue( Canvas.LeftProperty, OriginatingPostionOnCanvas.X);
    RubberBandBox.SetValue( Canvas.TopProperty,  OriginatingPostionOnCanvas.Y );

    cMain.Children.Add( RubberBandBox );
}

Our goals here are simple:

  1. Get and store the location where the user clicked in relation to the canvas.
  2. Create  and store the rectangle in red and with an opacity which does allows the user to see what is being selected.
  3. Setting the initial location of the rectangle and adding it to the children of the canvas.

Event Mouse Move

The user is moving and we must adjust the rectangle as appropriate for the target direction. Note we also have to handle when the user moves to the upper left instead of the lower right…. So when that happens we will change the cursor to a Stylus (the dot) for visual indication that we are getting a reverse band situation.

private void cMain_MouseMove(object sender, MouseEventArgs e)
{
    if (RubberBandBox == null)
        return;

    Point pointMovedTo = e.GetPosition( cMain );

    double xDelta = pointMovedTo.X - OriginatingPostionOnCanvas.X;
    double yDelta = pointMovedTo.Y - OriginatingPostionOnCanvas.Y;

    LayoutRoot.Cursor = ((xDelta > 0) && (yDelta > 0)) ? Cursors.Hand : Cursors.Stylus;

    if (LayoutRoot.Cursor == Cursors.Hand)
    {
        RubberBandBox.Height = yDelta;
        RubberBandBox.Width  = xDelta;
    }
    else if (LayoutRoot.Cursor == Cursors.Stylus)
    {
        RubberBandBox.Height = Math.Abs( yDelta );
        RubberBandBox.Width = Math.Abs( xDelta );

        if (xDelta < 0)
            RubberBandBox.SetValue( Canvas.LeftProperty, pointMovedTo.X );

        if (yDelta < 0)
            RubberBandBox.SetValue( Canvas.TopProperty, pointMovedTo.Y );
    }

    XValue = (int) (e.GetPosition( cMain ).X - OriginatingPostionOnCanvas.X);
}
Explanation
Line 3 The mouse can move through the canvas during times we are not processing. We need to check that and only process when we have an actual rectangle on the canvas.
Line 6 Extract the current location in relation to the canvas.
Line 8-9 Get the change differences for x and y as named deltas.
Line 11 If the deltas retrieved are in the positive we have a drag to the lower right and that is designated by or specifying the cursor to a hand. If not, the deltas indicate that movement is upwards and to the left; regardless change the  cursor to the stylus.
Line 13 If we are in the hand state, we want to grow (or decrease) the rectangle in that direction towards the lower right.
Line 18 If we are in the stylus state, there is a negative growth either in the x or y axis. Handle the negatives while changing the height and width. Depending on which delta is negative, handle the new location position of the upper left hand position of our bounding rectangle which will follow the mouse.
Line 30 Inform the user of the current X axis location of the mouse whether positive or negative.

Event Mouse Left Button Up

This is our final event we have to handle. We do a safety check on whether we are actually in operations, and if we are then we simply return the cursor to its original state and remove the rectangle from the canvas’ children.

private void cMain_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
{
    if (RubberBandBox == null)
        return;

    // Return to the previous state; whatever it is, the OS handles it.
    LayoutRoot.Cursor = null; 

    cMain.Children.Remove( RubberBandBox );

    // Show that we are not processing by making this null.
    RubberBandBox = null; 
}
Share

Tags: , , ,

Git: Rollback (or Undo) a Pull from an External Repository To Return To A Previous Stable Commit State

iStock_000017591577XSmallZen is not possible and we need to return to a state of peace within Git!

In the case where one pulls from a repository such as GitHub and something breaks, one may want to undo that pull. Here are the steps to rollback to a previous version using Git.

Note if you have any work in the local working directory done after the pull, it will be lost using this method.

Our goal is to move to the Head to the last snapshot before the pull and return the Zen to us.

Steps

  1. Find the SHA-1 version using reflog. The reflog is interactive and one uses a q to exit out.

    git reflog

    That will bring up a list such as this:

    da88c95 HEAD@{0}: pull origin br_1.0.0: Merge made by recursive.
    26a96f1 HEAD@{1}: commit: Non movable end block not being placed at end time fix.
    abc8366 HEAD@{2}: commit: Created durationd dependancy to adjust the timeline correctly to the actual time.
    1b3ca89 HEAD@{3}: commit: _ReSharper directory ignored
    5d579ba HEAD@{4}: pull origin br_1.0.0: Fast-forward
    68fbb98 HEAD@{5}: commit: Ignore tests which are creating timelines and provisioned timelines

     

    We are interested in the second line as highlighted above which is before the pull.

  2. Then we want to move the head to  either the hash or the symbolic name before the pull so we do a reset command which targets the local working directory and moves it to the target commit.
    git reset --hard 26a96f1

    or (in this case yours may be different)

     

    git reset --hard HEAD@{1}:

The — hard options  brings about changes to the working copy and sets us to the state before the pull. Now interestingly enough if we look at the log it shows our reset operation as the top level log item which has the same hash as where we wanted to go:

26a96f1 HEAD@{0}: 26a96f1: updating HEAD
da88c95 HEAD@{1}: pull origin br_1.0.0: Merge made by recursive.
26a96f1 HEAD@{2}: commit: Non movable end block not being placed at end time fix.
abc8366 HEAD@{3}: commit: Created durationd dependancy to adjust the timeline correctly to the actual time.
1b3ca89 HEAD@{4}: commit: _ReSharper directory ignored
5d579ba HEAD@{5}: pull origin br_1.0.0: Fast-forward

Final Thoughts

Of course you have only delayed the inevitable. One usually does this because something about the current state of changes in the remote is just not palatable and someone else is working on it. When ready do a normal pull to pull down those new changes and merge.

Share

Tags:

Xaml: Adding Visibility Behaviors Using Blend to A DataGrid for WPF or Silverlight

iStock_000015143879XSmallIn Xaml the determining when to trigger the visibility, or the hiding of  controls or their functionality is a key concept of doing either WPF or Silverlight programming. This article builds upon my article C#: WPF and Silverlight DataGrid Linq Binding Example Using Predefined Column Header Names in Xaml where we are going to add behaviors to the datagrid shown.  (Don’t worry about reading the article, for I will get you up to speed with the code snippets in this article.) We will use Microsoft’s Expression Blend product to do the dirty work of xaml modification to our DataGrid and it will be shown in a step by step process.

Concept

In the previous article the idea was to load our datagrid with two columns of data. The first column showed us a filename and the second column displayed a modified filename with a count in it. We will take that one step further and have a description show up with the file size. Here is the resulting look:

Inital with Description

The user gets the description when the row is clicked.

But what if we wanted to disable that functionality and automatically show the user all the items when the mouse hovers over the datagrid such as

Result

Setup

First thing we need to do is setup our datagrid. Below is the xaml and the code behind to load our datagrid. Note the datagrid has the RowDetailsVisibiltyMode set to collapsed. That means that when a user clicks on the row, it will only select it and not open up our description. The loading of the ItemsSource happens during the construction and after the initial initialization and is shown in C# in the second pane.

<DataGrid x:Name="dgPrimary"
            RowDetailsVisibilityMode="Collapsed">
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>

            <TextBlock FontWeight="Bold"
                        Text="{Binding Size, StringFormat=Size \{0\} (bytes)}" />

        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Original}"
                            Header="File Name Before"
                            IsReadOnly="True" />
        <DataGridTextColumn Binding="{Binding New}"
                            Header="File Name After"
                            IsReadOnly="True" />
    </DataGrid.Columns>
</DataGrid>
dgPrimary.ItemsSource = 
    new DirectoryInfo( "c:\\" ).GetFiles()
                               .Select( ( fInfo, index ) => new
                    {
                        Original = fInfo.Name,
                        New = string.Format( "{0}_{1}{2}", 
                                    System.IO.Path.GetFileNameWithoutExtension( fInfo.Name ), 
                                    index, 
                                    System.IO.Path.GetExtension( fInfo.Name ) ),
                        Size = fInfo.Length
                    } );

Behaviors and Blend

One of the easiest ways to add a behavior [of the action] to a control is to use Blend to add an interaction behavior.  In our case we want a mouse over action to open up all of the Row Details and a secondary action to close them when the mouse leaves. The behavior we need to search for in blend is the ChangePropertyAction. Below we drag (or add) two behaviors to the datagrid and change the RowDetailsVisibilityMode to visible on mouse enter and to collapsed on mouse leave.

cpaEnter cpaLeave

Then when we build and run the app, the mouse hover over makes the descriptions visible and collapsed depending on the mouse. Here is the final xaml:

<DataGrid x:Name="dgPrimary"
            RowDetailsVisibilityMode="Collapsed">
    <DataGrid.RowDetailsTemplate>
        <DataTemplate>

            <TextBlock FontWeight="Bold"
                        Text="{Binding Size, StringFormat=Size \{0\} (bytes)}" />

        </DataTemplate>
    </DataGrid.RowDetailsTemplate>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Original}"
                            Header="File Name Before"
                            IsReadOnly="True" />
        <DataGridTextColumn Binding="{Binding New}"
                            Header="File Name After"
                            IsReadOnly="True" />
    </DataGrid.Columns>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="MouseEnter" SourceObject="{Binding ElementName=dgPrimary}">
            <ei:ChangePropertyAction x:Name="cpaEnter" PropertyName="RowDetailsVisibilityMode">
                <ei:ChangePropertyAction.Value>
                    <DataGridRowDetailsVisibilityMode>Visible</DataGridRowDetailsVisibilityMode>
                </ei:ChangePropertyAction.Value>
            </ei:ChangePropertyAction>
        </i:EventTrigger>
        <i:EventTrigger EventName="MouseLeave">
            <ei:ChangePropertyAction x:Name="cpaLeave"
                PropertyName="RowDetailsVisibilityMode" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
Share

Tags: , , , ,