Try-Catch-FAIL

Failure is inevitable.

What makes a developer “great”?

clock June 9, 2009 06:46 by author Matt

I’ve been thinking recently about how to define the skills and traits that make for a “great” developer.  I’m certainly not an authority on the subject, but I consider myself to be a pretty solid developer.  I’ve worked with some truly stellar people, too, so I know what a great developer looks like.  But as I’ve tried to come up with a description, I’ve found that defining a great developer is a lot harder than recognizing one.  Here are a few traits that I think great developers usually have. 

Strong problem-solving skills – Software development is mainly about solving problems, particularly new problems (or variations on existing problems).  Let’s face it, if this wasn’t true, there would be no need for developers, since monkeys would be able to take off-the-shelf components, slap them together, and successfully meet all the customer requirements.  In reality though, any solution requires at least some customization and novel thinking; if not, the customer would be much better off using the existing solution.  So being able to solve problems is very important, but what makes a developer “great” is an ability to quickly come up with elegant solutions to problems.  A “great” developer doesn’t reinvent the wheel, either, he recognizes and utilizes existing solutions whenever possible.

A passion to learn – Software development is not a skill that you pick-up in school then coast on for the rest of your career.  “Great” developers recognize this and make both a professional and personal commitment to advance their knowledge.  They constantly explore new things, acquire new skills, and seek knowledge from those that are more knowledgeable than they are.  A “great” developer is not content and always seeks to improve themselves.

An ability to craft elegant solutions – A “great” developer will solve a problem using a solution that is both readable and efficient.  It will contain exactly as much code as is needed and no more.  A “great” developer will not hammer a problem with more and more code until it “works”.  A “great” developer dies a little inside every time he sees that happen.

A willingness to teach - “Great” developers are not greedy with their knowledge.  They’re happy to share it; they want to help their fellow developers learn.  They may not want to hand-hold constantly, but a “great” developer will be more than willing to provide feedback and guidance when asked.

An understanding of the differences between “developer” and “programmer” – While every developer is a programmer, not every programmer is a developer.  A “great” developer not only understands this, but they embrace it.  They know that development doesn’t just mean writing code, it means understanding the business and the users.  It means testing code, not just flinging it at the screen.  It means documenting code so that solutions aren’t write-only.  It means designing solutions, not just hacking at it until it works.  A “great” developer understands that the majority of their time will be spent not writing code. 

 

That’s certainly not an all-inclusive list, and if I sat here longer, I’d probably think of other things.  What do you think are some key traits that separate a great developer from an average one?

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Thesis defense complete!

clock June 3, 2009 01:22 by author Matt

The reason I suddenly went dark (again) was due to an unexpected change in the timing of my thesis defense.  Originally my deadline was July 9th, but I found out at the end of May that I actually had to defend by Friday, June 5th, because some of my committee members were going to be taking extended trips during the summer.  Last week, the schedule was again accelerated, as my defense was moved from June 5th to June 2nd (yesterday).  Fortunately, many late nights and lots of Red Bull were all it took to wrap things up, and I am now all clear to graduate in August.  I have the usual edits to make and a few things to clean up, but I’m mostly finished now. 

I probably won’t be posting much this week as I try to finalize my thesis and catch up on all the things that have fallen by the wayside, but I will start blogging again.  I still have lots of things I want to cover: deep-web crawling (and the public release of DeepCrawler.NET), my machine learning library for .NET, Lucene.NET, object databases, and more. 

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


ASP.NET MVC HtmlHelper for Uploadify, Take One

clock May 13, 2009 06:56 by author Matt

As I’ve mentioned before, I really, really hate the way most people seem to be creating reusable UI “controls” with ASP.NET MVC.  I do not like emitting JavaScript, HTML, etc. from within C# code.  It’s cumbersome to create, difficult to really test, and just a real PITA in general.

Based on feedback I received from Rob after my attempts at creating a helper for jqGrid, I decided to take a completely different approach when it was time to wrap another jQuery plug-in: Uploadify.  My goal was to minimize the amount of tag-soup embedded in my C# code while still maintaining the ease-of-use of the jqGrid helper, which required only a single HtmlHelper call to go from nothing to full grid.

Well, one painful afternoon later, I think I’ve arrived at something that makes some sense.  First, I couldn’t completely eliminate the tag soup, but I did minimize it (I think) while still keeping the thing extremely simple to use and (hopefully) maintain.  Let’s start with how you would use it:

<asp:Content ContentPlaceHolderID="HeadContent" runat="server">
    <%=Html.Uploadify("fileInput", 
        new UploadifyOptions
           {
               UploadUrl = Html.BuildUrlFromExpression<SandboxController>(c => c.HandleUpload(null)),
            FileExtensions = "*.xls;*.xlsx",
            FileDescription = "Excel Files",
            AuthenticationToken = Request.Cookies[FormsAuthentication.FormsCookieName] == null ?
                string.Empty :
                Request.Cookies[FormsAuthentication.FormsCookieName].Value,
            ErrorFunction = "onError",
            CompleteFunction = "onComplete"
           }) %>
           
    <script type="text/javascript">
        function onError() {
            alert('Something went wrong.');
        }
        function onComplete() {
            alert('File saved!');
        }
    </script>                                                   
</asp:Content>

The first parameter is the name of the input control to convert to an uploadify control, the second contains all the optional settings you can customize.  I prefer to use an options class like this rather than provide 50,000 overloads.  By using a dedicated options class, I can add new settings without breaking existing code or having to create new overloads.  The options should be fairly self explanatory, but here they are:

/// <summary>
/// Defines all options for <see cref="HtmlHelperExtensions.Uploadify"/>.
/// </summary>
public class UploadifyOptions
{
    #region Public Properties

    /// <summary>
    /// The URL to the action that will process uploaded files.
    /// </summary>
    public string UploadUrl { get; set; }

    /// <summary>
    /// The file extensions to accept.
    /// </summary>
    public string FileExtensions { get; set; }

    /// <summary>
    /// Description corresponding to <see cref="FileExtensions"/>.
    /// </summary>
    public string FileDescription { get; set; }

    /// <summary>
    /// The ASP.NET forms authentication token.
    /// </summary>
    /// <example>
    /// You can get this in a view using:
    /// <code>
    /// Request.Cookies[FormsAuthentication.FormsCookieName].Value
    /// </code>
    /// You should check for the existence of the cookie before accessing
    /// its value.
    /// </example>
    public string AuthenticationToken { get; set; }

    /// <summary>
    /// The name of a JavaScript function to call if an error occurs
    /// during the upload.
    /// </summary>
    public string ErrorFunction { get; set; }

    /// <summary>
    /// The name of a JavaScript function to call when an upload
    /// completes successfully. 
    /// </summary>
    public string CompleteFunction { get; set; }

    #endregion
}

Next, we have the actual HtmlHelper extension method:

/// <summary>
/// Renders JavaScript to turn the specified file input control into an 
/// Uploadify upload control.
/// </summary>
/// <param name="helper"></param>
/// <param name="name"></param>
/// <param name="options"></param>
/// <returns></returns>
public static string Uploadify(this HtmlHelper helper, string name, UploadifyOptions options)
{
    string scriptPath = helper.ResolveUrl("~/Content/jqueryPlugins/uploadify/");

    StringBuilder sb = new StringBuilder();
    //Include the JS file.
    sb.Append(helper.ScriptInclude("~/Content/jqueryPlugins/uploadify/jquery.uploadify.js"));
    sb.Append(helper.ScriptInclude("~/Content/jqueryPlugins/uploadify/jquery.uploadify.init.js"));

    //Dump the script to initialze Uploadify
    sb.AppendLine("<script type=\"text/javascript\">");
    sb.AppendLine("$(document).ready(function() {");
    sb.AppendFormat("initUploadify($('#{0}'),'{1}','{2}','{3}','{4}','{5}',{6},{7});", name, options.UploadUrl,
                    scriptPath, options.FileExtensions, options.FileDescription, options.AuthenticationToken,
                    options.ErrorFunction ?? "null", options.CompleteFunction ?? "null");
    sb.AppendLine();
    sb.AppendLine("});");
    sb.AppendLine("</script");

    return sb.ToString();
}

The helper uses a StringBuilder (yeah, I hate them, and I’m open to suggestions) to include two JavaScript files.  The first is the standard uploadify script, but the second is something custom, which I’ll get to in just a second.    Finally, the helper outputs a call to initUploadify inside of the page load event, passing in all the options that were specified.

And that brings us to that second JavaScript include:

//This is used in conjunction with the HtmlHelper.Uploadify extension method.
function initUploadify(control, uploadUrl, baseUrl, fileExtensions, fileDescription, authenticationToken, errorFunction, completeFunction) {
    var options = {};

    options.script = uploadUrl;
    options.uploader = baseUrl + 'uploader.swf';
    options.cancelImg = baseUrl + 'cancel.png';
    //TODO: Make this an option?
    options.auto = true;
    options.scriptData = { AuthenticationToken: authenticationToken };
    options.fileExt = fileExtensions;
    options.fileDesc = fileDescription;

    if (errorFunction != null) {
        options.onError = errorFunction;
    }

    if (completeFunction != null) {
        options.onComplete = completeFunction;
    }

    control.fileUpload(options);
}

In here, I’ve created a simple JavaScript function that actually calls the uploadify JavaScript plug-in.  By using this method instead of using C# to emit the configuration code directly, I’m cutting out a fair amount of tag soup, and I’m wrapping things up in a way that will be easier to change in the future.  Hopefully.  The down side to this approach is that you have to create a new JavaScript method and include for every plug-in you want to use, but combining the scripts and correctly setting cache headers should reduce the request overhead.

I’m not claiming that this is the best way to do this.  In fact, I really hope it isn’t, because I still don’t like it.  But I think that I like it better than the approach I took for jqGrid.  If you have any suggestions or feedback, please share.  Feel free to tell me that I’m doing things completely wrong.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Currently rated 3.0 by 1 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Using Flash with ASP.NET MVC and Authentication

clock May 13, 2009 01:47 by author Matt

There is a well-known bug in Flash that causes it to completely ignore the browser’s session state when it makes a request.  Instead, it either pulls cookies from Internet Explorer or just starts a new session with no cookies.  GOOD CALL, ADOBE.  And when I say this bug is well-known, I mean it was reported in Flash 8.  It’s still sitting in the Adobe bug tracker.  It has been triaged, it seems to have high priority, yet it remains unfixed.  Again, GREAT job, Adobe. 

Anyway, why should you care?  Well, if you want to use Flash for anything, even something simple like AJAX file uploads with Uploadify, you better hope you don’t need authorization and authentication.  But really, why would you want to authenticate users before letting them upload stuff to your site, anyway?  There’s no possible way that could ever be exploited, right?

If you do decide that security is important (HINT: IT IS), there are some well-known hacks to work around it.  None of them fit well with ASP.NET MVC though.  Just when all seemed lost, I found this post from Ariel Popovsky that saved the day.  I have wrapped his solution up in an easy-to-apply custom AuthorizationAttribute that you can tag to a controller or action method.  Here’s the code:

/// <summary>
/// A custom version of the <see cref="AuthorizeAttribute"/> that supports working
/// around a cookie/session bug in Flash.  
/// </summary>
/// <remarks>
/// Details of the bug and workaround can be found on this blog:
/// http://geekswithblogs.net/apopovsky/archive/2009/05/06/working-around-flash-cookie-bug-in-asp.net-mvc.aspx
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class FlashCompatibleAuthorizeAttribute : AuthorizeAttribute
{
    /// <summary>
    /// The key to the authentication token that should be submitted somewhere in the request.
    /// </summary>
    private const string TOKEN_KEY = "AuthenticationToken";

    /// <summary>
    /// This changes the behavior of AuthorizeCore so that it will only authorize
    /// users if a valid token is submitted with the request.
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        string token = httpContext.Request.Params[TOKEN_KEY];

        if (token != null)
        {
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(token);

            if (ticket != null)
            {
                FormsIdentity identity = new FormsIdentity(ticket);
                string[] roles = System.Web.Security.Roles.GetRolesForUser(identity.Name);
                GenericPrincipal principal = new GenericPrincipal(identity, roles);
                httpContext.User = principal;
            }
        }

        return base.AuthorizeCore(httpContext);
    }
}

The filter checks the request to see if the authentication ticket was submitted.  If so, it tries to decrypt it, then recreates the IPrincipal that is needed by the base AuthorizationAttribute to do its work.  Just apply it to your controller, make sure Flash is submitted the value of the Forms Authentication cookie, and BAM, everything works.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Testing SyntaxHighlighter

clock May 13, 2009 00:53 by author Matt

This is a test post to see how SyntaxHighlighter works with Windows Live Writer.  I’m using the PreCode plug-in. So, C#:

public class Testing
{
    public Id { get; private set; }
    public void Method(string parameter)
    {
        Console.WriteLine("Testing!");
    }
}

And JavaScript:

function(x,y)
{
    alert('X is:' + x + " and Y is: " + y);
}

And SQL:

SELECT * FROM Table
WHERE Col1='Test' 
AND 1=0

HTML:

<div>
    <h1>Something!</h1>
    <p>Something else!</p>
</div>
Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


“Hi, we’re Square Enix, and we are totally LAME!”

clock May 11, 2009 07:47 by author Matt

This isn’t directly development-related, but it is noteworthy.  On May 8th, Square Enix, makers of Final Fantasy, Chrono Trigger, etc, hit Chrono Compendium with a cease-and-desist related to ROM hacking and modifications, particularly as it relates to the Crimson Echoes fan project.  Square Enix has sent C&D projects in the Chrono community before, but they were always to people that were recreating Chrono in a new engine/platform/whatever, not to groups that were creating ROM hacks.  ROM hacks are essentially nothing more than mods, which are standard fare these days.  Instead of being appreciative of a community that has thrived despite being largely abandoned for the better part of a decade, Square Enix decided to be LAME and resort to legal threats. 

I was actually look forward to playing Crimson Echoes, and I’m very sad to see it go.  This is a great example of how screwed up intellectual properties are… anyway, if you have a blog and were/are a fan of the Chrono series, spread the word.  I highly recommend *NOT* buying things with the Square Enix label since they are unoriginal dicks who are surviving mostly on name and rehashes of old IP.  Good job, Square!  Digg the story here.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Correctly referencing jqGrid in ASP.NET MVC

clock May 8, 2009 07:41 by author Matt

Show of hands, how many of you are using jqGrid?  Alright, now how many of you have tried to run your ASP.NET MVC application in a configuration where the root of the application isn’t “/”? Alright, now raise your hand if you are having to edit jquery.jqGrid.js when you move the application to the alternate configuration? 

NOTE: I’m assuming that your development environment runs with the application at the root of WebDev.WebServer.exe; if not, simply swap the assumptions, and I bet you still have the same problem I’m about to describe.

If your hand is not up, congratulations, you are doing something correctly (I think).  If your hand is up, you have an extra step in your deployment process.  Deployment should be as simple and as automated as possible, so needing to change a path in a JavaScript file is a Bad Thing.  Until today, our application required JavaScript changes when we moved it from development (where it runs at the root of the site) to staging (where it runs in a folder).  I took the time to fix it today.  Here’s how I did it.

First, open up jquery.jqGrid.js and find the line where “pathtojsfiles” is defined.  Delete it.  Add a parameter of the same name (pathtojsfiles) to the jqGridInclude function, like so:

   1: function jqGridInclude(pathtojsfiles)
   2: {
   3: ...
   4: }

Next, jump to the end of the file, and remove this line:

   1: jqGridInclude();

Now, in your view markup, add a new script block after you’ve included jquery.jqGrid.js:

   1: <script type='text/javascript'>
   2: jqGridInclude('<%=Url.Content("~/Content/path/to/JQGrid/js/")%>');
   3: </script>

What did we do?  We modified jqGridInclude to take the base path as a parameter, and we changed it to not be called automatically when the file is included.  Instead, we call it explicitly and use Url.Content to get the correct path to jqGrid’s JavaScript.

The down-side to this approach is that you’ve made (minor) modifications to jqGrid files, so you will need to re-apply this fix if you upgrade to a new release.  But, that’s a small price to pay for eliminating a step from your deployment process.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Introduction to Lucene.NET

clock May 8, 2009 01:43 by author Matt

This is the obligatory “introduction to something that I’m going to be talking about periodically” post.  I think a lot of developers (myself included until recently) are not familiar with Lucene/Lucene.NET and where it fits in their development toolbox.  By the end of this post, you should understand the basics of Lucene, and you will (hopefully) want to come back for future posts that will show you how to use Lucene in your applications.

What is Lucene?

Good question.  For that, I defer to the documentation: Apache Lucene is a high-performance, full-featured text search engine library written entirely in Java. It is a technology suitable for nearly any application that requires full-text search, especially cross-platform.  Pretty self-explanatory, right?  Ok, maybe not.  Basically, it’s an open-source library that you can use if your application needs to provide search capabilities that go beyond simple relational queries. 

But Matt, this is a .NET blog, why are you talking about a Java library?  I HATE JAVA!”  Yeah, we all hate Java, but they have some cool projects (that run like crap because the Sun JavaVM is horrible, horrible abomination that should be purged by fire*).  As is the case for most good Java projects, we (the .NET community) have “borrowed” Lucene and created Lucene.NET: Lucene.Net is a source code, class-per-class, API-per-API and algorithmatic port of the Java Lucene search engine to the C# and .NET platform utilizing Microsoft .NET Framework.  Basically, Lucene.NET brings the Lucene API into .NET.  Be warned though: it is a very direct port.  You will see lots of GetSuchAndSuch and SetSuchAndSuch methods instead of properties; you will see things that expose iterators but are not IEnumerable, meaning you can’t just drop them into a foreach loop.  You will see things that *should* implement IDisposable, but don’t. 

Those issues aside, the fact that we can use Lucene in .NET is still awesome, awesome enough to outweigh the Javainess of the API. 

What’s it used for?

The most common use for Lucene is full-text indexing and search.  “But I’m not building a search engine, I’m building a bug tracking/record management/whatever!”  Is your system storing text?  Think users might ever want to search the text?  Congratulations, you have a need for full-text search.  Lucene can allow you to quickly and easily index text fields for fast, efficient searching later.  And when I say fast, I mean fast.  Way, way faster than doing something stupid like string.Contains across all your records or doing WHERE Text LIKE ‘%whatever%’ in your database. 

Lucene can index and search more than just text though.  You can index dates, numbers… virtually anything that your users might want to build queries on.  Sure, you could do something similar using SQL Server, but…

Why not use SQL Server?

Because I am telling you: do not use SQL Server to store and search large amounts of text.  I have direct experience going down this road, and it leads only to pain and suffering.  Yes, all versions of SQL Server support full-text indexing, but words cannot describe how terrible the performance is for large databases (and how bad it is at actually ranking results).  It’s also very not-flexible, as opposed to Lucene, which is quite flexible, supports a wide range of queries, and does a pretty good job of ranking things.  Lucene is also not the overweight elephant that SQL Server is: it uses very little RAM and runs fine even on old or scaled-down machines. 

Here’s my advice: use SQL Server for things that map well to set-based operations.  Use it for creating reports that involve aggregation.  Use it for doing primary key look-ups.  But please, PLEASE, don’t use it as a search engine, especially not for text.  I have been down this road (in fact, I’m *still* trying to get off this road), and it is not anywhere you want to be.

Up Next

Whoa, this post is already finished?  I thought it would take longer to make the case for why Lucene is great, but obviously not since this is the last section.  In future posts, we’ll start looking at how to get up and running with Lucene.NET.  We’ll move on to advanced topics, such as distributed indexing and search, faceting, and (hopefully) even look at how to correctly use Lucene and SQL Server together.  We’ll also take detours along the way to see how you can integrate Lucene with your ORM solution to get full-text indexing for free.  If anyone has suggestions or ideas for things they want to see, please let me know!

*That’s not a joke.  I have no problems with the Java language, other than it being inferior and all, but I hate the Sun Java VM with the fiery passion of a thousand suns.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Simplified unit testing for ASP.NET MVC JsonResult

clock May 7, 2009 01:45 by author Matt

There are quite a few examples floating around on the web that describe how to test your JsonResult objects to make sure the data was correctly packaged.  They all follow the same basic pattern: mock out core ASP.NET objects (such as ControllerContext, HttpResponse, and HttpContext), call JsonResult.ExecuteResult, recover what was written to HttpResponse.Output, and deserialize it.  Sure, this approach works, but in the same manner as cleaning your house out by lighting it on fire.  It’s way overkill.  There’s a much easier way.  For simple objects, just cast JsonResult.Data:

   1: string value = "Hello, there!";
   2:  
   3: JsonResult result = new JsonResult { Data=value };
   4:  
   5: //SURPRISE!
   6: Assert.AreEqual(value, (string)result.Data);

Yeah, that seems fairly obvious.  You don’t even need the explicit cast there, I just threw it in to prove the point.  But what about anonymous types?  Easy:

   1: var value = new { Id=5, Something="Else" };
   2:  
   3: JsonResult result = new JsonResult { Data=value };
   4:  
   5: IDictionary<string,object> data = new RouteValueDictionary(result.Data);
   6:  
   7: Assert.AreEqual(5, data["Id"]);
   8: Assert.AreEqual("Else", data["Something"]);

See, easy! “But what about arrays of anonymous types?!?!?” Do not fret, LINQ to the rescue:

   1: var values = new[]
   2:                  {
   3:                      new { Id = 5, Something = "Else" },
   4:                      new { Id = 6, Something = "New" },
   5:                      new { Id = 7, Something = "Old" },
   6:                  };
   7:  
   8: JsonResult result = new JsonResult { Data = values };
   9:  
  10: IDictionary<string, object>[] data = ((object[]) result.Data).Select(o => new RouteValueDictionary(o)).ToArray();
  11:  
  12: Assert.AreEqual(5, data[0]["Id"]);
  13: Assert.AreEqual(6, data[1]["Id"]);
  14: Assert.AreEqual(7, data[2]["Id"]);
  15: Assert.AreEqual("Else", data[0]["Something"]);
  16: Assert.AreEqual("New", data[1]["Something"]);
  17: Assert.AreEqual("Old", data[2]["Something"]);

Again, easy!

Alright, I know what you’re thinking.  “But Matt, the other solutions are all way more complicated, plus you’re cheating, that isn’t what JsonResult.ExecuteResult is going to do!” Well, you’re half-right, the other solutions are way more complicated, but this is actually simulating precisely what ExecuteResult will do.  Don’t believe me?  Pop it open in Reflector, or just browse the source (man I love Subversion).  It isn’t doing anything magical, it’s just using JavaScriptSerializer.  My solution just cuts out the middle man and doesn’t require you to mock out a bunch of complicated objects.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Prevent users from abandoning changes accidentally with jqGrid

clock May 6, 2009 05:22 by author Matt

In the project I’m working on, we use jqGrid to display hierarchical records in an Excel-like manner.  Changes are queued up on the client and submitted to the server whenever the user clicks the “Save” button.   That means a user could make a bunch of changes, then navigate away from the page accidentally, and lose everything they just did.  Preventing that is easy enough though, just use this JavaScript snippet:

   1: window.onbeforeunload=checkForChanges;
   2: function checkForChanges(){
   3:     var rows = $('#treeTable').getChangedCells('all');
   4:     if (rows != null && rows.length > 0) {
   5:         return 'You have unsaved changes that will be discarded.';
   6:     }
   7: };

The checkForChanges function is bound to the onbeforeunload event, which is fired whenever the browser window is about to change the page.  This includes page reload/refreshes or anything like that.  The checkForChanges function uses the jqGrid getChangedCells method to see if there are any unsaved changes.  If there are, it returns a message that will be shown automagically in a JavaScript confirm dialog.  If there aren’t, it doesn’t return anything, and the user is allowed to navigate without being nagged.

Share or Bookmark this post…
  • del.icio.us
  • DotNetKicks
  • Digg
  • msdn Social
  • Reddit
  • StumbleUpon

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About Matt

I am an overworked (and apparently overpaid) software developer that moonlights as a graduate student in computer science. I started off coding in C over a decade ago.  Since then, I've migrated from C to C++ and branched out to C#, PHP, VB.NET, JavaScript, and worked with a wide assortment of other languages that I hope to never deal with again (I'm looking at you, COBOL). Oh, and yes, I've written some Java.  Does that make me a bad person?

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in  anyway.

© Copyright 2009

Sign in