顯示具有 AJAX 標籤的文章。 顯示所有文章
顯示具有 AJAX 標籤的文章。 顯示所有文章

2010年8月12日 星期四

使用 Ajax 方式取得 UserControl 的內容

同事問到,如何以 Ajax 的方式取得 UserControl 的內容。

問題

由於 UserControl 是無法單獨被客戶端所呼叫(Request),因此我們必須想個辦法將 UserControl 加到一個 Page內。

解決步驟

1 Web Service 回傳 UserControl 的內容

使用 Visual Studio (我使用 2010) 建立一個 Web Application,並加入一個 WebService。程式碼如下

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class WebService1 : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public string GetUserControlHtml(string location)
    {
        var webServiceString = string.Format("Thread Id={0}, Time={1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("HHmmss fffffff"));
        var page = new Page();
        var userControl = (UserControl)page.LoadControl(location);
        userControl.EnableViewState = false;
        var form = new HtmlForm();
        form.Controls.Add(userControl);
        page.Controls.Add(form);
        var writer = new StringWriter();
        HttpContext.Current.Server.Execute(page, writer, false);

        return webServiceString + "<br /> " + RemoveFormTagFromHtml(writer.ToString());
    }


    /// <summary>
    /// 移掉 Form Tag
    /// </summary>
    /// <param name="html"></param>
    /// <returns></returns>
    private static string RemoveFormTagFromHtml(string html)
    {
        return Regex.Replace(html, @"<[/]?(form)[^>]*?>", "", RegexOptions.IgnoreCase);
    }
}

過程大略是:手動建立一個 Page,Page 內加入一個 Form,Form 動態載入指定的 UserControl。使用 Server.Execute 取得 html 內容後,將 <form tag 移除,避免避免原 asp.net 網頁失敗。

2 寫 UserControl

今天的配角是 UserControl。UserControl並不難實作。我簡單的寫了兩個 UserControl,分別 Thread.Sleep 了2秒,3秒。

3 於呼叫網頁使用 Ajax 方式呼叫 ASP.NET Web Service

我再入成兩種方式來呼叫。一個是 ASP.NET AJAX,一個則是 jQuery。

3.1 使用 ASP.NET AJAX 呼叫 Web Service

加入一個 ScriptManager,並加入一個 ScriptReference 參考到剛剛建立的 WebService。利用動態產生的 javascript file (可使用 http://…/WebService1.asmx/jsdebug 取得內容)協助呼叫 ASP.NET Web Service

3.2 使用jQuery 的 .ajax 呼叫 Web Service

使用jQuery 大概是近來 Web 技術的顯學吧。為了方便未來的 ASP.NET MVC的使用,還是乖乖的學一下吧。

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="WebApplication3._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
    <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var datestring = getNow();
            $("#div1").html("at client, " + datestring + "<br/>");
            getData("WebUserControl1.ascx", $("#div1"));

            datestring = getNow();
            $("#div2").html("at client, " + datestring + "<br/>");
            getDataByJquery("WebUserControl2.ascx", $("#div2"));
        });

        function getNow() {
            var today = new Date();
            return today.format("HHmmss ffffff");
        }
        function getData(controlLocation, container) {
            WebApplication3.WebService1.GetUserControlHtml(controlLocation,
                function (result) {
                    $(container).append(result);
                },
                function () { alert(error.get_message()); }
            );
        }

        function getDataByJquery(controlLocation, container) {
            var datestring = getNow();
            $.ajax({
                type: "POST",
                contentType: "application/json; charset=utf-8",
                url: "WebService1.asmx/GetUserControlHtml",
                data: "{'location': '" + controlLocation + "'}",
                dataType: "json",
                success: function (result) {
                    $(container).append(result.d);
                },
                error: function (error) { alert(error.responseText); }
            });
        }
    </script>
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/WebService1.asmx" />
        </Services>
    </asp:ScriptManager>
    <h2>
        Welcome to ASP.NET!
    </h2>
    <div style="border: 2px solid red">
        <h1>
            User Controll 1</h1>
        <div id="div1">
        </div>
    </div>
    <div style="border: 2px solid red">
        <h1>
            User Controll 2</h1>
        <div id="div2">
        </div>
    </div>
    <div id="result">
    </div>
</asp:Content>

4 測試

測試時發現狀況還不錯。雖然同時發出 Request, 約2秒後得到UserControl1 的內容,但 UserControl2卻在約略在 5.5 秒後才得到內容,而非3秒。為什麼?

image

5 Session 造成 lock,因而 Delay

原來是步驟1中,[WebMethod(EnableSession = true)] 這個 true 所造成的。改成 EnableSessioin = false後,網頁測試結果如下

image
這樣就符合我們的期待了。

沒事最好還是不要用 Session。

範例下載

2010年3月30日 星期二

Ajax Enabled WCF Service on IIS 7 always return 404 error

最近 study 了 Ajax enabled WCF Service,覺的著實好用。於是想放到 MOSS 上。而 MOSS 是位於 Windows Server 2008 的 IIS 7 上。一試,就發現 IIS 7 執行 svc 時是正常的,但一執行 *.svc/js 就會回傳 404 (not found) 的錯誤。

然而,在 IIS 6 (windows 2003) 上不會發生這個問題。

花了一整天,都在處理這個 Issue。一度還以為是 moss 的問題。

現在終於找到了原因。在此將完整的步驟記錄下來。

  1. 建立一個 WCF Service. 並且記得要 加上[ServiceContract],[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
  2. 在 config 上,使用 webHttpBinding, 並啟用 <enableWebScript/> 的 endPoint behavior
  3. 部署程式到 Windows 2008 的 IIS 7 上。此以瀏覽器輸入網址 *.svc/js 會回傳 404 錯誤
  4. 這一步就是我漏掉的。打開 IIS 管理員,找到該網站(Site),於 "處理常式對應”(Handler Mappings)上,新增一個指令對應。

要求路徑:*.svc
執行檔:%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll
名稱:svc-ISAPI-2.0

       如下圖。

image

這樣一來就解決了問題。

2009年5月26日 星期二

The control with ID 'endCalendarExtender' requires a ScriptManager on the page. The ScriptManager must appear before any controls that need it.

當 Web Application 由 ASP.NET 2.0升級到 3.5 後,原本一些網頁有使用 ASP.NET AJAX 1.0 的,全部掛了。錯誤訊息如下

The control with ID 'endCalendarExtender' requires a ScriptManager on the page. The ScriptManager must appear before any controls that need it.

為什麼呢?原來 Ajax control toolkit (ACT) 有許多版本,我使用的是搭配 ASP.NET AJAX 1.0 的版本。之後,微軟推出 ASP.NET 3.5 後,Ajax control toolkit 也出了搭配 3.5 的元件。但我仍未升級 ACT, 仍舊使用舊版的ACT。

因此,執行的過程中,舊版的 Ajax control toolkit 要找 ASP.NET AJAX 1.0 中的 ScriptManager,當然找不到。

此時,需要指定 runtime 的 相容性版本。如下。

 <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
請參考這裡

2009年5月4日 星期一

Asp.net 執行匯入進度

顯示匯入或上傳的進度,想必是許多 asp.net 開發人員一定會遇到的問題。
以下是我的解法。

為了讓 progress bar 能跑,當然需要 jQuery.
JQuery Progress Bar 1.1 已經幫我們寫好了,我們只需要使用即可。

再來,為了讓網頁能立刻反應當下的進度,需要使用 ASP.NET Ajax 的 AJAX 之 PageMethod 功能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script.Services;
using System.Web.Services;
using System.Threading;

namespace WebApplication1
{
  public partial class _Default : System.Web.UI.Page
  {
    private static SlowTask slowTask = null;

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    [ScriptMethod]
    [WebMethod]
    public static Progress GetTaskCurrentProgress()
    {
      int ii = Thread.CurrentThread.ManagedThreadId;
      return new Progress
      {
        CurrentCount = slowTask.CurrentCount,
        TotalCount = slowTask.TotalCount,
        Message =       string.Format("{0}/{1} : {2}", slowTask.CurrentCount, slowTask.TotalCount, 
        slowTask.GetAsyncTaskProgress())
      };
    }

    public struct Progress
    {
      public string Message;
      public int TotalCount;
      public int CurrentCount;
    }

    

    [ScriptMethod]
    [WebMethod]
    public static void RunItSync()
    {
      int ii = Thread.CurrentThread.ManagedThreadId;
      slowTask = new SlowTask(1000);
      slowTask.ExecuteAsyncTask();
    }
  }
}
Code behinds
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Script.Services;
using System.Web.Services;
using System.Threading;

namespace WebApplication1
{
  public partial class _Default : System.Web.UI.Page
  {
    private static SlowTask slowTask = null;

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    [ScriptMethod]
    [WebMethod]
    public static Progress GetTaskCurrentProgress()
    {
      int ii = Thread.CurrentThread.ManagedThreadId;
      return new Progress
      {
        CurrentCount = slowTask.CurrentCount,
        TotalCount = slowTask.TotalCount,
        Message =       string.Format("{0}/{1} : {2}", slowTask.CurrentCount, slowTask.TotalCount, 
        slowTask.GetAsyncTaskProgress())
      };
    }

    public struct Progress
    {
      public string Message;
      public int TotalCount;
      public int CurrentCount;
    }

    

    [ScriptMethod]
    [WebMethod]
    public static void RunItSync()
    {
      int ii = Thread.CurrentThread.ManagedThreadId;
      slowTask = new SlowTask(1000);
      slowTask.ExecuteAsyncTask();
    }
  }
}

sample code download here.

2009年3月3日 星期二

ASP.NET: 長時間的工作,取得目前進度到第n筆

在 asp.net 的應用程式中,最怕的一種需求,莫過於大量資料匯入,客戶又要求以 Web form 來呈現,並要求匯入的進度。

也就是說,畫面需要呈現目前進行到第幾筆,總共幾筆。

以下是我的實作。 實作分成兩部份,一個是伺服器端的同步,也就是一般的寫法。另一個則是伺服器端的非同步。

在 asp.net 的應用程式中,最怕的一種需求,莫過於大量資料匯入,客戶又要求以 Web form 來呈現,並要求匯入的進度。

也就是說,畫面需要呈現目前進行到第幾筆,總共幾筆。

以下是我的實作。 實作分成兩部份,一個是伺服器端的同步,也就是一般的寫法。另一個則是伺服器端的非同步。

aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default"
  Async="true" AsyncTimeout="1" %>




  

  



  

Code Behind

using System;
using System.Web.Script.Services;
using System.Web.Services;
using System.Web.UI;

namespace WebApplication1
{
  public partial class _Default : System.Web.UI.Page
  {
    private static SlowTask slowTask = null;

    protected void Page_Load(object sender, EventArgs e)
    {

    }

    [ScriptMethod]
    [WebMethod]
    public static string GetTaskCurrentProgress()
    {
      return string.Format("{0}/{1} : {2}", slowTask.CurrentCount, slowTask.TotalCount, 
        slowTask.GetAsyncTaskProgress());
    }

    public void RunItAsync()
    {
      slowTask = new SlowTask(1000);
      PageAsyncTask asyncTask1 = new PageAsyncTask(slowTask.OnBegin, slowTask.OnEnd, null, "Async", true);
      RegisterAsyncTask(asyncTask1);
    }

    [ScriptMethod]
    [WebMethod]
    public static void RunItSync()
    {
      slowTask = new SlowTask(1000);
      slowTask.ExecuteAsyncTask();
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
      RunItAsync();
      Page.ClientScript.RegisterStartupScript(typeof(string), "ok", "");
    }
  }
}

關於 SlowTask 的程式如下,主要是封裝長時間工作的內容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;

namespace WebApplication1
{
  public class SlowTask
  {
    private readonly int totalCount;
    public int TotalCount
    {
      get
      {
        return totalCount;
      }
    }

    public SlowTask(int count)
    {
      totalCount = count;
    }
    private String taskGrogress;
    private AsyncTaskDelegate dlgt;

    protected delegate void AsyncTaskDelegate();

    public String GetAsyncTaskProgress()
    {
      return taskGrogress;
    }

    public void ExecuteAsyncTask()
    {
      Random r = new Random(5);
      for (int i = 0; i < totalCount; i++)
      {
        Thread.Sleep(10 * r.Next(5));
        CurrentCount++;
      }
    }

    public int CurrentCount { get; set; }

    public IAsyncResult OnBegin(object sender, EventArgs e,
        AsyncCallback cb, object extraData)
    {
      taskGrogress = "AsyncTask started at: " + DateTime.Now + ". ";

      dlgt = new AsyncTaskDelegate(ExecuteAsyncTask);
      IAsyncResult result = dlgt.BeginInvoke(cb, extraData);

      return result;
    }

    public void OnEnd(IAsyncResult ar)
    {
      taskGrogress += "AsyncTask completed at: " + DateTime.Now;
      dlgt.EndInvoke(ar);
    }

    public void OnTimeout(IAsyncResult ar)
    {
      taskGrogress += "AsyncTask failed to complete because it exceeded the AsyncTimeout parameter.";
    }
  }
}

畫面如下

image

 

AJAX 的部份,可參考 AJAX 之 PageMethod
由實驗得知,兩種實作起來,伺服器端同步的AJAX效果比較好,但伺服器端非同步的伺服器資源使用率會較高。

程式碼可由這裡下載

2009年2月4日 星期三

ThickBox

今日無意間發現了一個好用的 jquery extension library, ThickBox

可用來顯示圖片,model dialog 等。
試試該 demo 吧!(在ThickBox網頁的下方)

2008年8月4日 星期一

AJAX 之 PageMethod

之前使用 Asp.Net 時,一直使用 UpdatePanel。這個實在是好用。
但是,我卻誤解了,誤以為使用 Ajax 一定會降低頻寬。

我寫了一個範例 , 一個使用傳統的 UpdatePanel,一個使用 PageMethod。
可以使用 fiddler 之類的工具,查到ajax 所使用的 request 及 response 內容。

大小差別如附圖。

image

使用 UpatePanel 的版本,Request (244 bytes)及 Response(648 bytes) 的內容竟然包山包海,根本無法將傳輸的資料量降低。
image
竟然還有 __VIEWSTATE!? 

原來,asp.net ajax team 當初設計 UpdatePanel 的目的,在於簡化開發的過程。因此對於傳回的 request 內容 ,幾乎等同於未 ajax 的版本。相同地, Response 也是幾乎等同於未 ajax 的版本。只不過不必自己塞到不同的 html control 內了。

 

相反地,使用 PageMethod 是一個相當經清的方法。

  1. 將 ScriptManager 的 EnablePageMethods 屬性設寫 true
  2. 在 code behind 程式加上這一段
        [WebMethod]
        [ScriptMethod]
        public static string GetCurrentTime()
        {
          return DateTime.Now.ToString();
        }
  3. 最後,要寫 javascript,讓 button click 時呼叫

function Button2_onclick() {
  PageMethods.GetCurrentTime(ShowTime);
}

function ShowTime(value)
{
  $get("Label2").innerHTML = value;
}
這樣的好處是,傳輸的資料可直接控制。Requet 0 bytes,Response 27 bytes。
Response 的內容為 {"d":"8/4/2008 3:20:09 PM"}。夠精簡吧。 不過這樣一來,資料的內容要塞到 html control 的功夫可不小啊。範例中的simple case,簡直不可能發生。
想想當要顯示一個客戶資料到一個 DetailView 要花多少的javascript程式碼啊!!!

2008年2月5日 星期二

自訂 AutoCompleteExtender







使用 AutoCompleteExtender時,如果要像Google Search 之 tips 效果,要怎麼做呢?

重點是,預設之AutoCompleteExtender,會將所有的 text,全部設定到 textbox 上。

找AutoCompleteExtender 之 AutoCompleteBehavior.js 可以找到下面這一段。

_setText: function(text) { this._timer.set_enabled(false);
this._currentPrefix = text;
var element = this.get_element();
var control = element.control;
// todo: should check for 'derives from' too and should somehow manually cause TB to raise property changed event
if (control && control.set_text)
{
control.set_text(text);
}
else { element.value = text; }
this._hideCompletionList();
},

可以發現,是有機會 override set_text 的。

google 一下後,找到了 solution

function pageLoad() {
AjaxControlToolkit.AutoCompleteBehavior.prototype._setText = function(item) {
var text = item.split(" ")[0];
this.get_element().value = text;
}
}

此會將找到的字串之第一段空白前的文字,塞到 textbox 中。

2007年11月13日 星期二

一個新的 Rich Text Editor for asp.net ajax

來源自:http://www.codeplex.com/rte 可於 http://rteditor.members.winisp.net/ 上測試其功能。 哇!超強,而且比 FckEditor 感覺簡單親切許多。

2007年7月16日 星期一

Sys.argumentOUtOfRangeException Value must be an integer

當我使用AJAX Toolkit 的 calendarExtender 時,發生了一個JScript錯誤 Sys.argumentOUtOfRangeException Value must be an integer Parameter anme:x Actual value was NaN 完全不知道是怎麼回事。 爬了 google,找到了原因。原來我使用了 frameset,其中的 frameborder 的值,"1", "0", "Yes", "No" 都是合法的值。 但 AJAX ToolKit 卻只認得 "1", "0" 改了之後就OK 了

Share with Facebook