eKnacks.com - Smart solutions sharing

by andrei 29. November 2008 03:27

In the last 2 weeks one of the projects we have been working on is a new idea: www.eknacks.com. We launched it fast because we want to see if people's response to this approach is positive, but we have many cool features coming up in the next version.

 

Try it for a bit and let us know what you think. I hope you'll enjoy it! Here is the link for programming knacks.

 

 

Enjoy programming!

 

How to retrieve the selected Ids from an ASP .Net list with jQuery

by andrei 26. September 2008 21:38

Here is an example of a list in which I can select rows (through checkboxes), and when the lnkGetSelection button is clicked I want to retrieve the selected Ids:

<ul runat="server" id="ulTest">
    <asp:Repeater runat="server" ID="rptTest">
        <ItemTemplate>
            <li>
                <asp:CheckBox ID="ckTest" runat="server" />
                <asp:HiddenField ID="hdnId" runat="server" Value='<%# Eval("Id") %>' />
                <p>
                    <%# Eval("Name") %></p>
            </li>
        </ItemTemplate>
    </asp:Repeater>
</ul>
<a href="#" id="lnkGetSelection" onclick="getIds();">Get selection</a>

 

It's incredibly easy to do with jQuery:

function getIds()
{
    var checkedCombos = $('input:checkbox[id*=ckTest]:checked'); // we get an array with the checked checkboxes
    var selectedIdControls = checkedCombos.next(); // we get an array with the correspondent hidden fields
    var selectedIds = new Array(selectedIdControls.length); // we create a new array for the Ids
    selectedIdControls.each(function(i){selectedIds[i] = selectedIdControls[i].value;}); 
    // we iterate over the hidden fields, and put each id value in the list
    
    return selectedIds;
}

 

Technorati Tags: ,,

 

 

Enjoy programming!

 

Draggable window, cookies, AJAX callbacks, user controls and JSON - combination powered by jQuery, ASP .Net and JSON .Net

by andrei 26. September 2008 08:53

The code for this example can be found here.

 

Here is an interesting functionality I had to implement the other day:

  • a floating / draggable window
  • loads its content (a web control) from a web service through an AJAX callback, based on information that is sent from the client
  • keeps the last location in cookies, when you come back the window pops up where you left it the last time

Here is how it will look like:

ScreenHunter_03 Sep. 26 22.00

Let's start.

 

First of all, I am working with a Web Application project (which is included in Visual Studio 2008, but needs to be installed manually in Visual Studio 2005). With a simple Website project you might encounter problems with Web Services (for example, I was unable to access the classes for my user controls from inside the web service class).

ScreenHunter_02 Sep. 26 20.17

 

I am adding the Javascript files that I need:

ScreenHunter_04 Sep. 26 20.20

and putting them into the page:

<head runat="server">
    <title>Untitled Page</title>

    <script src="JS/jquery-1.2.6.pack.js" type="text/javascript"></script>

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

    <script src="JS/JSON.js" type="text/javascript"></script>

    <script src="JS/jquery.cookie.pack.js" type="text/javascript"></script>

</head>

You can get these files from the following locations:

 

Also, a reference to JSON .Net:

ScreenHunter_07 Sep. 26 20.44

which you can get from here.

 

I am also adding a div for the draggable window (with a handle area containing a title and a close button, and a content area where we will load the data from the server):

<div>
    <div id="divExample">
        <div id="divExample_handle">
            <span id="divExample_title">This is a draggable window</span> <span id="divExample_close">
                <a href="javascript:clearExamplePosition();" id="lnkExampleClose">[ x ]</a></span>
        </div>
        <div id="divExample_content">
        </div>
    </div>
</div>

and a link to show / hide the draggable window:

<a href="#" id="lnkShowDraggableWindow">Show the draggable window</a>

 

To prepare the stage, let's add

  • a JSON serializer helper:
using Newtonsoft.Json;

namespace DraggableWindow.CodeFiles
{
  public delegate T Action<T>(T value);

  public class JsonConverter
  {
    public static string Serialize(object entity)
    {
      return JavaScriptConvert.SerializeObject(entity);
    }
  }
}
  • a base response class:
public class BaseResponse
{
  public string htmlValue { get; set; }
  public IList<string> errorMessages { get; set; }
}
  • a base service (which basically knows how to serialize a BaseResponse) - I am using an idea and some code from Rick Strahl's West Wind Ajax Toolkit for ASP.NET
  • a user control to fill the content of the draggable window with (DraggableWindowContent.ascx)
  • and finally the web service which will handle the client calls:
namespace DraggableWindow.Services
{
  [ToolboxItem(false)]
  [ScriptService]
  public class DraggableWindowService : BaseService
  {
    [WebMethod(EnableSession = true)]
    public string GetDraggableWindowContent(string text)
    {
      return Response(delegate(Page p)
      {
        DraggableWindowContent ctrl =
          p.LoadControl("~/DraggableWindowContent.ascx") as
          DraggableWindowContent;

        if (ctrl != null) ctrl.Initialise(text);
        return ctrl;
      });
    }
  }
}

Back to the client-side, we need to initialize the draggable window:

if($ != 'undefined'){
    $('document').ready(function(){                    
        $("#divExample").easydrag();
        $("#divExample").setHandler('divExample_handle');
        $("#divExample").ondrop
            (
                function(e, element)
                    { 
                        $.cookie('examplePosLeft', element.offsetLeft); 
                        $.cookie('examplePosTop', element.offsetTop); 
                    }
            );
    
        $('#lnkExampleClose').click(function() 
            { 
                    $("#divExample").hide(); 
                    $.cookie('showExample', 0)
            }); 
            
        $('#lnkShowExample').click(function() 
            { 
                    $.cookie('showExample', 1);
                    $.cookie('examplePosLeft', 100); 
                    $.cookie('examplePosTop', 100); 
                    loadData();
            });
            
        loadData();
    });
}

We store the user preference of showing the window or not in the showExample cookie. The position cookies (examplePosLeft and examplePosTop) will be set each time the draggable window is dropped.

 

I also need to write the loadData method:

function loadData()
{
    var show = $.cookie('showExample');
    if (parseInt(show) == 0)
    {
        $("#divExample").hide(); 
        return;
    }
    
    $("#divExample").show(); 
    var left = $.cookie('examplePosLeft');
    var top = $.cookie('examplePosTop');
    if (left != undefined && left.length != 0 && top != undefined && top.length != 0)
    {
            $("#divExample").css('top', parseInt(top));
            $("#divExample").css('left', parseInt(left));
    }
    
    var text = 'Client Side Text';
    
    var dataForPost = {
        someText : text
        };

    $.ajax({
            type: "POST",
            url : "/Services/DraggableWindowService.asmx/GetDraggableWindowContent",
            contentType: "application/json; charset=utf-8",
            dataType : "json",
            data : JSON.stringify(dataForPost),
            
            success : function(res){
                var objReturned = eval("("+res.d+")");
                $("#divExample_content")[0].innerHTML = objReturned.value;
            },
        
            error: function(res){
                alert(res.responseText);
            }
                
    });
                    
    return false;
}

which checks if the user wants to see the draggable window or not. If yes, it puts it in the last known location and loads the content from the server.

 

That's it. You can get the code from here.

 

 

 

Enjoy programming!

 

DTOs are for JSON serialization (and any serialization) also, not only for display

by andrei 23. September 2008 18:00

Data transfer objects are very useful for preparing domain information for a presentation layer, and this usually means the UI. But they are at least as useful when it comes to transferring information between different environments, especially when serialization is involved.

 

Here is an example: I needed to serialize a hierarchy of value objects in JSON, so I can put them in a hidden field and use them in an algorithm on the client-side.

These are the value objects:

 

  • CombinationOfProfiles:
public interface ICombinationOfProfiles
{
  IList<ProfileInCombinationWithOtherProfiles> ProfilesInCombinationWithOtherProfiles { get; }
  void AddProfileInCombinationWithOtherProfiles(ProfileInCombinationWithOtherProfiles combination);
  bool IsEmpty { get; }
}

public class CombinationOfProfiles : ICombinationOfProfiles
{
  private IList<ProfileInCombinationWithOtherProfiles> _profilesInCombinationWithOtherProfiles = new List<ProfileInCombinationWithOtherProfiles>();

  public IList<ProfileInCombinationWithOtherProfiles> ProfilesInCombinationWithOtherProfiles
  {
    get { return new ReadOnlyCollection<ProfileInCombinationWithOtherProfiles>(_profilesInCombinationWithOtherProfiles); }
    set { _profilesInCombinationWithOtherProfiles = value; }
  }

  public void AddProfileInCombinationWithOtherProfiles(ProfileInCombinationWithOtherProfiles combination)
  {
    _profilesInCombinationWithOtherProfiles.Add(combination);
  }

  public bool IsEmpty
  {
    get { return _profilesInCombinationWithOtherProfiles.Count == 0; }
  }
}
  • ProfileInCombinationWithOtherProfiles:
public class ProfileInCombinationWithOtherProfiles
{
  private readonly Product _profile;
  private readonly ProductCombinationForProfile _combinationOfProductsForProfileWhichIsValidInCombination;

  public ProfileInCombinationWithOtherProfiles(Product profile, ProductCombinationForProfile combinationOfProductsValidInCombinationForProfile)
  {
    _profile = profile;
    _combinationOfProductsForProfileWhichIsValidInCombination = combinationOfProductsValidInCombinationForProfile;
  }

  public ProductCombinationForProfile ValidCombinationOfProducts
  {
    get { return _combinationOfProductsForProfileWhichIsValidInCombination; }
  }

  public Product Profile
  {
    get { return _profile; }
  }
}

  • ProductCombinationForProfile:
public class ProductCombinationForProfile
{
  private readonly IList<Product> _products = new List<Product>();

  public IList<Product> Products
  {
    get { return new ReadOnlyCollection<Product>(_products); }
  }

  public void AddProductInCombination(Product p)
  {
    _products.Add(p);
  }
}

 

As you can see, they encapsulate a list of products (profiles) and for each product a list (valid combination) of products.

 

I initially tried to serialize the list of CombinationOfProfiles:

hdnProfileAssistanceData.Value =
    JsonConverter<CombinationOfProfiles>.Serialize(dto.PossibleCombinations);

(dto.PossibleCombinations is an IList<CombinationOfProfiles>)

This got me an error, because the hierarchy contains Product entities (which contain subentities and a whole business structure), and these are not serializable.

 

So I have 2 options: either make the Product entity serializable, or find another way to transmit the information I need to the client.

Making the Product entity serializable is a bad idea, because it would mean I am doing changes in my domain classes because of an infrastructure issue. This will impose initial restrictions, and in time it will become a problem. The domain must remain independent and uninterested of the other layers.

I realized I only needed the Ids of the products and nothing else, because on the client-side I just wanted to check al valid combinations based on this hierarchy. And this made things very simple.

 

I created a set of DTOs for JSON serialization:

[Serializable]
public class CombinationOfProfilesJSONDTO
{
    public CombinationOfProfilesJSONDTO()
    {
        
    }

    public CombinationOfProfilesJSONDTO(IEnumerable<ProfileInCombinationWithOtherProfiles> profiles)
    {
        Profiles = new List<ProfileInCombinationWithOtherProfilesJSONDTO>();
        foreach(ProfileInCombinationWithOtherProfiles profile in profiles)
        {
            Profiles.Add(new ProfileInCombinationWithOtherProfilesJSONDTO(profile));
        }
    }

    public IList<ProfileInCombinationWithOtherProfilesJSONDTO> Profiles { get; set; }
}

[Serializable]
public class ProfileInCombinationWithOtherProfilesJSONDTO
{
    public ProfileInCombinationWithOtherProfilesJSONDTO(ProfileInCombinationWithOtherProfiles profile)
    {
        Product = profile.Profile.Id;
        ProductCombinationForProfile = new ProductCombinationForProfileJSONDTO(profile.ValidCombinationOfProducts);
    }

    public int Product { get; set; }
    public ProductCombinationForProfileJSONDTO ProductCombinationForProfile { get; set; }
}

[Serializable]
public class ProductCombinationForProfileJSONDTO
{
    public ProductCombinationForProfileJSONDTO(ProductCombinationForProfile products)
    {
        Products = new List<int>();
        foreach(Product p in products.Products)
            Products.Add(p.Id);
    }

    public IList<int> Products { get; set; }
}

 

and a mapper between a CombinationOfProfiles domain value object and an CombinationOfProfilesJSONDTO:

public class CombinationOfProfilesJSONMapper : IMapper<CombinationOfProfiles, CombinationOfProfilesJSONDTO>
{
    public void CopyFrom(CombinationOfProfiles source, CombinationOfProfilesJSONDTO destination)
    {
        throw new System.NotImplementedException();
    }

    public CombinationOfProfilesJSONDTO MapFrom(CombinationOfProfiles input)
    {
        return new CombinationOfProfilesJSONDTO(input.ProfilesInCombinationWithOtherProfiles);
    }
}

Here is how my call looks now, which generates the entire hierarchy of DTOs automatically:

hdnProfileAssistanceData.Value =
    JsonConverter<CombinationOfProfilesJSONDTO>.Serialize(
        dto.PossibleCombinations.MapAllUsing(new CombinationOfProfilesJSONMapper()));

 

 

Enjoy programming!

 

Single file upload - choosing a file naming strategy

by andrei 23. August 2008 22:37

When you implement file upload for one file (for example a logo which can be replaced through repeated uploads), you have 3 options for naming the file:
  1. you keep the real filename each time (and maybe increment a number at the end if that filename already exists)
  2. you generate a unique filename each time
  3. you use the same filename each time another file for that logo is uploaded (the fixed filename can be hardcoded in the application or stored in the database)
With 1 and 2 there is a little more work involved, you have to store the current filename in the database or in a configuration file, and there is also the downside of unused files that remain each time a new version is uploaded.
Solution 3 seems more efficient: you don't have to store a new filename each time, and no unused files remain after repeated uploads.

But you should use 1 or 2 instead of 3, anyway. If you use solution 3 the file might be in use when the user is trying to upload a new version, and the action will result in a timeout or an error.

 

 

Enjoy programming!

 

AJAX callbacks with ASP .Net, jQuery, JSON and Web Services

by andrei 8. August 2008 22:31

I will describe how we can load data from the server through an AJAX callback and send it back to the server once editing is done.

The technologies involved are:
- ASP .Net
- jQuery
- JSON, with this utility for client-side Javascript processing and JSON .Net for server-side
- ASP .Net Web services

First we need to add a reference to the Newtonsoft.Json dll in our project.
The page that we are using must contain references to the javascript libraries and our custom functions (which are in Content.js):
<script src="JS/JSON.js" type="text/javascript"></script>
<script src="JS/jquery-1.2.5.js" type="text/javascript"></script>
<script src="JS/Content.js" type="text/javascript"></script>

Also, we are adding a div where error messages should appear in case there is a problem, a dropdown which selects the content to be edited based on its title, an editor which edits the current content (I was using a Telerik RadEditor in this case, but it could be anything including a simple textbox or a multiline textbox), and a button for saving the changes:
<div class="text" id="dcmErrorMessages" style="display:none; border-color:Red;">
</div>
<asp:DropDownList ID="ddlContent" runat="server" onchange="EditContent(this.value);">
</asp:DropDownList>
<telerik:RadEditor ID="edtContent" runat="server" Width="100%" Height="600px" EnableAjaxSkinRendering="false"
EnableResize
="false" EnableTheming="false" EnableViewState="false">
</telerik:RadEditor>
<asp:Image ID="btnSave" runat="server" CssClass="input-button" ImageUrl="~/images/btn-submit.gif"
AlternateText
="Save Changes" onclick="SaveContent()" />

The page is initialised like this:

private IContentRepository _contentRepository = IoC.Resolve<IContentRepository> ( );

 

protected void Page_Load ( object sender, EventArgs e )

{

    if (!Page.IsPostBack)

        Bind ( );

}

 

private void Bind ( )

{

    IList<Content> list = _contentRepository.GetAll ( ).ToList<Content> ( );

    ddlContent.DataSource = list;

    ddlContent.DataTextField = "Title";

    ddlContent.DataValueField = "ContentId";

    ddlContent.DataBind ( );

 

    edtContent.Content = list[0].Value;

}


IContentRepository provides services for content entities loading and saving. The IoC class offers an interface to the dependency injection framework. These represent good practices, but they are not essential for our example and they can be replaced with simpler solutions.

When we change the dropdown selection, the EditContent method from Content.js is called:

function EditContent
(id)
           
{
            var dataForPost
= {
                ContentId
: id
            }
;

           
$.ajax({
                type:
"POST",
               
url : "/Services/ContentService.asmx/GetContentValue",
               
contentType: "application/json; charset=utf-8",
               
dataType : "json",
               
data : JSON.stringify(dataForPost),
               
success : function(res){
                    var objReturned
= eval("("+res.d+")");
                   
if(objReturned.errorMessages.length === 0){
                        SetContent
(objReturned.value);
                   
}else{
                        displayMessages
('dcmErrorMessages',objReturned.errorMessages);
                        }
                    }
,
               
error: function(res){
                    displayMessage
('dcmErrorMessages',res.responseText);
                        }
                        }
);
               
return false;
            }


This calls the GetContentValue method of the ContentService.asmx web service, with the id of the content that needs to be edited. The method loads the content, serialises it into a JSON object, and sends the object back to the client:

[ScriptService]

public class ContentService : System.Web.Services.WebService

{

  [WebMethod]

  public string GetContentValue(int contentId)

  {

    IContentRepository _contentRepository = IoC.Resolve<IContentRepository>();

    Content c = _contentRepository.GetById(contentId);

 

    var objectToSend = new { value = (c.Value.IsEmpty() ? "" : c.Value), errorMessages =

        ValidationMessagesRepository.GetCurrentMessages() };

 

    return JsonConverter.Serialize(objectToSend);

  }

}


ValidationMessagesRepository is a general application mechanism we use for storing error messages when they appear, but again any mechanism for obtaining them should be fine.
The Serialise method of the JsonConverter class does a simple call to the JSON .Net serialisation method:

public static string Serialize (object entity)

{

    return JavaScriptConvert.SerializeObject(entity);

}


BaseResponse looks something like this:

public class BaseResponse

{

  public string htmlValue { get; set; }

  public IList<string> errorMessages { get; set; }

}


After the client receives the response, if no error messages appeared it sets the new content into the editor through SetContent(objReturned.value):

function SetContent
(value)
{
    var editor
= $find("<%=edtContent.ClientID%>");
    editor.set_html(value);
}


If error messages have appeared, it displays them with a simple displayMessages function.
function displayMessages(divId,messages){
if(!divId) return ;
if(!messages) return ;
hideMessages(divId);
divId = "#" + divId;
if(!$(divId)) return ;
var ulList = '<ul>';
for(var i = 0 ; i < messages.length; i++){
ulList += '<li>' + messages[i] +'</li>';
}
ulList += '</ul>';
$(divId).prepend(ulList);
$(divId).show();
var divOffset = $(divId).offset().top;
$('html, body').animate({scrollTop:divOffset - 20}, 'fast'); 
}

Considering that no errors have appeared, we now have the content in our editor. When we finish editing and click on the submit button, the SaveContent function is called:

var ddl = "select[id*=ddlContent]";
function SaveContent
()
           
{
           
var id = $(ddl)[0].value;
           
var value = GetContent();
           
var dataForPost = {
                ContentId
: id,
               
value: value
            }
;
           
$.ajax({
                type:
"POST",
               
url : "/Services/ContentService.asmx/SaveContent",
               
contentType: "application/json; charset=utf-8",
               
dataType : "json",
               
data : JSON.stringify(dataForPost),
               
success : function(res){
                    var objReturned
= eval("("+res.d+")");
                   
if(objReturned.errorMessages.length === 0){
                        displayMessage
('dcmErrorMessages',"Content saved successfully.");
                   
}
                   
else{
                        displayMessages
('dcmErrorMessages',objReturned.errorMessages);
                        }
                        }
,
               
error: function(res){
                        displayMessage
('dcmErrorMessages',res.responseText);
                       
}
                    }
);
   
return false;
}


function GetContent
()
{
    var editor
= $find("<%=edtContent.ClientID%>");
   
return editor.get_html();
}


The method gets the content from the editor, prepares an object with the content id and the new content text, and calls the SaveContent method of the ContentService.asmx web service:

[WebMethod]

public string SaveContent(int contentId, string value)

{

  IContentRepository _contentRepository = IoC.Resolve<IContentRepository>();

  _contentRepository.SaveContent(contentId, value);

 

  var objectToSend = new { value = "", errorMessages = ValidationMessagesRepository.GetCurrentMessages() };

 

  return JsonConverter<BaseResponse>.Serialize(objectToSend);

}


If everything went well a success message will appear in the client browser, otherwise the error message will be shown.

The current example involves a few steps, but with some work be transformed into a small framework which would save us from a lot of the repetitive work.

 

 

Enjoy programming!

 

jQuery and ASP .Net Ids, part 2

by andrei 5. August 2008 22:30
I posted a while ago about a very simple way to retrieve ASP .Net controls using jQuery. Here are a few methods based on that idea:

Find the control by type and name:
function GetControlByType(name, type)
{
    return $(type + "[id*=" + name + "]")[0];
}
Example: ddl = GetControlByType("ddlCountry", "select")

Find the input by name
function GetControl(name)
{
    return GetControlByType(name, 'input');
}
Example: txt = GetControl("txtState"))

Find the input inside a div, by name
function GetControl(name, divLimit){
    return GetControlByTypeAndLimit(name,'input',divLimit);
}
Example: firstName = GetControl('txtFirstName', 'divPersonDetails');

Retrieve the control value, or a default value if the control is not found
function GetControlValue(name, type, defaultValue)
{
    if(!defaultValue) defaultValue = "";
    if(!type) type = "input";
     
    var control = GetControlByType(name, type);
    return control ? control.value : defaultValue;
}
Example:
val = GetControlValue("txtName");
val = GetControlValue("ddlOptions","select","0")

Find the control inside a particular div by type and name
function GetControlByTypeAndLimit(name, type, divLimit)
{
    if(divLimit)
        return $("#" + divLimit + " " + type + "[id*=" + name + "]")[0];
    else
    return GetControlByType(name, type);
}

 

 

Enjoy programming!

 

Client Side Validation VS Server Side Validation

by andrei 24. July 2008 22:26

The possible combinations for validation locations in web applications are:

  1. client-side validation for what can be validated there, and server-side for the rest

    You should never do this, period. All validation must be implemented on the server-side, and if possible some of it can also be ported on the client-side.

  2. client-side validation for what can be validated there, and server-side validation for everything

    This method is a good one, with one drawback: you will duplicate at least some of the validation logic in both server-side code and client-side code. Since validations ported on the client-side are ussualy simple (for example validation for mandatory fields, numeric fields, etc), these won't require frequent changes and it is safe to leave them like this (at least for a while). But when the validation rule needs to be modified both places need to be considered, and this can lead to problems.

  3. no client-side validation, server-side validation for everything

    This method is also a good one, but it means that there will be a postback every time something is validated. With many ocasions the drawback is less important than the stability and quality of the code (which is not duplicated like in point 2).

  4. server-side validation for everything, ajax callbacks from client-side to the server for validation decisions

    Given the flexibility that Ajax offers, this might be the best validation solution. Here are the advantajes:
    • all the validation exists on the server
    • no validation is duplicated
    • the UI is still very responsive because the data transfer is really inexpensive (a few KB through JSON objects is all that is needed), and you don't have to do any validation postbacks unless you want to

A good approach for writing the validation logic for your application is to start by implementing it on the server, and especially in the business layer (for data that is persisted in the database). This is the most important part, the others are only variations or usability improvements.

Also, make sure you have an efficient validation mechanism in place. Here is a very good article on this subject: http://www.jpboodhoo.com/blog/ValidationInTheDomainLayerTakeOne.aspx.

 

 

Enjoy programming!

 

ASP .Net Client Side Validation With jQuery

by andrei 21. July 2008 22:24

 

I was looking for a nice jQuery client-side validation solution the other day, and after some investigation I discovered that this is the tool of choice in the community: jQuery Validation plugin.

The truth is that it is flexible, has a very impressive set of features, many examples and a good documentation. One very cool think about it is that it can work with callbacks for server-side validation, which opens a new horizon of options.

Here are a few tips for installing this validation plugin (in an ASP .Net website):

  • make sure you are using an appropriate version of jQuery.js and jquery.validate.js; for some reason, it did not work until I got these files (I think the problem was with my previous jquery.validate.js):

    http://code.jquery.com/jquery-latest.js

    http://dev.jquery.com/view/trunk/plugins/validate/jquery.validate.js

  • if possible, set AutoEventWireup="false" on pages where you are using validation; it seems to work better this way
  • because ASP .Net does the renaming trick on all its controls, you need to use this method when adding rules so you can specify names dynamically

This is a simple as possible example that I came up with to test how it works:

<%@ Page Language="C#" AutoEventWireup="false" CodeBehind="Test.aspx.cs" Inherits="iTF.Web.Test" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<script src="../JS/jquery.js" type="text/javascript"></script>
<script src="../JS/jquery.validate.js" type="text/javascript"></script>
<style type="text/css">
div.container
{
display: none;
}
.container label.error
{
display: inline;
}
</style>
<script language="javascript" type="text/javascript">
$("document").ready(function(){
var container = $('div.container');
var validator = $('#form1').validate(
{
errorContainer: container,
errorLabelContainer: $("ol", container),
wrapper: 'li',
meta: "validate"
});
$("input[id*=txt1]").rules("add", { required: true });
$("input[id*=txt2]").rules("add", { required: true });
});
</script>
</head>
<body>
<form id="form1" runat="server">
<div class="container">
<ol>
<li>
<label for="<%= txt1.ClientID %>" class="error">
Please enter txt1</label></li>
<li>
<label for="<%= txt2.ClientID %>" class="error">
Please enter txt2</label></li>
</ol>
</div>
<div>
<asp:TextBox runat="server" ID="txt1"></asp:TextBox><br />
<asp:TextBox runat="server" ID="txt2"></asp:TextBox><br />
<asp:Button runat="server" Text="Submit" />
</div>
</form>
</body>
</html>

There is also the jQuery.YAV framework, which looks quite well and is worth a try.

 

 

Enjoy programming!

 

Simple MVP (Model-View-Presenter) example

by andrei 19. July 2008 22:23

ASP .Net MVC gets all the attention these days (and it diserves it), but UI patterns can also be used with ASP .Net. MVP (Model-View-Presenter) is one of them.

My task was to create a set of pages that were presenting dynamic text loaded from the database. The entire procedure was exactly the same for all pages, except each one needed its particular row from the database table. The table entries were static (no delete or add), and only the dynamic text for each one could be configured.

Which makes clear at least a few things regarding the design of this solution:

  • we can create a DynamicContent table, which will have a primary key without autoincrement (the Id's will be set, so we can map them in the application)
  • there will be a repository which loads each text based on its Id, like this:

     

    public class DynamicContentRepository : BaseRepository<DynamicContent>, IDynamicContentRepository

      {

        public DynamicContent GetById(int id)

        {

          return (from ent in _context.DynamicContents

                  where ent.DynamicContentId == id

                  select ent).SingleOrDefault<DynamicContent>();

        }

      }

     

    public class DynamicContentPresenter

      {

        private readonly IDynamicContentService _dynamicContentService;

        private readonly IDynamicContentView _view;

     

        public DynamicContentPresenter ( IDynamicContentView view, IDynamicContentService dynamicContentService )

        {

          _dynamicContentService = dynamicContentService;

          _view = view;

     

          Initialize ( view );

        }

     

        private void Initialize ( IDynamicContentView view )

        {

          view.ShowContent += view_ShowContent;

        }

     

        void view_ShowContent ( object sender, EventArgs e )

        {

          DynamicContent c = _dynamicContentService.GetById(_view.DynamicContentId);

          _view.ContentHolder.Text = c.Value;

        }

      }

    and here is the interface for the view:

    public interface IDynamicContentView

      {

        Literal ContentHolder { get; }

        int DynamicContentId { get; }

     

        event EventHandler ShowContent;

      }

    Each page who wants to present dynamic content only needs to implement IDynamicContentView, and the presenter will take care of bringing the information for it. Here is an example:

    public partial class PageWhoNeedsDynamicText1 : BasePage, IDynamicContentView

      {

        private DynamicContentPresenter presenter;

     

        protected void Page_Load ( object sender, EventArgs e )

        {

          if (ShowContent != null)

            ShowContent ( sender, e );

        }

     

        protected override void OnInit ( EventArgs e )

        {

          base.OnInit ( e );

          presenter = new DynamicContentPresenter ( this, IoC.Resolve<IDynamicContentService> ( ) );

        }

     

        public Literal ContentHolder

        {

          get { return litDynamicContent; }

        }

     

        public int DynamicContentId

        {

          get { return (int)enumDynamicContent.DynamicText1; }

        }

     

        public event EventHandler ShowContent;

      }

    Effects:

    • each page contains only UI code, anything else is handled by the presenter
    • if we want to change something in the logic of the presentation, we only have to do the change in the presenter and it will be applied to all its views
    • classes remain small: if a page needs to present 3 separate types of information, instead of putting all that logic in its code-behind we can create one presenter for each type of information and the page can become a View for each of them (it will implement 3 view interfaces); this way, the page will only take care of its controls, and each of the presenters will encapsulate the logic of retrieving and preparing the data
    • if we ever need to show the same information in a different medium (for example in emails), the class which will prepare the email will only have to implement the View interface; of course, it would use the data in a different way to insert it into the email, but it would retrieve it in the exact same way
    • if the data that is arriving from the business layer (which represents the model and the business) is not exactly what the UI wants, it needs to be processed. The presenter is a good place for transforming the business layer data into data transfer objects (DTOs), which can be constructed based on the needs of the UI. If we would make this transformation in the business layer the layer would become contaminated with UI logic, and if we would make this transformation in the code-behind we would add unnecesary logic into the UI

     

    Conclusion

    You can use this pattern each time a code-behind file becomes too complex (it would be best to apply the pattern before it does), or when you need to use the same presentational process (retrieving and preparing of the information) in more than one page.

 

 

Enjoy programming!

 

Powered by BlogEngine.NET 1.4.5.0