Click here to Skip to main content
15,038,345 members
Articles / Programming Languages / C# 5.0
Article
Posted 24 Mar 2017

Stats

9.6K views
595 downloads
9 bookmarked

Create Desktop UX With ASP.NET MVC (Part 2 of 3)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
24 Mar 2017CPOL33 min read
Building the ASP.NET MVC desktop UX (User Experience). See ContextMenu in action.

Introduction

If you've read the first part in this series (Create Desktop UX With ASP.NET MVC (Part 1 of 2)[^]) or you understand the concepts that make up the ASP.NET MVC foundation you are ready to build the core functionality of the app we will build in this article.

In this second part of the article series we will jump in and create our app which will provide a desktop UX.

final target context menu

While writing this article I determined that we are going to need an additional installment (Part 3) because there is a natural break after the content of this (Part 2) article.  That's because this part focuses entirely on the UI / UX.

What This Article Covers

My focus in this article is to complete the entire UI / UX work.  

Complete Entire UI / UX

While completing the entire UI / UX we will :

  • Progress through building the code with associated code downloads for each step
  • See the purpose of every line of code that is added in an attempt to build our UX
  • Take a closer look at CSS styles, using many to create our Windows Form concept and context menu.
  • Use JavaScript to build the actions that provide the User Experience
  • See Nuget in action to retrieve jQueryUI and learn details of how it works inside Visual Studio
  • Learn how to leverage jQuery functionality and things like how jQuery selectors are used.
  • Implement some Bootstrap styles by applying Bootstrap classes to our HTML

By the time you're done reading this article you will have :

  1. a complete reusable draggable Window Form element
  2. a stylized context menu which appears upon right-clicking the form element
  3. a context menu which allows user to choose other functionality to run
  4. the framework of a working context menu

Part 3 : Implementing the Final Functionality

Then in Part 3 we will implement the last of the functionality which will :

  • implement MVC Controllers and JavaScript Ajax calls to handle async requests from the Context Menu
  • implement partial views that display resulting data back to the user in a result view which is also represented by a Windows Form element.

Now, let's get started building out our Windows Form element with working Context Menu.

Windows Form Element

The first thing I wanted in this app, in an effort to create the desktop UX, was the concept of a Windows form element.  This item is going to be a part of our UI and we build our web-based UI using HTML and CSS.

Styling a Form Element

I'm going to create this element using a simple <div> tag and some styles that I will add to the Site.css.  I'll explain the styles we use as I add them and I'll show you the UI in screenshots as it progresses.

Create and Add Background Color

The first steps we'll take are:

  1.  add a <div>
  2. set background-color (so you can see where the div is located
  3. set a border on the <div>
  4. set a height and width on the <div>

To accomplish those items I am going to add some styles to our project's main stylesheet file, site.css.

CSS Styles Are Name/Value Pairs

I won't go into CSS styles too much but you should know that they are simply name / value pairs with the name being predefined by the CSS standard. 

All of the name value pairs are grouped inside { } brackets, a name is separated from its value by a colon : and each name/value pair is separated from others using a semicolon ; .

Of course you also need to name the style so you can refer to it later.  The name can be a class, indicated by a dot . an id indicated by a # or the name of a predefined HTML tag (<body>, <li>, <div>, etc) so that the style applies to all tags of that type on your web page.

A generic CSS style looks something like:

CSS
.className{

    name1:value1;

   name2:value2;

}

#idName{

    name1:value1;

   name2:value2;


}

body{

    name1:value1;

   name2:value2;
}

WhiteSpace Doesn't Matter

Of course, white-space does not matter since CSS uses the tokens (semicolon, colon, bracket) to separate all of the items it looks for.  That means the first one in the previous example could be typed out to look like the following and is still completely valid.

CSS
.className{ name1:value1;name2:value2; }

Applyin the Styles to HTML Elements

The previous examples shows how to define the styles in the .CSS file.  However, they do not show up until they are applied to your HTML.

Of course to apply the style to a particular HTML element you have to add it to the HTML.

Applying Class Styles To HTML

For example if you wanted to apply the CSS class style of the previously defined on a <div> in your HTML you would add a class attribute to the <div> tag and add the name of the defined class style.  It would look like the following:

HTML
<div class="className">this block would be styled as defined in className CSS defintion</div>

Applying ID Styles to HTML

If you want to apply one of the styles where you used the # to indicate it is a ID element you will set the id attribute of on your <div> tag.

HTML
<div id="idName">this block would be styled as defined in idName CSS defintion</div>

Do Not Use Special Symbols When Referencing Styles

Notice that we only use the special symbols when we define the class or id in the stylesheet.  We do not use the symbol when referring to its name in the HTML.  That's because to indicate which type it is we use the attribute name id or class.

Applying to Predefined HTML Tags

In the case of the style defined for the <body> tag (or any other predefined tag) the style will be applied to the entire element where ever it is found in the page, as long as the stylesheet is loaded on that page.  This allows you to define styles that will be applied on a tag at all times and not only when indicated by an id or class style.

Just A Matter of Knowing Name Attributes and Possible Values

Now that you know how CSS is structured all you need to know are the valid name attributes (such as background-color, padding, border-style) and then know what the possible values are for the specified name attribute.  One you know those (which can be easily looked up on the web) you can style your HTML easily.

For example, if you want to set the background color of an item using CSS you need to know that the CSS name for that attribute is: background-color.  After that, you simply need to know what values and types of values are valid for the attribute.

I just Googled : background-color css and the first result was:  CSS background-color property[^] and saw example code like the following:

CSS
body {
    background-color: yellow;
}

h1 {
    background-color: #00ff00;
}

p {
    background-color: rgb(255,0,255);
}

As you can see, there are a number of formats that you can use to set the background-color.

That's about enough understanding of CSS so we can jump into creating our styles now.

Draggable Class Style

I've created a class style I'm going to call draggable because eventually I want my div to allow the user to click it and drag it around the browser.  Here's how I've defined it so far.

CSS
.draggable {
            background-color:mintcream; 
            border-color:black;
            border-style:solid;
            overflow:hidden;
            border-radius: 25px; 
            padding: 0.5em; 
            cursor: move;
 }

Add that to your site.css file and save it.

Most of those styles are self explanatory and you can guess at what they are doing.  Looks like I've added a border that is going to be black and made the background-color a predefined color.   I've done all that so you can tell where the <div> is actually located on the web page.  The other elements like padding (set some space between the inner edge of div and any text) and border-radius (put a curve on border boundary edges) are a little less obvious but you can look them up for more details.  The last attribute is interesting, because I've decided to change the cursor to the move cursor (a four-way arrow) to indicate that you can grab the <div>.

Where Is Site.css Loaded?

Let's head back over to our _Layout.cshtml that we've become so familiar with in the previous article and take a look and see where the site.css file is linked.   This is how the site.css file is loaded up so that its styles can be applied.

Right at the top of _Layout.cshtml we see the following:

HTML
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
    <link href="~/Content/bootstrap.min.css" rel="stylesheet" type="text/css" />
    <script src="~/Scripts/modernizr-2.6.2.js"></script>
</head>

Fetched From Server, Loaded Into Browser Memory

Of course the bolded line is the one where our site.css is loaded up.  When the browser sees that line, it will fetch the file from the server and load the styles into memory so they can be applied.

Bootstrap Bonus

You can also see that on the line that follows the link of the site.css that the Layout template also loads up the Bootstrap styles.  That's really all Bootstrap consists of : a bunch of predefined styles that create a common look and feel for users.   We'll use a few of these ourselves because they make it particularly easy to get some styles that would otherwise be a lot of work.

Let's Apply Our Styles and Take a Look

The target View we are working with is in Index.cshtml so go ahead and open up that file.

I'm going to add a div with our new style and a few lines of fake data just so we can see the div on our page.

Here's the entire listing for Index.cshtml as we have it so far.

HTML
@{
   
    ViewBag.Title = "Index";
}
<div class="draggable">
    <p>data</p>
    <p>more data</p>
    <p>extra data</p>
    <p>other data</p>
    <p>various data</p>
</div>
<h2 >Index</h2>

Of course, the thing we are focusing on here is the new <div> tag I've added.  You can see that I've styled it with our new draggable class.

Once you added the new HTML, go ahead and run the app again.  Note: I also removed the alert dialog fomr our ContextMenu.js so I wouldn't have to keep clicking [OK] every time.

first run with new styled div

Get Source Download

If you download v002 (v001 is available in the first article) of the code at the top of this article you can run the app and continue from this point if you like.

Now that we've walked through all of these basics and you have a good understanding of where we go to do these things like define styles we can move a bit faster in our development.

Make the Div Draggable

The <div> we just defined is the element I will focus on to create our Windows Form-like UX that is the entire point of this article series.  I now want to make that element draggable just as a Windows Form is on the Windows desktop. That'll begin to create the UX I'm hoping for.

However, as you research how to make an HTML element draggable you will find answers that supposedly work but actually do not carry out the solution entirely.  I've found the easiest way to do this that works in all major browsers.

Not Just a Style, Back To JavaScript

To get the draggable functionality to work, we actually have to implement it in JavaScript.  If you think about it there would need to be something that determines the x,y coordinate location of the <div> and then update that whenever it is moved so it's probably more complex than we first thing.

However, I've found the simplest way to make the div draggable while only including a small library.  The additional library is the sister to jQuery.  It is jQueryUI.  It's also easy to get in Visual Studio using Nuget.

Here's how we do it.

Getting jQueryUI Via Nuget

What is Nuget?

Nuget is a package manager that is built into Visual Studio as of 2010 and newer.  What is a package manager?  It is a helpful third-party utility that can install common code libraries for us.  

Nuget Installs Base MVC Libraries

Nuget installs all of the base MVC libraries so it is already at work in your project anyways.  It is aslo the utility that restores the packages when you get the source code from this article and run it for the first time.

Note: Nuget Package manager can be a pain. If you do not want to go through these steps simply download the V003 version of this project from the top of this article.  It already has references to the required jQueryUI library.

In Visual Studio on the top menu bar, find the TOOLS menu item and click it.

Slide down to Nuget Package Manager menu item and then over to the Manage Nuget Packages for Solution...

nuget pkg manager

A window will appear and now let's take a quick peek at all the packages that Nuget is already managing.  

Click the [Intalled Packages] item on the left side of dialog box and it will then look something like the following:

install pkgs

Now go ahead and click the [Online] item on the left (below the [Installed Packages] item) and under that choice there should be a [Microsoft and .NET] item.   Choose that one.

When you choose [Microsoft and .NET] the UI refreshes and shows you different libraries.  We are looking for the one that is jQuery UI (Combined Library). You can type jqueryui (all one word) in the search box at the top right and it should show you the exact one we want.

jqueryUI package

On the right side you can see some version information and details about the library.

jqueryUI details

Click the [Install] button and a confirmation dialog will popup.

confirm pkg man install

Click [OK] and the package and dependencies will be installed.

jqueryUI installing

Once, it's done, that installation dialog will disappear and the Nuget Package manager will still be on your screen.  Click the [Close] button in the lower right corner and you'll be back at Visual Studio.

Nuget Package Manager & Studio Problem

There are some problems with Nuget.  It's still not perfectly seamless to use inside Visual Studio.

There are a few issues:

  1. The ASP.NET MVC template in Visual Studio 2013 references an old version of jQuery (1.10.2)
  2. In _Layout.cshtml we load the original jQuery version (~/Scripts/jquery-1.10.2.min.js)
  3. However, when we choose the jQueryUI library and add it to our project the Nuget package installer updates jQuery to a newer version it needs.  When it does that it removes v1.10.2.  However, it does not update our _Layout.cshtml file to reference the new version.  That means jQuery wouldn't load at all because it is referencing an old file that doesn't exist.

Here's a view of the original Scripts folder, before(prior to the Nuget installation) and after on the right:

old scripts foldernew scripts folder

You can see that the 1.10.2 jQuery is gone.  However, there is another problem really, because Nuget didn't clean up the jquery-1.10.2.intellisense.js (a Visual Studio editor helper version to display intellisense in JS).

Back to _Layout.cshtml to update the script references.

Edit _Layout.cshtml

Open up _Layout.cshtml, move to the bottom of the file and delete the old script referencing jquery-1.10.2.min.js.

Add the two new references:

HTML
<script src="~/Scripts/jquery-1.12.4.min.js"></script>
<script src="~/Scripts/jquery-ui-1.12.1.min.js"></script>

Make sure you place those references before the bootstrap.js and the ContextMenu.js since those scripts will actually reference these two new ones.

Now we can finally implement some code to make the <div> draggable.

Make Class Draggable With jQuery Selector

The jQueryUI provides a very nice method which allows us to make any element draggable and handles all the work for us.  It might not be 100% perfect, but it is quite amazing for how easy it is to implement.  I believe it will also convince you that it is worth adding the extra library to get the functionality.

Just One Line of Code!

All we need to do now, is add one line of code to our ContextMenu.js file. We will add it inside our function that is called after the page loads.

Here's all the code that we'll have in ContextMenu.js with the added line bolded.

JavaScript
//ContextMenu.js

$(function() {
        $(".draggable").draggable();
    }
);

jQuery Selector

Let's break this short line of code down so we know what it does. 

It does two things:

  1. selects the targeted element
  2. applies functionality (draggable) to that element

The jQuery selector allows us to choose any element simply by including its class name or id name.  

jQuery selectors have a form like: 

$(".className") or $("#idName") 

They allow us to select an item (DOM element) so we can work on it.

The jQuery selector does require that we include the special character of dot . or # when we're accessing a class or id.  So the first part of the line finds or selects our div element by finding the one that has a class="draggable".  My naming makes it all seem a bit redundant, but nevertheless jQuery finds our div and then it runs a method called draggable() on it.  That draggable() method makes the item uh...err...draggable.  :)

Houston, We Have Drag & Drop

Rebuild and run and now you can simply click anywhere in the div and drag it around and drop it.  It's really cool. 

Here's animated gif so you can see it.  

drag and drop in action

Of course, we still have much more to do to make it useful.

Also, did you notice in the video that when I was moving the <div> around it slipped under the navbar at the top?  That's related to the a CSS attribute named z-index set on those items and we'll fix that later.

Get Code Download V003

You can get all the work that has been done so far by downloading the v003 version that is linked at the top of this article.

Not Quite There

Of course the success isn't celebrated long because now I can grab that <div> anywhere to move it and that just isn't how it works on a Windows Form.  Instead you can only grab the top menu bar to move a Windows Form.  I have an idea how to do that so let's go change some styles and see if I can get it to do that.

Actually, first of all, let's change the structure of our <div> element in the Index.cshtml because it isn't quite right.

Add Another Div

Over in Index.cshtml let's change the div as follows:

HTML
@{
    ViewBag.Title = "Index";
 }
<div class="mainForm">
    <div class="titleBar">Title Bar</div>
    <p>data</p>
    <p>more data</p>
    <p>extra data</p>
    <p>other data</p>
    <p>various data</p>
</div>
<h2 >Index</h2>

I've changed the name to our <div> class to mainForm and I've added a new nested <div> to it.  Note: Please notice I'm using camel case on my names now.  

The new nested <div> will represent our title bar on our form and it will be the section of the form that is grabbable (or draggable) so I've named it titleBar now.   That's a much more descriptive name and makes it easier to find in the CSS.  We'll have change the styles so that they reference the correct <div> now, but that's easy enough.

Let's open up site.css and make the style changes now.  There are a couple that I'm really happy with and I'm excited to show them to you.

Alter Styles

MainForm Styles

First of all, let's take a look at the mainform class styles.

CSS
.mainform{background-color:mintcream; 
          border-color:black;
          border-style:solid;
          border-radius: 25px;
          z-index: 100000;
}

The outer div has a black rounded rect border (border-radius) just as it did before.  It still has the mintcream background-color.  Not much changed there, except now we do not need the move cursor to show on the outer div. Instead we want that only when the user floats over the Title Bar.

I also added the z-index value and set it to one hundred thousand (the higher the value the more topmost the element) in an effort to make sure our <div> will be on top of everything else.  We'll see later, in the animated gif how this makes our <div> float right over the top navbar.

The Title Bar is where things get interesting.

TitleBar Styles

At first I had only the three following styles, but as you will see, it doesn't quite come off right.

CSS
.titleBar {
    background-color: lightblue;
    overflow:hidden;
    padding: .5em; 
    cursor: move;
}

Here's how that renders now.  Not quite right.  :)

first shot titlebar issue

As you can see the upper corners of the title bar are not rounded as they should be to match the outer mainForm <div> and it doesn't look good.  I Googled and found another CSS style and I'm quite happy with it.

Here's the entire titleBar CSS with the two new styles bolded.

CSS
.titleBar {
    background-color: lightblue;
    border-top-right-radius: 25px; 
    border-top-left-radius: 25px;
    overflow:hidden;
    padding: .5em; 
    cursor: move;
}

Now it looks like:

rounded title bar

That's more like it.  However, you still cannot grab the titleBar.  That will take some JavaScript work.  Let's do that now.  It took me a bit to work this out because I learned that the jQueryUI .draggable() method has to be initialized first or it doesn't quite work right.  Once I figured that out, it was fairly straight forward.

JavaScript, To Make Life Easy

JavaScript to make life fun.  :)  I know that many people don't feel that way about it.  However, used judiciously JavaScript can be your friend.

First take a look at the entire code listing of ContextMenu.js.  This enables just the titleBar to be grabbed, but the entire outer <div> will move when you drag. 

JavaScript
//ContextMenu.js
var isDraggable = false;
$(function () {
        // these first calls to draggable() initialize the jQuery method for use
        $(".mainForm").draggable();
        $(".mainForm").draggable("disable");
        $(".titleBar").mousedown(mouseDownToggleDrag);
        $(".titleBar").mouseup(mouseUpToggleDrag);
    }
);

function mouseDownToggleDrag() {
    console.log("mousedown");
    isDraggable = true;
    toggleDrag();
}

function mouseUpToggleDrag() {
    console.log("mouse up");
    isDraggable = false;
    toggleDrag();
}

function toggleDrag() {
    if (isDraggable) {
        console.log("turn on...");
        $(".mainForm").draggable("enable");
    } else {
        console.log("turn off...");
        $(".mainForm").draggable("disable");
    }
    console.log("isDraggable : " + isDraggable.toString());
}

I've placed some comments in there and I've also left some of my console.log() statements so you can follow along and see the code run if you like.

Initialization When Page Loads

When the page loads we do a bit of initialization.  As you can see from the comment I learned the painful way that I needed to call the draggable() method on my element in order to initialize the function.  Before that, the method would make the <div> draggable one time and then fail every time thereafter.

Register MouseDown and MouseUp Events

The next two lines use jQuery to register the mousedown and mouseup events on the titleBar element.  This allows us to know when those events occur.  Here we supply it with a function to call so we can do some work when those events occur.

MouseDown Event

When the mouse goes down we do two things:

  1. set a global variable (isDraggable) to true
  2. call the toggleDrag() method

MouseUp Event

The mouseup event does the same two things, except it sets the isDraggable to false.  

Together these event insure that the entire <div> is draggable when the user has clicked the Title Bar and not draggable if she clicks anywhere else within the <div>.

We are well on our way to our desktop UX (User Experience).  Here's another animated gif of the draggable <div> in action.

drag mvc in action 2

Download Source Code v004

At this point you can download v004 of the source code attached to this article and you'll be up to speed with all of the changes.

Now that we have a basic UI that provides the beginning desktop UX we can begin to implement our functionality as I've imagined it might work.  

More Parts to This Article Series

Initially I thought I could write this up in just two articles, but in an effort to break the content into more digestable pieces I'm going to extend this series to a third article which will cover the core functionality of the app.  

That core functionality will do the data retrieval and display by leveraging the ASP.NET MVC framework and Razor Views.  However, the ContextMenu that we will build will be more JavaScript which is where we are working right now.  So for this article let's build the ContextMenu and get it to display then in the third article I'll walk you through how to use use it to retrieve data.

Building the ContextMenu

ContextMenu Functionality Overview

The core functionality I'm driving toward is :

  1. A form-like object in the browser desktop that displays data.
  2. The user can right-click on a row of data to display a ContextMenu which provides options the user can choose to get additional information about the row of data 
  3. When the user makes a choice on the ContextMenu an AJAX call is made to server.
  4. The server looks up related data and responds with a MVC PartialView that displays the information to the user.

ContextMenu HTML Element 

The first thing we need to do is create an HTML element that will represent our ContextMenu.  Here again we will use the fantastically flexible <div> element as our basic structure.  Let's start out with the following code.

HTML
<div class="RADContextMenu">

</div>​​​

I've created a new HTML / CSS class named RADContextMenu which we will be using to style our ContextMenu and to refer to the element from JavaScript.

Enter Bootstrap

I have messed around with Bootstrap styles enough to know that I'd like to apply the Bootstrap panel style.  

You can see a lot more at the official Bootstrap site (Panels Via Bootstrap[^]).  As we move along the Bootstrap predefined styles will help us get to our look and feel faster than if we have to write all the CSS ourselves and Bootstrap is included in the MVC project by default.

HTML
<div class="RADContextMenu panel panel-primary">
        <div class="panel-body">

        </div>
 </div>

Those extra class attributes (panel, panel-primary and panel-body) are all defined by Bootstrap and will apply styles to these <div> elements.

Where Should We Add The New Div?

When we add a few more styles to this <div> it will only show up after the user does a right-click on a mainForm element.  That means this <div> can go anywhere in the _Layout.cshtml file.  I am placing it at the bottom of the file, right before the all of the <script> tags.  Here's a snapshot of that bottom portion of the file so you can see where I'm adding it.  

new div location

If you build and run you will actually see the <div> but we will create a style so that it is hidden until the user right-clicks.  It is all the way at the bottom of the next screenshot : the area with the blue boundary.

panel is visible

Let's go add the new CSS that will hide this panel until we neeed it.  We'll also set numerous other styles in an attempt to copy the style of a Windows Form context menu.

Creating the ContextMenu Style

Open up your site.css and add the following new style:

CSS
.RADContextMenu{
         z-index:10000000;
         visibility:hidden;
         display:none;
         width:200px;
         height:150px;
         position:absolute;
         font-family:'Microsoft Sans Serif';
         font-size:11px;
      }

You can see that we've set the visibility to hidden so now the item will not show up.  We've also set the display attribute to none.  That insures that even though the <div> is hidden it will not take up any space in the DOM. If you don't add this attribute you will see a blank space where the <div> would be located.

Width and Height: Number of Menu Items

For now, we set the width and height so when the item does display it will take up a specific amount of space.  We'll have to talk more about that item later, because it depends upon how many menu items will appear on the context menu.

Position : Show Up Where User Right-Clicks

We've also set the position to absolute which means place it at the exact x, y coordinates within the browser no matter what other elements are there.  That's because we are going to position the context menu exactly to the right of any location the user right-clicks.  However, to do that work, we will have to write some JavaScript.

Finally, we set the font and font-size to mimic a Windows Form context menu.

Of course, if you build and run you will not see the context menu element any more because we've hidden it.  

Let's go write our JavaScript and add it in to the ContextMenu.js to display it when the user right-clicks.

Z-Index Attribute Again

This element has a z-index also  and this time I've set it to 10,000,000.  That's ridiculous but it is just so it will always be on top of any other element.  Remember when we set the mainForm z-index to 1,000,000?  We can work out better values for the z-indexes later.  For now, this will work.

Display the ContextMenu Using JavaScript

Track Mouse Movement

The first thing we need to do is track the mouse movement.  That is because we need to know the X, Y location any time the right mouse is clicked so we can draw the ContextMenu at the cursor's location, just as it is on a Windows Form.  It's very easy to register for any browser even using jQuery.  

JavaScript
$(document).mousemove(onMouseMove);

That's all there is too it.  Now any time the mouse is moved over the document in the web browser then the onMouseMove() function will be called.  Our onMouseMove() function will simply get the current x, y location and store it in a global variable, mousePos, for our use.

JavaScript
function onMouseMove(e) {
    mousePos = { x: e.clientX, y: e.clientY };
}

Go ahead and define the global mousePos variable at the top of the JavaScript file too and initialize it as an object using the bracket notation { }.

JavaScript
var mousePos = {};

Each time the mouse is moved the mousePos will have its x property and a y property updated.  Then we can access that value later.

Test MouseMove

If you want to test the mouse move you can add the following line as the last line of the onMouseMove() function and build and run.  Then check your console as you move your mouse around on Index.cshtml and you'll see the value changing.

JavaScript
console.log("mousePos.x : " +mousePos.x.toString() + "  mousePos.y : " + mousePos.y.toString());

It generates a lot of output so remove it or comment it when your done.

Now that we know the x, y location of the mouse at all times, we can insure we draw your context menu at that location. 

Knowing When the Browser ContextMenu Is Displayed

Now things get interesting.  We want to display our custom context menu, however, the browser itself provides a context menu.  We need to override that one, but somehow still allow that one to be shown for times when we need it.

Here's the default context menu rendered in Chrome.

default context menu

First of all, we only want to show the customer context menu when a user right-clicks our mainForm element.  We can use jQuery to register that event.   There is actually a jQuery on() method that lets you provide the event you want to know about.  In our case we weant on("contextmenu") so that when the default context menu would display we interrupt it.  When we interrupt it, we want it to run our custom code.  

Shift Key Shows Default Context Menu

However, if the user holds the shift key down I am going to say we ignore our custom code and show them the default context menu.

preventDefault() Overrides Browser Event

When the user doesn't hold the shift key down, then our context menu will appear and it will prevent the default one from showing up.  We can do that by calling the e.preventDefault() method which is provided to us by the browser event model.  

Here's the entire code listing for to register for this contextmenu event.

JavaScript
$(".mainForm").on('contextmenu', function (e) {
                if (e.shiftKey === false) {
                    drawContextMenu();
                    e.preventDefault();
                }
            });
        }

Our custom code that will display our context menu is found in the drawContextMenu() method.

JavaScript
function drawContextMenu() {
    isContextMenuDisplayed = true;
    $(".RADContextMenu").css({ 'visibility': 'visible', 'display': 'block' });
    $(".RADContextMenu").css({ 'top': mousePos.y, 'left': mousePos.x });
}

It's all very simple once you know what to do.  It's very little code and that makes me happy.  The less code the better. :)  Especially, JavaScript.

How drawContextMenu() Works

The drawContextMenu() sets the global variable isContextMenuDisplayed to true so later we can determine if the context menu is currently on the screen.  In some cases when it is displaying we may want to hide the menu.  For example, when the user displays the context menu then doesn't want to run any of the functionality she can click somewhere else, off of the menu and it will disappear. 

The next thing we do is use the jQuery selector to get a reference to our menu.  When we do that, we call a jQuery helper function called css() which allows us to programmatically set some styles on the element.  As you can see, we are setting the visibility to visible and the display attribute to block so it will render properly over the other elements.

After that, we use the css() method to set the top attribute which is the x, y coordinate of the top left corner of our target <div>.  This is where we use our mousePos object so we can draw the context menu at the cursor's location.

That's all there is to it.  Now we can build and run again and we will see the context menu.

blank context menu

I clicked in that location and the context menu now displays.  You can see that the context menu is blank because we've added no individual items, but at least it is now displaying.   Let's go add some fake items for now just so we can see what it will look like.  We add items in the Context Menu by adding a list using the HTML <ul> (unordered list) and <li> list tags.   

Add Context Menu Items

Go back to the _Layout.cshtml and move down to the RADContextMenu <div> and let's add the following code (bolded in the next code listing).

HTML
<div class="RADContextMenu panel panel-primary">
  <div class="panel-body">
    <ul>
      <li onclick="onContextMenuClick(this)" id="lookupValue" class="contextMenuItem">Get Lookup Value</li>
      <li onclick="onContextMenuClick(this)" id="randomInteger" class="contextMenuItem">Get Random Integer</li>
      <li onclick="onContextMenuClick(this)" id="randomWord" class="contextMenuItem">Get Random Word</li>
      <li onclick="onContextMenuClick(this)" id="randomFloat" class="contextMenuItem">Get Random Float</li>
      <hr class="hrContextMenu" />
      <li onclick="onContextMenuClick(this)" id="lastError" class="contextMenuItem">Get Last Error</li>
      <hr class="hrContextMenu" />
      <li onclick="onContextMenuClick(this)" id="nothing" class="contextMenuItem">Do Nothing</li>
     </ul>
    </div>
</div>

We can add that code, build and run again but things still aren't quite right.  

menu step 2

We need some more styles added to help us.  

Hover Over Menu Item Should Change Item

On a real context menu the item you hover over changes color. Plus, I need to explain what happens when you click each of the items. Let's add the styles, view the changes and then we will fix up more of the JavaScript.

We actually need three new styles:

  1. a style for the <ul> element when it appears on a RADContextMenu
  2. a style for the <li> element when it appears on a RADContextMenu
  3. a style for the panel-body when it appears on a RADContext menu (alter the Bootstrap style)

Basically we need to clear out those bullets (dots) that automatically show up on a list item and we need to clear out some of the padding and margin that occurs on items.

We just add the following CSS to site.css to do that work.

CSS
.RADContextMenu ul{
        /*width: 100%;  */
        list-style-type: none; 
        margin: 0px 0px 0px 0px;
        padding: 0px 0px 0px 0px;
          
}

.RADContextMenu li{
          /*width:100%;*/
          margin: 0px 0px 0px 0px;
          padding: 3px 0px 3px 15px;
}

.RADContextMenu .panel-body{
        width: 100%;
        padding: 0px 0px 0px 0px;
        margin: 0px 0px 0px 0px;
}

I've also added a couple of <hr> horizontal rule tags which render as horizontal lines and they don't look quite right so I need to alter their CSS also.  Add the following for those <hr> tags.

CSS
.hrContextMenu{
    display: block; height: 1px;
    border: 0; border-top: 1px solid #4281F4;
    margin: 0 0 0 0; 
    padding: 0 0 0 0;
}

If you check back in _Layout.cshtml you can see all of these styles applied.

context menu looks good

Now the context menu looks quite good.  And, I'm confident that if you've been following along you really understand how we've accomplished everything.

Of course, we still need the menu item to change when the mouse hovers over it.  We just need one more style to accomplish that, because CSS provides the hover attribute we can use.

Let's add that style in now.  It simply sets the style so that background is blue and the text becomes white.

CSS
.contextMenuItem:hover {
    background-color: #4281F4;
    color:white;
}

Our Context Menu is STYLE COMPLETE!!

Of course, you can't tell from the static picture but as you move the mouse over each item the style changes and makes it feel very responsive to the user.

context menu style complete

Get Source: Download v005

Right-clicking and displaying the menu and then floating over that menu is so much fun that you have to tryit.

You can get the code as it stands so far by downloading V005 from the top of this article.  Do it now and try out that fantastic hover functionality.  :)

How Does Functionality Get Fired?

Now, we just need to hook up the JavaScript so that when the user selects one of the menu items then specific functionality gets fired.  If you look back at our HTML code listing from _Layout.cshtml you will see that we added onclick() handler.  That is a method that will fire when the element is clicked.  It looks like the following:

HTML
<li onclick="onContextMenuClick(this)" id="lookupValue" class="contextMenuItem">Get Lookup Value</li>

That HTML insures that any time the item is clicked then the onContextMenuClick() method will be ran.  We need to define that method in our ContextMenu.js file and write the implementation for it.

Notice also that the method takes the this object as a parameter.  In this case the this object represents the DOM element which has been clicked.  That way the same method can handle the functionality for each individual <li> that is clicked.  You will see in the JavaScript method that it will check the value of the this object's id so we can know which element was clicked.  

Let's take a look at the implementation of the code now.

onContextMenuClick() Implementation

The first thing we need to do when the Context Menu item is clicked is hide the context menu.  That's because once the user has selected the item the menu is no longer necessary and would only get in the way.

JavaScript
function onContextMenuClick(e)
    {
      console.log("onContextMenuClick()");
      hideContextMenu();
      console.log(e.id);
    }

In this case, I use console.log to output to the console to insure the menu click even is firing.  AFter that I call another method we need to write, hideContextMenu() which will hide the menu.

Finally, I write out the id of the element that the method was called on.  This way we can take a look at the value and make sure our thinking is correct.  First let's add the hideContextMenu() method and then we'll try running the code to see what we get when we click a menu item.

hideContextMenu() : Making the Menu Invisible Again

JavaScript
function hideContextMenu() {
    if (isContextMenuDisplayed) {
        $(".RADContextMenu").css({ 'visibility': 'hidden', 'display': 'none' });
        $(".RADContextMenu").css({ 'visibility': 'hidden', 'display': 'none' });
        isContextMenuDisplayed = false;
    }
}

This code is really the direct opposite of what we do when we want to show the Context Menu and the functionality that is in drawContextMenu().

Now that we have a basic structure of what we want to happen, let's run the code again and watch the console window as we click items on the menu.

running context menu to console

Code Download v006

You can get the source and try this by downloading V006 code from the top of this article.  Once you download it, build and run, just click the F12 button while its running in your browser to open the console.

After that start clicking menu items and the id values will appear and the context menu will hide itself.

Add Two More Things

Well, there are just two more things to add and we have our Windows Form element and Context Menu foundation.

We will add:

  1. A call to hideContextMenu() any time the user clicks anywhere else except on the visible context menu.
  2. A switch statement which will allow us to run different functionality depending upon which menu item was selected.

Let's finish up those two things and we'll have everything we need to finish out our app in Part 3.

Hide Context Menu When It Isn't Clicked On

To solve the problem of hiding the Context Menu when it is visible but ins't clicked on we just need one more line of code.  This line of code registers the click() event for the entire document.  Any time a click occurs on the document, hideContextMenu is called.  If the Context Menu is shown then it will be hidden.  Otherwise it won't.

JavaScript
$(document).click(hideContextMenu);

We simply add that to the load event at the top of our ContextMenu.js.

You can try it out when you download v007 of the source code from the top of this article.  That will be the final source code listing and will contain the rest of our work also.

Add Switch Statement To onContextMenuClick()

To insure we run the custom code when a specific menu item is clicked we simply switch on the value we get in the id.  The code looks like the following.  Notice, I am again using console.log to stub out this code.  In Part 3 we will finally be making our AJAX calls to the server to for each of these statements.

JavaScript
function onContextMenuClick(e) {
    console.log("onContextMenuClick()");
    hideContextMenu();
    console.log(e.id);
    switch (e.id) {
        case "lookupValue":
        {
            alert("I am looking up a value.");
        }
        case "randomInteger":
        {
            alert("I am generating a random integer");
            break;
        }
        case "randomWord":
        {
            alert("I am  generating a random word.");
            break;
        }
        case "randomFloat":
        {
            alert("I am  generating a random float.");
            break;
        }
        case "lastError":
        {
           alert("The last error was : system failure.");
            break;
        }
        case "nothing":
        {
            alert("I am doing nothing.");
            break;
        }
    }
}

I've implemented the functionality as an alert dialog for now. Here's a snapshot of what it looks like as it runs.  

final working menu

For each menu item you choose you will get a different alert dialog message.

Get The Final Download Source V007

Get the final download (v007 at the top) for this article and try it out.   We've really learned a lot through this article and come a long way.  Next time, in Part 3, you get to see how this will work with asynchronous calls to the server to display real data.  See you next time.

History

03/24/2017 -- First release of code and article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

raddevus
Software Developer (Senior) RADDev Publishing
United States United States
Roger has worked in IT for over 25 years in numerous roles (Technical Support, Quality Assurance, Capacity & Performance Engineering and Software Development).
During that time, he has recognized that software often just becomes another layer of work that the user has to wade through.
Sometimes technical documentation is like that too: so confusing and complex that it wastes developers' time.
That's why when he writes his books like Programming Windows 10 Via UWP and his articles (Practical Electronics For Makers) he strives to explain things in the shortest available space with the simplest language possible. Often that means, writing in a tutorial style with numerous images to help guide the user.
He believes the best guiding principle is Einstein's famous quote: "Everything should be made as simple as possible, but not simpler."

Comments and Discussions

 
-- There are no messages in this forum --