loads of useful information, examples and tutorials pertaining to web development utilizing asp.net, c#, vb, css, xhtml, javascript, sql, xml, ajax and everything else...

 



Advertise Here
C-Sharpener.com - Programming is Easy!  Learn Asp.Net & C# in just days, Guaranteed!

Translating an Asp.NET site to Another Language Using .resx Files

by naspinski 9/15/2009 10:35:00 AM

translating the .resx files to another language does not require any programming knowledge at all

Translating a properly globalized site in asp.net is simple and does not require the least bit of development knowledge... which is good, because most developers I know barely have a grasp on their own [non-programming] language. This post should probably come after one on how to globalize your asp.net site, but I have an urgent need to explain how to do this first (someone has volunteered to translate my open-source project Slick-Ticket to Spanish; thanks Victor!).

how does it work?

First of all, you should understand how this actually works, it may make it easier. Every page in a site has access to two types of resources (when I say resources here I am referring to strings, words or phrases): Global Resources, and Local Resources - these resources are held in .resx files. Global Resources are commonly used resources that all pages can see, and Local Resources are resources that only a specific page can see. Each language/dialect has a specific code, which you can see here: Language Codes (you can see your default setting in your browser options, for IE, check Tools->Internet Options->Languages). Each page then has place-holders where it will substitute a specific language's resource if it is available. For example, if your browser's settings are set to Mexican Spanish (es-mx) and you are trying to view example.aspx, asp.net will look for the example.aspx.es-mx.resx file to fill in the text. If that file is not present, it will default back down to the default resx file which is example.aspx.resx.

simple enough, how do I edit them?

.resx files are just xml files, and can be edited in a text editor, but that can be very confusing and error prone as there is no syntax and/or structure checking. I recommend you use a program such as Visual Studio if you have it, or Visual Web Developer (free) if you don't; alternatively, there are a few more options such as Mads Kristensen's .NET resource editor application for .resx files, and Resx Editor on SourceForge.

Once you have your editor, just go through and edit the column to be translated, and *ONLY* that one column, messing with the key/name field will break the translation completely.

folder structure

This is the only part that might be a bit confusing. Each folder that contains .aspx, .ascx, etc. files (files with words to be translated) will have their own App_LocalResources folder, and in this folder, there will be a corresponding *.aspx.resx file for each file. Also, any additional languages will be held in a folder with the language abbreviation, and a corresponding file for each file as well. For example, in the admin folder shown here, you can see that it has it's own App_LocalResources folder, in that folder is each corresponding default (in blue) .resx file and there is a folder for each additional language. Here you see the en-us folder (English - United States) and in that you see the same .resx file, but each is named *.en-us.resx (in red) instead of just *.resx. This is the same for each folder, so you will see many App_LocalResources folders. If you forget to change any of them, it will be pretty apparent when you run the program and a lot of stuff does not match up.

Then there is the App_GlobalResources folder in the root; the same thing applies here, just do the same and translate the value column.

Remember that asp.net will first look for your desired language, and use it if it is there, if not, it will drop down to the default.

a couple notes

A couple things to keep in mind, if you see any html markup (not the best practice, but sometimes necessary), remember this:

<do_not_edit>edit_this</do_not_edit>


Also, if you see brackets with a number in them (i.e. {0}, {2}, etc.), be sure to leave those, the developer is using that as a word placeholder.

that's it

Now, as long as it is available, your users will see your site in their preferred language. It is a bit tedious to translate, but much easier than walking through the markup and screwing things up that way. Also, if a resx file is ever missing, it will not error out, it will simply use the default for any parts that are not available.

Parsing/Reading a PDF file with C# and Asp.Net to text

by naspinski 3/16/2009 1:19:00 PM

PDFs are a very ubiquitous and useful file type, but they can be a pain to work with programatically

PDFs are extensively used in my organization, and people always want programs that will extract information from them. It can be very difficult to get the information they want due to the strange format, but sometimes it's a necessity. Here is how to get a PDF into text, from there you are on your own!

download the necessary files

There are always more than one way to skin a cat when it comes to programming, but the easiest way I have found for PDFs is to use the fantastic, open-source project PDFBox. The download is good for all sorts of platforms, but you only need a few parts to use it with Asp.Net and C#.

*Now keep in mind the version numbers I show here may change, but the process should stay the same.

what you need

  • Just a few things, pull the following files into your bin:
    • FontBox-0.1.0-dev.dll
    • IKVM.GNU.Classpath.dll
    • IKVM.Runtime.dll
    • PDFBox-0.7.3.dll

  • Now just make sure you add a couple references in your project, it is a bit of a strange process, so follow it closely. First, add this reference:
    • IKVM.GNU.Classpath

  • Then build the project, as the next reference requires the previous to be built. So you then add this reference:
    • PDFBox-0.7.3

  • Then build again - once again, there may be a better way to do this, but this is what the documentation said and it worked, so I won't mess with it. Now all you need to do is make sure you add the following using statements to any code file that needs to use the parser:
    using org.pdfbox.pdmodel;
    using org.pdfbox.util;

all set, now use it

This quick snippet shows how to use the program to take in a pdf file and output it to a .txt file.  The inputs are simply the string 'pdf_in' which is the path to a PDF to parse, and the string 'txt_out' which is the path to the output text file. You can easily modify it to take in a Stream of some sort or something else like using a FileUpload or output some other way, but this should get the idea across.
public void parsePDF(string pdf_in, string txt_out)
{
    StreamWriter sw = new StreamWriter(txt_out, false);
    try
    {
        sw.WriteLine();
        sw.WriteLine(DateTime.Now.ToString());
        PDDocument doc = PDDocument.load(pdf_in);
        PDFTextStripper stripper = new PDFTextStripper();
        sw.Write(stripper.getText(doc));
    }
    catch (Exception ex) { Response.Write(ex.Message); }
    finally
    {
        sw.Close();
        sw.Dispose();
    }
}

And there you have it, your PDF is now a (most likely, ugly and difficult to parse) text file with your PDF data in it; now it's up to you to figure out how to use it. As you will see, PDFs can (not always) be very strange in how they come out as text, tables will often be in odd order and such and it is a new adventure each time to engineer an effective and acurate parsing scheme that is very case-specific. Normally I would offer a download, but these files are pretty big, so I will leave it to the guys at SourceForge.

Tags: , ,

c# | tutorials

Inserting New Items Into a Table : 'REAL' AJAX with Asp.Net Series

by naspinski 3/15/2009 6:45:00 AM

Add a new item into the table and database and have it immediately sortable, pageable, etc without any postback

This is a continuation of my 'REAL' AJAX with Asp.Net (not Asp.Net AJAX) series posts for those of us trying to stop relying on Asp.Net 'AJAX'.

This is probably the hardest part about a full CRUD (Create, Read, Update, Delete) system, but as we have already covered 'RUD', it's time to hit the 'C'. First of all, we have to break down what will be happening, both on the client-side, and the server side, then it will be easier to dissect.
  1. User pulls up a 'New Entry' dialogue (client)
  2. User enters information and it is validated (client*)
  3. Valid information is sent to the server (client)
  4. Server attempts to add in new item (server)
  5. Server spits back status update (server)
  6. status update is displayed (client)
  7. item is added into the visible table (client)
  8. input fields are cleared and the input dialogue is hidden (client)

The * above denotes that while in this example we are doing only client-side validation, it is in your best interest to add in some server side as well (keep in mind that this example will not work without javascript, so it is not robust and does not 'gracefully' degrade).

As you can see, most of the heavy lifting here is done on the client side, with the dedicated server doing just a couple things.

set up the entry dialogue

First I am adding a couple divs which will be clicked to open the dialogue:

insert.aspx

<div class="button triggerNew">New Entry</div>


In the CSS, you can see that these will render like buttons:

demo.css
.button 
{ 
    width: 100px; 
    font-weight: bold; 
    border: outset 2px blue; 
    padding: 1px; 
    text-align: center; 
    color: Blue; 
}
.button:hover 
{ 
    border-style: inset; 
    cursor: pointer; 
    padding: 2px 0 0 2px; 
}

To go with the whole ajax feel, I am setting up the entry in a modal popup; you probably noticed the class 'triggerNew' which isn't really a css class, but it will be used to by jQuery to hook into any elements that have that class and tie them to the modal. To do that first we must include the jquery.jqModal plugin (which IMO is the best modal plugin as it is minimalist and customizable as well as easy to use) in our masterpage.
demo.master

<script type="text/javascript" src="js/jquery.jqModal.js"></script>


As for the modal popup, here is the markup:

<div class="jqmWindow" id="new">
  <h3 class="modal_header">
    <div class="working">working...</div>
    <a href="#" class="x jqmClose">X</a>
    Add New Entry
  </h3>
  <div class="pad">
    <div class="left">
      <h4>
        <asp:RequiredFieldValidator ID="rfvFirst" ControlToValidate="txtFirst" CssClass="right"
          ErrorMessage="required" runat="server" ValidationGroup="new" />
        First Name
      </h4>
      <input type="text" ID="txtFirst" runat="server" />
    </div>
    <div class="left">
      <h4>
        <asp:RequiredFieldValidator ID="rfvLasst" ControlToValidate="txtLast" CssClass="right"
          ErrorMessage="required" runat="server" ValidationGroup="new" />
        Last Name
      </h4>
      <input type="text" ID="txtLast" runat="server" />
    </div>
    <div class="left">
      <h4>
        <asp:RegularExpressionValidator ID="regAge" ControlToValidate="txtAge" CssClass="right"
          ErrorMessage="1-3 digits" runat="server" Display="dynamic" ValidationGroup="new" />
        <asp:RequiredFieldValidator ID="rfvAge" ControlToValidate="txtAge" CssClass="right"
          ErrorMessage="required" runat="server" Display="dynamic" ValidationGroup="new" />
        Age
      </h4>
      <input type="text" ID="txtAge" runat="server" />
    </div>
    <div class="summary">
      <div id="submit" class="button">Submit</div>
    </div>
  </div>
</div>


Now there is a lot going on there. First of all, I used the Asp.Net validators as we are most familiar with them, and they play nicely with jQuery. Also, I am not using asp:TextBoxes at all, I am sticking with the basic input boxes. But, because I am using the Asp.Net Validators, I still need to include runat="server" with each input that I Validate; this also means that they will get weird Asp.Net IDs and not the exact ones I assign them. All I am doing here is making sure all the fields have values, and that age is a diget with 1-3 digits (yes, someone can be 999 in this system...).

In addition, the css class 'jqmClose' class is included in a link: this will automatically be assigned to close the modal window by jqModal.

I also put an additional 'working' element in there so the user can see that dialogue while the program is running its magic.

But as it stands now, we can't even see that popup, get the validators to fire or submit any sort of data. Here comes the jQuery.

work the jQuery magic

Here is the script, it is explained in the comments:
//assign all 'triggerNew' elements to open the modal dialogue
$('#new').jqm({ trigger: false }).jqmAddTrigger($('.triggerNew'));

//attach this event to the clicking of the 'submit' div
$("#submit").click(function() {
    //will not do anything if the validators don't check out
    //Page_ClientValidate('ValidationGroup') is an Asp.Net generated function
    if (!Page_ClientValidate('new')) { return false; }
    else {
        // get the values from the textboxes into an array
        // notice that this is using .ClientID to get the strange Asp.Net ID assigned to it
        var vals = [
            $("#<%= txtFirst.ClientID %>"),
            $("#<%= txtLast.ClientID %>"),
            $("#<%= txtAge.ClientID %>")
        ];
        // do the ajax post
        $.post(
            //function is at
            "ajax_functions/insert.aspx",
            //send the values frorm the vals object
            { first: vals[0].val(), last: vals[1].val(), age: vals[2].val() },
            function(data) {
                //output the return data
                $("#report").html(data);
                // the class 'success' was sent back if it worked...
                var success = (data.toString().substring(12, 19) == "success");
                //add the new data into the table so the user can see it if it was successful
                if (success) {
                    oTable.fnAddData([
                    vals[0].val(),
                    vals[1].val(),
                    vals[2].val(),
                    '&nbsp;'
                ]);
                    //clear the textboxes
                    $(vals).each(function() { $(this).val('') });
                }
                //hide the modal now that it's done
                $('#new').jqmHide();
            }
        );
    }
});

Note that this will not allowed items added since the last postback to be deleted/edited, that is why the last cell simpy gets an '&nbsp;'. Keep in mind that it is possible, just beyond the scope of this tutorial.

Also, in a ghetto form of error reporting, I passed the css class which will either be 'error' on error, or 'success' otherwise.  We can use that to decide whether or not to post the new data into the table and to clear the inputs.  This will catch any errors that we did not already account for.

Really all that remains is the server-side part, we need to make the page that is called: "ajax_functions/insert.aspx" to handle the inputs passed.

server-side functions

Notice this time that we used a $.post() method, which sends the stated values and returns something - in this case, we are having it return a chunk of html which will tell us what happened with the server, and it will be pushed into the 'report' div. Here is our server operations:

ajax_functions/insert.aspx.cs
using System;

public partial class ajax_functions_insert : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        demoDataContext db = new demoDataContext();
        try
        {
            // get all the new values
            string first = Request.Form[0];
            string last = Request.Form[1];
            int age = Convert.ToInt32(Request.Form[2]);

            //make a new person
            person p = new person()
            {
                first_name = first,
                last_name = last,
                age = age
            };

            //insert them into the db
            db.persons.InsertOnSubmit(p);
            db.SubmitChanges();

            //if it was all successful
            Response.Write("<div class='success'>" + first + " " + last + " [" + age + "] inserted</div>");
        }
        // this will catch all the errors and output an error message if caught
        catch (Exception ex) 
        {
            Response.Write("<div class='success'>Error: " + ex.Message +"</div>");
        }
    }
}

As usual, this is the easy part. Now you have a full working CRUD system, with some pretty good error catching as such with absolutely no postbacks! Ajax is fun and can be quite useful, I hope these tutorials helped make it a little more accessible to some Asp.Net devs, I know I learned a lot making them.

« Deleting

Deleting Items From a Table : 'REAL' AJAX with Asp.Net Series

by naspinski 3/11/2009 1:50:00 AM

Delete an entry from the DB and remove the corresponding row from the table; plus an ajax loading indicator

This is a continuation of my 'REAL' AJAX with Asp.Net (not Asp.Net AJAX) series posts for those of us trying to stop relying on Asp.Net 'AJAX'.

GridView has an incredibly easy 'Delete' feature when used in conjunction with a DataSource, the problem is, that is requires a postback. Since we are going the ajax route, this is a no-go. Alternatively, we could wrap in in an UpdatePanel, but that is exactly what we are trying to avoid. Deleting is very easy, we use a lot of the same methods that were involved in fixing the sort on the last post and using the jQuery .ajax() method.

changes to the markup

First we have to add another column for deleting and stick a delete image in there, this is simple enough. I added unique ids to each row and each delete cell for jQuery to have something to hook on to, you could also add classes. The markup hasn't changed all too much:

delete.aspx

<asp:Repeater ID="rpt" runat="server" DataSourceID="lds">
  <HeaderTemplate>
    <table id="sort_table">
      <thead>
        <tr>
          <th>First</th><th>Last</th><th>Age</th><th></th>
        </tr>
      </thead>
      <tbody>
  </HeaderTemplate>
  <ItemTemplate>
    <tr>
      <td id="frst<%#Eval("person_id")%>" class="editable">
        <%#Eval("first_name")%>
      
</td>
      <td id="last<%#Eval("person_id")%>" class="editable">
        <%#Eval("last_name")%>
      
</td>
      <td id="age_<%#Eval("person_id")%>" class="editable">
        <%#Eval("age")%>
      
</td>
      <td style="test-align:right;" id="del_<%#Eval("person_id")%>">
        <img src="images/delete_item.png" alt="delete" />
      </td>
    </tr>
  </ItemTemplate>
  <FooterTemplate>
    </tbody></table>
  </FooterTemplate>
</asp:Repeater>
<asp:LinqDataSource ID="lds" runat="server"
  ContextTypeName="demoDataContext" TableName="persons" />


We put the id on the cell itself and not the image because it will be easier to grab with dataTables, not to mention easier to click.

now for the js

First of all, you will notice if we leave the .dataTables() call the same as before, you can now 'sort' by the last column (the delete column), that isn't right, we have to pass some parameters to our 'dataTables()' method to make sure that column gets ignored:
oTable = $("#sort_table").dataTable({
    "aoColumns": [null, null, null, { "bSortable": false}]
});

Also notice that we decided to store the resulting table in 'oTable' just like the last lesson. Remember that this is because the sortable data is not kept in the table itself, but in an associated array, which we will need later.

Now, using the same method as last time, we will grab the position of the clicked item, attempt the ajax call, but instead of changing a value, we will delete a row. Normally, deleting a row in ajax is very simple, you grab the row by the id, and do a hide() or slideUp(), etc. - but that will not totally work here. Sure it would function, but since we are using sizing and paging, they wouldn't reflect the changes: if you had 30 items in your table, and you were 'showing 10 of 30' items, and you deleted one the normal way, it would still say 'showing 10 of 30 items' when in fact, you are only now showing 9 of 29 items, and the problem then just compounds. The brilliant minds who wrote the dataTables plugin built in ways to get around this.

First thing, we get the position of the clicked item (which all have 'del_' as an id prefix) and store that:
var pos = oTable.fnGetPosition(this);

As well as the id of the cell as we need the corresponding 'person_id' that is contained there:
var del = $(this);

Then we simply call the .ajax() method in jQuery:
$.ajax({
    type: "POST",
    url: "ajax_functions/delete.aspx",
    data: "person_id=" + del.attr('id').replace('del_', ''),
    success: function() {
        $("#report").addClass("success").html("Person Deleted");
        oTable.fnDeleteRow(pos[0]);
    }
});

And that is where the magic happens. It was indicated that this was of type 'POST', the url that it is sent to (we will get to that in a moment), and the POST query that was sent (the delete cell's id, minus the 'del_' prefix). Then, on success, we simply output some feedback to the user into a new div with the id of 'report' - nothing special there. But on the next line, we use the 'oTable' that we stored earlier that has all of the sortable data, and call the included function 'fnDeleteRow' with the position that we collected earlier, and that is what deletes the row. The finished javascript looks like this:

delete.aspx
var oTable;
var aPos;
var editedCell;
$(function() {
    $("td[id^='del_']").addClass("pointer").click(function(event) {
        var pos = oTable.fnGetPosition(this);
        var del = $(this);
        $.ajax({
            type: "POST",
            url: "ajax_functions/delete.aspx",
            data: "person_id=" + del.attr('id').replace('del_', ''),
            success: function() {
                $("#report").addClass("success").html("Person Deleted");
                oTable.fnDeleteRow(pos[0]);
            }
        });
    });
    $(".editable").click(function() {
        aPos = oTable.fnGetPosition(this);
        editedCell = $(this);
    }).editInPlace({
        url: "ajax_functions/update.aspx",
        params: "ajax=yes",
        value_required: true,
        default_text: "click to edit",
        success: function() {
            var data = oTable.fnGetData(aPos[0]);
            data[aPos[1]] = editedCell.html();
        }
    });
    oTable = $("#sort_table").dataTable({
        "aoColumns": [null, null, null, { "bSortable": false}]
    });
});

backend

Lastly is the easy part, to make the backend to "ajax_functions/delete.aspx" to handle the POST.

ajax_functions/delete.aspx.cs
using System;
using System.Linq;

public partial class ajax_functions_delete : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        demoDataContext db = new demoDataContext();
        try
        {
            System.Threading.Thread.Sleep(1000);
            // get the person_id of the person being deleted
            int person_id = Convert.ToInt32(Request.Form[0]);
            db.persons.DeleteOnSubmit(db.persons.First(x => x.person_id == person_id));
            db.SubmitChanges();

            Response.Write("deleted");
        }
        // this will catch all the errors and output them to the edited field
        catch (Exception ex) { Response.Write("Error: " + ex.Message); }
    }
}

This code is pretty simple. Since we specified the POST in the ajax() call in jQuery, we knew the first element of the POST was going to be the person_id, and that is used to delete it from the DB.
*NOTE* that I used a Thread.Sleep() in there which I NEVER recommend using. it is just there to illustrate the ajax loading indicator that was implemented into the masterpage, which is covered next.

ajax loading indicator

I snuck a new div into the masterpage with the id of 'working' and bound it to all jQuery ajax events with the following js:
demo.master
$(function() {
    $("#working").bind("ajaxSend", function() {
        $(this).show();
    }).bind("ajaxComplete", function() {
        $(this).hide();
    });
});

Now any time jQuery is doing ajax, there will be a little notification so the user knows your app is not quitting on them. All other times, that div will be hidden.

Now all that we have left is the ability to add new items.

Inline editing with DB write : 'REAL' AJAX with Asp.Net Series

by naspinski 3/8/2009 2:01:00 AM

Writing to the database inline without a postback, simple and effective

This is a continuation of my 'REAL' AJAX with Asp.Net (not Asp.Net AJAX) series posts for those of us trying to stop relying on Asp.Net 'AJAX'.

This is the first example of ajax I am showing with a write to the DB, and this will show just how easy it really is. I am building on the last post (GridView Sorting/Paging : 'REAL' AJAX with Asp.Net Series) which will explain where/how the data and display is set up.

Also, you will notice that the CSS changed a bit, that is because this plugin has a hover property that resets the backgorund of the td, and that gave a funky interface; now this can be taken care of with some jQuery magic pretty easily, but that is outside the scope of what this is trying to show.

load all of your js

I am still using jQuery and the extension dataTables but for this lesson I am using an additional plugin: inPlace. So now our script loading looks like this (notice I moved the script refs into the masterpage in this lesson):

demo.master

<script type="text/javascript"
  src="js/jquery.js"></script>
<script type="text/javascript"
  src="js/jquery.dataTables.js"></script>
<script type="text/javascript"
  src="js/jquery.inplace.js"></script>


decide what you want to be editable

Now, since we are using our Repeater table from the last example, we just modify if ever-so-slightly to make it editable, to do that, we just add the class 'editable' (or whatever you want it to be) to each td that we want to be editable. In addition to adding a class, we are going to add meaningful ids to each of the cells, each ID will contain:
  • First 4 Letters - What field is being edited
  • Following digits - 'person_id' that is being edited

if you are unsure as to why we are doing this, keep reading, it will become clear.

inline_edit.aspx

<asp:Repeater ID="rpt" runat="server" DataSourceID="lds">
  <HeaderTemplate>
    <table id="sort_table">
      <thead>
        <tr><th>First</th><th>Last</th><th>Age</th></tr>
      </thead>
      <tbody>
  </HeaderTemplate>
  <ItemTemplate>
    <tr>
      <td id="frst<%#Eval("person_id")%>" class="editable">
        <%#Eval("first_name")%>
      
</td>
      <td id="last<%#Eval("person_id")%>" class="editable">
        <%#Eval("last_name")%>
      
</td>
      <td id="age_<%#Eval("person_id")%>" class="editable">
        <%#Eval("age")%>
      
</td>
    </tr>
  </ItemTemplate>
  <FooterTemplate>
    </tbody></table>
  </FooterTemplate>
</asp:Repeater>
<asp:LinqDataSource ID="lds" runat="server"
  ContextTypeName="demoDataContext" TableName="persons" />


make them editable

Now just add in a a little jQuery magic, and the fields will be editable:

inline_edit.aspx
$(function() {
    $("#sort_table").dataTable();
    $(".editable").editInPlace({
        url: "ajax_functions/update.aspx",
        params: "ajax=yes",
        value_required: true,
        default_text: "click to edit"
    });
});

You can look on the documentation for jquery.inplace.js for a further explanation, but what we declared there is that all items that have class 'editable' will now be editable; also, requests will get sent to 'ajax_functions/update.aspx' (which we haven't made yet) and we are requiring a value.

handling the ajax

Ok, so what is happening now, is if you click on a cell you will be able to edit it, and when you click off, or hit enter (you can edit these behaviors if you want), a POST will be sent to 'ajax_functions/update.aspx'. What is sent to the page is the following values:
  • update_value - the value you edited
  • element_id - the element id that was edited
  • original_html - value from before your edit

Now we only care about the first two in this example, but the third value could also be useful given the situation; possibly returning the old value if an error occurs? Also, it may be easier to see why we included both the field and the person_id in the ID of the <td>.

For handling, you could easily have seperate pages that only handle a certain element, i.e. ajax_functions/first_name.aspx for first_name, ajax_functions/last_name.aspx for last_name, etc. but I found that if you did it that way, you ended up writing the same code over and over - so what I did was combine it into a single update handling page. That is why I include 'what' is being updated in the id.

When you make your handling page, it is important that there is no html in it to start with - as the page text itself will be what is stuffed back into the area that you edited. This is the ENTIRE ajax_functions/update.aspx page:

ajax_functions/update.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="update.aspx.cs" Inherits=ajax_functions_update" %>


Anything we want to appear we will just dump out a Response.Write() for. Here is the handling code, I will explain afterwards:

ajax_functions/update.aspx.cs
using System;
using System.Configuration;
using System.Data.SqlClient;
using System.Linq;

public partial class ajax_functions_update : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        demoDataContext db = new demoDataContext();
        // this is unneccessary, but more efficient since there will 
        // be more than 1 db call, this uses the same connection
        SqlConnection con = new SqlConnection(
            ConfigurationManager
            .ConnectionStrings["demoConnectionString"].ConnectionString);

        try
        {
            // get the new value
            string newValue = Request.Form[0]; 
            // figure out what is being edited (remember, its the first 4 chars in the id)
            string what = Request.Form[1].Substring(0, 4);
            // get the person_id of the changing value (the remaining digits)
            int person_id = Convert.ToInt32(
                Request.Form[1].Substring(4, Request.Form[1].Length - 4));

            con.Open();
            // get the record you want to deal with
            person p = db.persons.First(x => x.person_id == person_id);

            // change the value depending on 'what' was sent
            switch (what)
            {
                case "frst": p.first_name = newValue; break;
                case "last": p.last_name = newValue; break;
                case "age_":
                    try { p.age = Convert.ToInt32(newValue); }
                    catch { throw new Exception("not an int"); }
                    break;
                default: throw new Exception("nothing saved");
            }
            db.SubmitChanges();
            con.Close();
            Response.Write(newValue);
        }
        // this will catch all the errors and output them to the edited field
        catch (Exception ex) { Response.Write("Error: " + ex.Message); }
        finally { con.Close(); }
    }
}

First off, the code simply sets the DataContext and opens a SqlConnection (it is explained why in the code). Then, we pull from the POST values from the Request. We pull 3 things from this:
  • person_id - 'person_id' that is being edited
  • what - what field is being edited
  • newValue - the new value for the update

Now is the easy part, get the 'person' to edit, run a switch on 'what' and and update it to 'newValue'. I caught all errors and output them to Response.Write() as that is all the user will be able to see. Also, whatever is written is returned so it integrated seamlessly.  What the user will see is 'Saving...' (unless you change the text or substitute an image) and when it is complete, they will see the new value in place of the old value (which is now written to the DB) as that is what was put in the Response.Write() (if there are no errors, otherwise they'll see the error message).

There you have it! A 'real' ajax inline update with asp.net, and it was as painless as can be! There are all sorts of other things you can do like apply a js function after the update has completed, add in a nice loading graphic instead of the text, etc. - fun to play around with.

Keep in mind with this, if a user (hacker) realizes how it works, they would be able to fabricate a POST to send to your page and update it how they see fit; so be sure you test, protect against that. In one of my apps that uses this. I make sure that the record being edited belongs to the user currently logged in before they can update.

Next will be deleting a row in this table with ajax.



but...

If I have 3 'people', with last names, 'a', 'b', and 'c', and edit 'b' to now be 'z', it will not sort correctly; 'z' will not be the last one in the table, it will still be 'c'. That is because the table data that does the sorting is not held within the table itself, but in an array of 'nodes'.

I left that as a seperate download because this part dives a little deeper into the relation of the sort/edit. What needs to be done is we have to go into the array that holds the nodes, and change the related data along with the represented data in the table.

how do I do that?

This one might require reading a few times, so bear with me. First you need to set the global variables we will be using:
var oTable;
var aPos;
var editedCell;

Then, you will need to store your table in memory:
oTable = $("#sort_table").dataTable();

Now you need to intercept the click in any editable field, and get the position of it in memory; also, we will store the edited cell into a variable as well:
$(".editable").click(function() {
    aPos = oTable.fnGetPosition(this);
    editedCell = $(this);
})

What is now in aPos is a 2-element array [int row_index, int cell_index]; editedCell is the cell that you just clicked. At this point we have all the necessary values to plug the data back in. So next we tack the '.editable()' on to the end of the previous 'click()' call and add a 'success' parameter that will fire on success of the edit. Here is the completed js:

inline_edit.aspx
var oTable;
var aPos;
var editedCell;
$(function() {
    $(".editable").click(function() {
        aPos = oTable.fnGetPosition(this);
        editedCell = $(this);
    }).editInPlace({
        url: "ajax_functions/update.aspx",
        params: "ajax=yes",
        value_required: true,
        default_text: "click to edit",
        success: function() {
            var data = oTable.fnGetData(aPos[0]); 
            data[aPos[1]] = editedCell.html();
        }
    });
    oTable = $("#sort_table").dataTable();
});

Now, the table will re-sort correctly, it's a little extra work, but the added benefit is worth it IMO. Here is the download with the extended functionality (it also has the next demo in it):

Shout it