Friday, October 31, 2008

Custom AutoComplete 1: Mutiple rows message for each Item

AjaxControlToolkit AutoComplete will pop up the result list according to what you typed. Each items of result list contains one-line text. When we type like "Vin", it will return "Vince", "Vincent", "Vint" in the result list. But if we want to append some detail to an additional line within the an item, how we can get that? For example, When we type "Vin", we hope it will result the related name and age in an additional line. When we select the item, it will return just name as the value. (See the thumbnail)


<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="server" ID="ScriptManager1" />
<asp:TextBox runat="server" ID="myTextBox" Width="300" autocomplete="off" />
<ajaxToolkit:AutoCompleteExtender
runat="server" OnClientPopulated="dd" OnClientItemSelected="itemSelected"
BehaviorID="AutoCompleteEx"
ID="autoComplete1"
TargetControlID="myTextBox"
ServicePath="AutoComplete.asmx"
ServiceMethod="GetCompletionList"
MinimumPrefixLength="2"
CompletionInterval="1000"
EnableCaching="true"
CompletionSetCount="8"
CompletionListCssClass="autocomplete_completionListElement"
CompletionListItemCssClass="autocomplete_listItem"
CompletionListHighlightedItemCssClass="autocomplete_highlightedListItem"
DelimiterCharacters=";, :">
</ajaxToolkit:AutoCompleteExtender>


</form>
</body>
<script type="text/javascript">
function itemSelected(ev)
{
var index=$find("AutoCompleteEx")._selectIndex;
var dd=$find("AutoCompleteEx").get_completionList().childNodes[index]._value;
$find("AutoCompleteEx").get_element().value=dd;
}
function dd()
{
var comletionList=$find("AutoCompleteEx").get_completionList();
for(i=0;i<comletionList.childNodes.length;i++)
{

var _value=comletionList.childNodes[i]._value;
comletionList.childNodes[i]._value=_value.substring(_value.lastIndexOf('|')+1);// parse id to _value

_value=_value.substring(0,_value.lastIndexOf('|'));
comletionList.childNodes[i].innerHTML=_value.replace('|','<br/>');
}

}
</script>


WebMethod in AutoComplete.asmx:

[WebMethod]
public string[] GetCompletionList(string prefixText, int count)
{
    System.Threading.Thread.Sleep(2000);
    if (count == 0)
    {
        count = 10;
    }

    if (prefixText.Equals("xyz"))
    {
        return new string[0];
    }

    Random random = new Random();
    List<string> items = new List<string>(count);


    for (int i = 0; i < count; i++)
    {
        char c1 = (char)random.Next(65, 90);
        char c2 = (char)random.Next(97, 122);
        char c3 = (char)random.Next(97, 122);
        int id = i;
        int age = random.Next(18, 70);
        items.Add(prefixText + c1 + c2 + c3 +"|"+age.ToString()+"|"+id.ToString());
    }

    return items.ToArray();
}


ID, Name and Age will be returned from web method to the client. JavaScript will change the format of result and set the value as ID. When you select the item, the value will be as the text of TextBox.

In this way, we can even append an image in font of each item. I will intoduce about "AutoComplete with Image" next time.

Monday, October 27, 2008

How to display Progress bar when the first time page load

 

Last time, I introduced the ModalUpdateProgress, which is designed for postback. Then how to show "page loading" when the first time page load,  we need to find another way to achieve it.

We hope the page will not flash too much time. It will be better if the page shows "page loading" with percent progress untill the page loaded and rendered.

 

    protected void Page_Load(object sender, EventArgs e)
    {
        showPageLoad();
        //do somthing


    }
    private void showPageLoad()
    {
        int i=0;
        Response.Write("<div id='divTitle'>Loading(<span id='message'>0%</span>)</div>");
        Response.Flush();
        for(i=5;i<=100;i=i+5)
        {
            System.Threading.Thread.Sleep(200);// any codes can be typed here
            outputflash(i.ToString() + "%");
            Response.Flush();
        }
    }
    private void removePageLoad()
    {
        ScriptManager.RegisterStartupScript(Page, this.GetType(), "deleteLoading", "var d = document.getElementById('divTitle');d.parentNode.removeChild(d);", true);

    }
    private void outputflash(string value)
    {       
      Response.Write("<script type='text/javascript'>document.getElementById('message').innerHTML='" + value + "';</script>");

    }

 

How to make ModalUpdate Progress Bar on page?

1. Generate the ModalUpdate Progress bar when a button is triggered.

UpdateProgress can help us to build "page loading" progress on the page. But some times we want the page loading is popped up above the page and disable the background of page. Check the below image. In this way, we can use AjaxControlToolkit ModalPopup to achieve it.

1

Try the below sample code to achieve Modalupdate progress.

Firstly, you need download & install AjaxControlToolKits : http://www.asp.net/AJAX/AjaxControlToolkit/Samples/Default.aspx

<%@ Register
Assembly="AjaxControlToolkit"
Namespace="AjaxControlToolkit"
TagPrefix="ajaxToolkit" %>

<head runat="server">
<title>Untitled Page</title>
<style>
.modalBackground {
background-color:Gray;
filter:alpha(opacity=70);
opacity:0.7;

}

.modalPopup {
background-color:#ffffdd;
border-width:3px;
border-style:solid;
border-color:Gray;
padding:3px;
width:250px;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="Server" ID="ScriptManager1" />
<script type="text/javascript">
function showPopup() {
var modalPopupBehavior = $find('programmaticModalPopupBehavior');
modalPopupBehavior.show();
}
function hidepopup()
{
var modalPopupBehavior = $find('programmaticModalPopupBehavior');
modalPopupBehavior.hide();
}
</script>

<asp:Button ID="loginButton" runat="server" Text="login" OnClientClick="showPopup()"
onclick="loginButton_Click" /><br />

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
Put the control which needs to update here, such as GridView.

</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="loginButton" EventName="Click"/>
</Triggers>
</asp:UpdatePanel>

<br />


<asp:Button runat="server" ID="hiddenTargetControlForModalPopup" style="display:none"/>
<ajaxToolkit:ModalPopupExtender runat="server" ID="programmaticModalPopup"
BehaviorID="programmaticModalPopupBehavior"
TargetControlID="hiddenTargetControlForModalPopup"
PopupControlID="programmaticPopup"
BackgroundCssClass="modalBackground"
DropShadow="True"
RepositionMode="RepositionOnWindowScroll" >
</ajaxToolkit:ModalPopupExtender>

<asp:Panel runat="server" CssClass="modalPopup" ID="programmaticPopup" style="background-color:##FFFFCC;display:none;height:25px;width:85px;padding:10px">
<div id='messagediv' style="text-align:center">Loading...</div>

</asp:Panel>
</form>
</body>
    protected void loginButton_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(2000);//For testing.
programmaticModalPopup.Hide();

}

(You can change the css style or add an image into programmaticPopup instead of "Loading".)

With this sample, we need assign the specific control to raise the ModalUpdateProgress.

2. How can we pop up the ModalUpdateProgress bar while each request is posted and each control is triggered?

On assuming that you have a GridView. And when the request is from GridView, it will generate popup panel to display "loading".

<script type="text/javascript">
function pageLoad()
{
$find('modalbehavior').hide();
}
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(
function(sender, e)
{
if (e.get_postBackElement().id == "<%= GridView1.ClientID %>") // This line can make sure only the request from GridView1 will present popup panel, you can change it to other control client id.
{
$find('modalbehavior').show();
}
});

</script>

Beside this code, you need a ModalPopup to show the Loading message.

<style>
.modalBackground {
background-color:Gray;
filter:alpha(opacity=70);
opacity:0.7;

}

.modalPopup {
background-color:#ffffdd;
border-width:3px;
border-style:solid;
border-color:Gray;
padding:3px;
width:250px;
}
<asp:Button runat="server" ID="hiddenTargetControlForModalPopup" style="display:none"/>
<ajaxToolkit:ModalPopupExtender runat="server" ID="programmaticModalPopup"
BehaviorID="programmaticModalPopupBehavior"
TargetControlID="hiddenTargetControlForModalPopup"
PopupControlID="programmaticPopup"
BackgroundCssClass="modalBackground"
DropShadow="True"
RepositionMode="RepositionOnWindowScroll" >
</ajaxToolkit:ModalPopupExtender>
<asp:Panel runat="server" CssClass="modalPopup" ID="programmaticPopup" style="background-color:##FFFFCC;display:none;height:25px;width:85px;padding:10px">
<div id='messagediv' style="text-align:center">Loading...</div>

</asp:Panel>


How to get HTML code of a page?

 

How to get HTML code of the current page?

 

It's not difficult to achieve. We can get it in Page Render.

  protected   override   void   Render(HtmlTextWriter   writer)    
  {  
                System.IO.StringWriter   sw;  
                HtmlTextWriter   htmltw;  
                sw   =   new   System.IO.StringWriter();  
                htmltw   =   new   HtmlTextWriter(sw);  
                base.Render(htmltw);  
                htmltw.Close();  
                sw.Close();  
                String   tempsource   =   sw.ToString();  
                //tempsource is the HTML code of the page, you can create HTML page with it to complete converting aspx to HTML.  
  }

 

How to get HTML code of external page?

 

If you are familiar with AJAX, you must have known that XMLHttpRequest can retrieve the HTML responsed from srver asynchronously. However, XMLHttpRequest can't support cross-domain. We can't retrieve the HTML from page which is in other demain. How do we get that, how do we get HTML code from external page?

 

We can make use of System.Net.WebRequest to retrieve the HTML from external web page and save it to file or display on the page.

    private void saveURL(string url)
    {
        if (!string.IsNullOrEmpty(url))
        {
            string content = "";

            System.Net.WebRequest webRequest = WebRequest.Create(url);
            System.Net.WebResponse webResponse = webRequest.GetResponse();
            System.IO.StreamReader sr = new StreamReader(webResponse.GetResponseStream(), System.Text.Encoding.GetEncoding("UTF-8"));
            content = sr.ReadToEnd();
            //save to file
            StreamWriter sw = new StreamWriter(Server.MapPath("webcontent.txt"));
            sw.Write(content);
            sw.Flush();
            //display it in this page
            Response.Write(content);
        }
    }

You can call the method like:  saveURL("http://forums.asp.net/t/1201527.aspx"); Please pay attention on the format of URL, "http://" is necessary. Without it, you will encounter the error "The format of the URI could not be determined. "

Also, you can retrieve the HTML responsed with cross-domain through this server agent.

 

How can we update the UpdatePanel on Client?

When you do postback via the control which is the trigger-control of UpdatePanel, the content of UpdatePanel will be updated asynchronously. We can also call Update1.Update() to refresh the content within UpdatePanel. But how to update the UpdatePanel on client?

Fortunately, we can call Sys.WebForms.PageRequestManager.getInstance()._doPostBack('UpdatePanel1_UniqueID','UpdatePanel1_UniqueID') to achieve updating UpdatePanel1 on client.



Now, please see this codes:

function pageLoad(objSender, args) {
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('UpdatePanel1', 'UpdatePanel1');
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('UpdatePanel2', 'UpdatePanel2');
Sys.WebForms.PageRequestManager.getInstance()._doPostBack('UpdatePanel3', 'UpdatePanel3');
}

Does it will update all these UpdatePanel controls on client?

No. Actually, the new request will cancel the old request if the old request is not finished. So in this scenario, only UpdatePanel3 will be updated. How to update these three UpdatePanel controls?

JavaScript is for single-thread. It have to call another one when the previous is done. So we can post the new request when the old one is finished.

<script type="text/javascript">
var IsSpecific=false;
var updatepanelid;
function doupdate()
{
IsSpecific=true;
CurrectUpdatePanelId=0;
updatepanels_id=['<%=UpdatePanel1.UniqueID%>','<%=UpdatePanel2.UniqueID%>','<%=UpdatePanel3.UniqueID%>'];
doNext();
}

function doNext()
{
Sys.WebForms.PageRequestManager.getInstance()._doPostBack(updatepanels_id[CurrectUpdatePanelId], updatepanels_id[CurrectUpdatePanelId]);
CurrectUpdatePanelId++;

if(CurrectUpdatePanelId>=updatepanels_id.length)
IsSpecific=false;
}

</script>
<div>
<script type="text/javascript">

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(
function(sender, e)
{
if(IsSpecific)
{
doNext();
}
});


</script>
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<%=DateTime.Now %>
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<%=DateTime.Now %>
</ContentTemplate>
</asp:UpdatePanel>
<asp:UpdatePanel ID="UpdatePanel3" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<%=DateTime.Now %>
</ContentTemplate>
</asp:UpdatePanel>
<input id="Button1" type="button" value="button" onclick="doupdate()" />
</div>

Now the three UpdatePanel controls will be updated one by one.

Automatically ajust AjaxControlToolkit Calendar position on the page

Many Deveopers are puzzled by this issue. When we create an AjaxControlToolkit Calendar control on the page, if the Calendar control is closed to the bottom of page and there is no room to present the whole Calendar, the Calendar will be still popped up towards the bottom of page. We can only see a part of Calendar.

We hope that it can ajust position itself. If there is no room to present, it can popped up upwards.



The below codes will achieve it by making use of client event "OnClientShown".



<script type="text/javascript" language="javascript">
//$get('<%=TextBox1.ClientID %>').value=sender.get_selectedDate().localeFormat("MM/dd/yyyy");
function getTop(e)
{
var offset=e.offsetTop;
if(e.offsetParent!=null) offset+=getTop(e.offsetParent);
return offset;
}


function onCalendarShown(sender,args)
{
var calendar= $find('Calendar1');
var screenTop=null;
if(Sys.Browser.agent==Sys.Browser.InternetExplorer)
screenTop=document.documentElement.scrollTop;
else
screenTop=document.body.scrollTop;
var screenBottom=screenTop+document.documentElement.clientHeight;

if((getTop(calendar.get_element())+calendar.get_element().offsetHeight+calendar._height)>screenBottom)
{

calendar._popupDiv.style.top=getTop(calendar.get_element())-calendar._popupDiv.offsetHeight+'px';
}



}


</script>
<body>
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />
<br /><br /><br /><br /><br /><br /><br /><br /><br />
<ajaxToolkit:CalendarExtender ID="calendar1" runat="Server" BehaviorID="Calendar1" TargetControlID="TextBox1" PopupPosition="BottomLeft" OnClientShown="onCalendarShown" />
<asp:TextBox ID="TextBox1" runat="server" autocomplete="off" ></asp:TextBox>
<br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br />
</form>
</body>