Feb 012010
 

I’m not sure if what I’m doing is actually the right way to create a “user control” in ASP.NET MVC, but it’s worth sharing this tidbit either way. Instead of using a MVC View User Control to create a hidden field, a text box, two anchors, and three JavaScript functions, I chose to put it all in a HtmlHelper in which I write out the HTML and JavaScript myself. Everything worked fine except the almost magical auto-repopulating of the hidden and text fields after a post that didn’t work as expected as in a typical MVC View Page.

The situation: I have a page that needs to be called as a popup from many pages in my MVC application. The page allows single or multiple selection of “items” driven by an XML file. In the event that one day, almost always immediately, I have two or more of these “controls” on one view page, I need the two fields and the three JavaScript functions to have unique names so they don’t cross paths and cause unexpected behavior. I had an ASP.NET User Control to do this in plain old ASP.NET (POAN) since v1.1, and I can’t live without it.

The confusion: If I were to place the hidden, textbox, anchors, and JavaScript functions directly in the calling page, something magical happens after a post. If the controls had values before the post, they appear to magically retain there values after the post. It wasn’t until a colleague of mine, Sat, and I dug into Reflector for a while did we realize what was happening. Html.TextBox, Html.Hidden, and others all do something similar to auto-magically re-populate their values after the post. Since I’m writing out my fields as <input type=”hidden”/> and <input type=”text”/>, the magic doesn’t happen.

      NOTE: The magic will also not happen if you just write <input type=”text”/> on the page. It only happens if you use Html.TextBox.

The solution: I am still new to MVC and still trying to wrap my head around the “right way” to do things. Reflector showed that the HtmlHelpers all looked at the ModelState in the ViewData before rendering their HTML. They looked for their value by key (key being the control/tag name), and, if present, used that as the control/tag’s value. Bing! Maybe I should do the same thing. So just before I go to town with TagBuilder to assemble my controls/tags, I look in the ViewData’s ModelState for my value. If it is there, it must have been posted there by me (my control).

   48         UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);

   49         string textValue = null;

   50         ModelState state;

   51 

   52         if (helper.ViewData.ModelState.TryGetValue(textFieldName, out state))

   53         {

   54             textValue = state.Value.AttemptedValue;

   55         }

Works like a charm! Now my hidden, textbox, two anchors, and three JavaScript functions are bundled nicely inside of an HtmlHelper class that looks and feels like I’m using a built-in ASP.NET MVC HtmlHelper class. Most importantly, I have the pleasure of typing only this on all my consuming pages.

   40     <%= Html.MySelector(“selectedIDs”, “selectedNames”, “State”)%>

 Posted by at 2:50 am
Sep 212008
 

WCF never ceases to amaze me. Around every corner is another fascinating use for WCF, and much forethought on Microsoft’s part to make it look and behave great. I wanted to expose my services to my AJAX functions on my web site. I did not want to change my class library because it is used by other clients. I could just add the service classes to this web site, but why re-do when you can re-use.

If you have an existing WCF Service Library, you will need to expose it with the AspNetCompatibilityRequirementsMode.Allowed attribute on the service class to make it visible to ASP.NET clients. To avoid changing your service library in any way, the easiest thing to do is to add a new class to your web site that inherits from your service class. In this example, my existing service library uses the JeepServices namespace. Notice there is no implementation in this class. It is simply a placeholder for the real service implementation with the compatibility attribute attached.

    1 using System.ServiceModel;

    2 using System.ServiceModel.Activation;

    3 

    4 [ServiceBehavior]

    5 [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    6 public class WebHttpService : JeepServices.Service

    7 {

    8 }

Now that I have a ASP.NET compatible service, I need to expose it to the web site clients. Create a service file (.svc), and change the Service and CodeBehind attributes to point to the .svc file. The last thing you need is the Factory attribute. This notifies WCF of this service, eliminating the need for a configuration file entry for the service endpoint. In fact, you don’t even need the <system.servicemodel> in your configuration file at all. This is because it is only hosted as a web script, and cannot be called outside of the web site.

    1 <%@ ServiceHost Language=“C#” Debug=“true” Service=“WebHttpService” CodeBehind=“~/App_Code/WebHttpService.cs”

    2     Factory=“System.ServiceModel.Activation.WebScriptServiceHostFactory” %>

 

In your web page you will need a few things. First your will need a ScriptManager with a ServiceReference to the .svc file. You will then need the Javascript functions to make the call (DoJeepWork), handle the success message (OnJeepWorkSucceeded), and handle the failure message (OnJeepWorkFailed). Notice in DoJeepWork that you don’t call the service by it’s service name WebHttpService, you call it by the ServiceContract namespace and name. For this example, my interface has ServiceContract attributes Namespace = “JeepServices”, and Name = “JeepServiceContract”. Now you just wire up a ASP.NET control’s OnClientClick or an input or anchor tag’s onclick to DoJeepWork() and you are good to go.

    1 <%@ Page Language=“C#” AutoEventWireup=“true” CodeFile=“Default.aspx.cs” Inherits=“_Default” %>

    2 

    3 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”

    4 “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>

    5 <html xmlns=“http://www.w3.org/1999/xhtml”>

    6 <head runat=“server”>

    7     <title>Test page</title>

    8 

    9     <script type=“text/javascript”>

   10         function DoJeepWork() {

   11             JeepServices.JeepServiceContract.DoWork(OnJeepWorkSuccedeed, OnJeepWorkFailed);

   12         }

   13         function OnJeepWorkSuccedeed(res) {

   14             document.getElementById(“<%= this.lblMessage.ClientID %>”).innerText = res;

   15         }

   16         function OnJeepWorkFailed(error) {

   17             // Alert user to the error.   

   18             alert(error.get_message());

   19         }

   20     </script>

   21 

   22 </head>

   23 <body>

   24     <form id=“form1” runat=“server”>

   25     <div>

   26         <asp:ScriptManager runat=“server”>

   27             <Services>

   28                 <asp:ServiceReference Path=“~/Services/WebHttpService.svc” InlineScript=“false” />

   29             </Services>

   30         </asp:ScriptManager>

   31         <asp:Label ID=“lblMessage” runat=“server” Text=“No work has been done” />

   32         <a href=“javascript:void(0); DoJeepWork()”>Do Work</a>

   33     </div>

   34     </form>

   35 </body>

   36 </html>

 

Mission accomplished! Here you’ve seen how to expose an existing WCF service library without changing any code in the library itself. Adding two files allowed the service to be exposed to your AJAX clients. Best of all, there is no configuration file changes to make.

Useful Links:

 Posted by at 4:21 pm
Dec 192007
 

A great series of blog posts by Scott Guthrie about the ASP.NET MVC Framework coming soon as part of the ASP.NET 3.5 Extensions release.


Upon hearing the news, a few friends started questioning its intent, usefulness, and longevity. Many of us have been using or contemplating conversion to the MVP pattern, most recently using WCSF. The recent split of the MVP pattern by Fowler has caused many believers to question their faith. While many are still “proving” MVP, MVC has been around for nearly 30 years. Some believe that MVP and MVC can co-exist. Here is a comparison of MVP and MVC that concludes by painting an optimistic picture of MVP and MVC contributing to each other.


ASP.NET MVC appears to be the answer to my unit testing, REST, and code separation prayers. Thank you ScottGu and team!


Check it out!

 Posted by at 2:06 am
Nov 062006
 

The starter kit already allowed individual file upload, and batch upload from a directory which requires files to be moved to Upload directory by FTP.  The starter kit also stores the images in the database. While I prefer this method for most files, I don’t prefer it for images. I changed a few methods to store the images in an images folder, and modified the image serving handler accordingly.  The album page load time is a fraction of what it was with images in the database.


I also created an XP Publishing Wizard that allows any user with credentials to create/choose an album, and upload images from Windows XP. The beauty of the XPPW is that it can resize the images before uploading. That way all of us with 10 megapixel cameras no longer have to spend any extra time resizing to prevent reaching a web host storage quota.


A few articles helped me figure this stuff out:



Creating the wizard was easy enough.  You first need to create a registry entry in the following format:


Windows Registry Editor Version 5.00

[HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerPublishingWizardPublishingWizardProvidersYour Photo Gallery]
“displayname”=”Your Photo Gallery”
“description”=”Online Photo Albums”
“href”=”http://www.yoursite.net/XPPublish.aspx”
“icon”=”http://www.yoursite.net/favicon.ico”


Next is a single aspx page that accepts the files.  The page handles user login, album creation/selection, and accepts multiple files in a single form post.  All the hard work is done by some javascript methods that handle the XML sent from Windows XP.  The javascript looks like this:


        <script language=’javascript’>
            function startUpload()
            {
                var xml = window.external.Property(“TransferManifest”);
                var files = xml.selectNodes(“transfermanifest/filelist/file”);
                var albumId = document.getElementById(“Album”).value;

                for (i = 0; i < files.length; i++)
                {
                    var postTag = xml.createNode(1, “post”, “”);
                    postTag.setAttribute(“href”, “http://yoursite.net/XPPublish.aspx“);

                    postTag.setAttribute(“name”, “userpicture”);

                    var dataTag = xml.createNode(1, “formdata”, “”);
                    dataTag.setAttribute(“name”, “MAX_FILE_SIZE”);
                    dataTag.text = “10000000”;                    
                    postTag.appendChild(dataTag);
                    
                    var dataTag1 = xml.createNode(1, “formdata”, “”);
                    dataTag1.setAttribute(“name”, “btnUpload”);
                    dataTag1.text = “Save”;
                    postTag.appendChild(dataTag1);
                    
                    var dataTag2 = xml.createNode(1, “formdata”, “”);
                    dataTag2.setAttribute(“name”, “hidAlbumId”);
                    dataTag2.text = albumId;
                    postTag.appendChild(dataTag2);

                    files.item(i).appendChild(postTag);
                }
                
                var uploadTag = xml.createNode(1, “uploadinfo”, “”);
                uploadTag.setAttribute(“friendlyname”, “Family Photo Gallery”);
                var htmluiTag = xml.createNode(1, “htmlui”, “”);
                htmluiTag.text = “http://yoursite.net/Personal/Albums/Photos.aspx?AlbumID=” + albumId;
                uploadTag.appendChild(htmluiTag);

                xml.documentElement.appendChild(uploadTag);

                window.external.Property(“TransferManifest”) = xml;
                window.external.SetWizardButtons(true,true,true);
                document.getElementById(“divContent”).innerHtml = xml;
                window.external.FinalNext();
            }

            function OnBack()
            {
                window.external.FinalBack();
                window.external.SetWizardButtons(false,true,false);
            }

            function OnNext()
            {
                if (document.getElementById(“divLogin”))
                {
                    document.getElementById(“LoginArea_Login1_LoginButton”).click();
                }
                else
                {
                    startUpload();
                }
            }

            function OnCancel()
            {
            }

            function window.onload()
            {
                window.external.SetHeaderText(‘Photo Gallery’,’Your Photos’);
                window.external.SetWizardButtons(true,true,false);
            }
        </script>


In case you haven’t seen the XP Publishing Wizard in action, check out these screenshots:



 



 



 



 



 



 



 



 



 

 Posted by at 2:35 am
Jul 242005
 

Dmitri Khanine and Phil Carrillo author this fine article on Javascript RPC. Finally, an article that mentions separation of business and presentation logic, and implementing MVC in ASP.NET. We need more patterns and practices discussions, and a lot less “look what I can do” articles.


MSDN Article: Life without Refresh

 Posted by at 2:01 am