Thursday, May 28, 2009

Build the client-side contentChanged event of HTML Editor -- hook keypress client event of Editor content

In this time, let us talk about HTML Editor. We can see HTML Editor has three modes: Design, HTML Text and Preview mode.
Design mode is in an IFrame actually. HTML Text is prensented in a TextArea and Preview mode is in another Iframe. The code rendered about these three modes is as below.

Design mode:

<iframe id="editor1_ctl02_ctl00" name="editor1_ctl02_ctl00" marginheight="0" marginwidth="0" frameborder="0" src="javascript:false;" style="height:100%;width:100%;display:none;border-width:0px;">
</iframe>



HTML Text mode:

<textarea id="editor1_ctl02_ctl01" class="ajax__htmleditor_htmlpanel_default" style="height:100%;width:100%;display:none;">
</textarea>


Preview mode:

<iframe id="editor1_ctl02_ctl02" name="editor1_ctl02_ctl02" marginheight="0" marginwidth="0" frameborder="0" src="javascript:false;" style="height:100%;width:100%;display:none;border-width:0px;">
</iframe>


In design mode, we can use the following JavaScript to append a callback function in onKeypress event so that we can do somthing when content is changed on client.

<script type="text/javascript">
        var count = 0;
        function pageLoad() {
            $get('editor1_ctl02_ctl00').contentWindow.document.body.onkeypress = function() {
                count++;
                $get('cc').innerHTML = "you input " + count + "charactors";        

            };
        }

</script>


As the same way, you can append another keypress event on TextArea and Preview IFrame.

If you'd like to do something on server-side when you typed in content of Editor, you can call Web Service to achieve it.

AjaxControlToolkit ComboBox not appearing in ModalPopup

Most of us is enjoying ComboBox presenting, except it is inside ModalPopup.
When we use ComboBox in ModalPopup, it throws the client error and only thing in our sight is a bald TextBox instead of entire ComboBox in ModalPopup Panel. After debugging, we can find the error message is pointing to this line b.width=c+"px".
Actually, the ModalPopup behavior mix it up. So the only thing we need to do is
1. Put the ModalPopupExtender behind the Panel which contains ComboBox.
2. Set style "display" to "block"(default value if you don't set the value of it).

Check the sample as below:


<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<asp:Panel ID="panel" runat="server" >
<cc1:ComboBox ID="ComboBox1" runat="server" AutoPostBack="true" DropDownStyle="DropDown">
<asp:ListItem Value="0" Text="1" >Text 1</asp:ListItem>
<asp:ListItem Value="1" Text="2">Text 2</asp:ListItem>
<asp:ListItem Value="2" Text="3">Text 3</asp:ListItem>
</cc1:ComboBox>
</asp:Panel>
<cc1:ModalPopupExtender ID="MPE" runat="server" PopupControlID="panel" TargetControlID="LB"> ---Put ModalPopupExtender behind Panel "panel".---
</cc1:ModalPopupExtender>
<asp:LinkButton ID="LB" runat="server" Text="clickme">click</asp:LinkButton>


Actually we have the second way to resolve this problem. The issue will be encountered as soon as the parent element of combobox is invisible(style display=none or visibility=hidden). So the simplest approach is we can set it visible after rending and set it back in pageLoad.



Input your modalpopup panel clientID and put the above script after document body and form content.
The same problem in this link: http://vincexu.blogspot.com/2009/07/ajaxcontroltoolkit-combobox-not-showing.html

Tuesday, May 26, 2009

How to cancel Tab active changing with Validation

Sometimes, when you change the active Tab Panel, you want to do validation and check if it should go to next tabpanel. It will go to the new Tab Panel only if it is meets the validation.
For example, there is a TextBox in TabPanel. If TextBox is empty, I don't want to let it go to another TabPanel.
For this scenario, the validation is based on the client side. The first confirm is we have to use JavaScript to do validation or catch the validation result if you used Validation control. There is a client event "ActiveTabChanged"(add_activeTabChanged method in behavior) of TabContainer you can make use of to do validation in this event. If it is valid, you can let it free to go. Overwise, you need use javascript to let it back to the previous tab. In this way, you need use a client validation to restore the history of the active tab index so that it can remeber which tab panel it can go back to in JavaScript .
But in this approach, it is go through the ActiveChanged client event. It means the active tab has been changed before we call this event. We would see the active tab goes to another one and go back again if it is invalid with validation. It looks too stupid and ugly.

So, I got two approaches to achieve this.

1. We can modify the source code of Tab Panel behavior as below. In _header_onclick method, it calls raiseClick and set the activeTab directly. We can insert an additional code line before setting active tab so that we can do something on validation.

AjaxControlToolkit.TabPanel.prototype._header_onclick = function(e) {
this.raiseClick();
if (isValidated()) // add this validation method
this.get_owner().set_activeTab(this);
};

It won't go to another tab unless it meets the validation. See the entire code below:



<body>
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />
<ajaxToolkit:TabContainer runat="server" ID="TabContainer1">
<ajaxToolkit:TabPanel runat="server" ID="TabPanel1" HeaderText="TabPanel1">
<ContentTemplate>
TabPanel1
<input type="text" id="text1" />
</ContentTemplate>
</ajaxToolkit:TabPanel>
<ajaxToolkit:TabPanel runat="server" ID="TabPanel2" HeaderText="TabPanel2">
<ContentTemplate>
TabPanel2
</ContentTemplate>
</ajaxToolkit:TabPanel>
</ajaxToolkit:TabContainer>
</form>
</body>

<script type="text/javascript">

AjaxControlToolkit.TabPanel.prototype._header_onclick = function(e) {
this.raiseClick();
if (isValidated()) // add this additional code line to do validation
this.get_owner().set_activeTab(this);

};

function isValidated() {
if ($get("text1").value == "")
return false;
return true;
}

</script>


2. The above code looks like a workround purely. The directly approach is using ActiveTabChanging client event. In general ASP.Net Ajax behavior model, we can call e.set_cancel(true) to cancel the changing behavior after validation, so we can prevent the active tab changing before ActiveTabChanged called.
Unfortunately, it doesn't contain this event in tab.js. The only public client event is add_activeTabChanged. So what I wanna saying is Let's make an add_activeTabChanging client event in tab.js behavior.

1) Please open tab.js in VS.
2) Please append the following code in AjaxControlToolkit.TabContainer.prototype = {


///<extended for activeTabChanging>
add_activeTabChanging: function(handler) {
this.get_events().addHandler("activeTabChanging", handler);
},
remove_activeTabChanging: function(handler) {
this.get_events().removeHandler("activeTabChanging", handler);
},
raiseActiveTabChanging: function(eventArgs) {
var eh = this.get_events().getHandler("activeTabChanging");
if (eh) {
eh(this, eventArgs);
}

},


///</extended for activeTabChanging>

3) Please modify set_activeTabIndex method block (The blod font is new code we need to append):



set_activeTabIndex : function(value) {
if (!this.get_isInitialized()) {
this._cachedActiveTabIndex = value;
} else {
///<extended for activeTabChanging>
var eventArgs = new Sys.CancelEventArgs();
this.raiseActiveTabChanging(eventArgs);
if (eventArgs.get_cancel()) {
return false;
}
///</extended for activeTabChanging>
if (value < -1 value >= this.get_tabs().length) {
throw Error.argumentOutOfRange("value");
}
if (this._activeTabIndex != -1) {
this.get_tabs()[this._activeTabIndex]._set_active(false);
}
this._activeTabIndex = value;
if (this._activeTabIndex != -1) {
this.get_tabs()[this._activeTabIndex]._set_active(true);
}
if (this._loaded) {

this.raiseActiveTabChanged();
}
this.raisePropertyChanged("activeTabIndex");

}
return true;
},


4) Then we can use add_activeTabChanging client event now. As the same HTML sample, you can just call this event on client to cancel the process if it doesn't meet the validation.


<body>
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />
<ajaxToolkit:TabContainer runat="server" ID="TabContainer1">
<ajaxToolkit:TabPanel runat="server" ID="TabPanel1" HeaderText="TabPanel1">
<ContentTemplate>
TabPanel1
<input type="text" id="text1" />
</ContentTemplate>
</ajaxToolkit:TabPanel>
<ajaxToolkit:TabPanel runat="server" ID="TabPanel2" HeaderText="TabPanel2">
<ContentTemplate>
TabPanel2
</ContentTemplate>
</ajaxToolkit:TabPanel>
</ajaxToolkit:TabContainer>
</form>
</body>

<script type="text/javascript">

function pageLoad() {

$find('TabContainer1').add_activeTabChanging(aa);

}
function aa(se, e) {

if ($get('text1').value == "")
e.set_cancel(true);
}


</script>

Wednesday, May 20, 2009

new version of AjaxControlToolkit 3.0.30512 and new controls ColorPicker, ComboBox, HTML Editor

Lately, AjaxControlToolkit 3.0.30512 is published. ColorPicker, Combobox and HTML Editor are three new controls appeared in this new version. I'm a support engineer for Microsoft product, especially for Asp.Net Ajax. I got many questions about these three new ajax controls, especially for HTML Editor. I'd like to talk about it with you guys here.

I checked the new version a little, and I found the behavior.js file is no longer composed by only one js file, but it is divided to release.js and debug.js file which is like the architecture of ASP.Net Ajax framework.

ColorPicker and Combobox are simple(I build an client-side ASP.Net combobox at one time. http://vincexu.blogspot.com/2008/11/build-combobox-in-aspnet.html ). But for HTML Editor, it is huge thing to make. It's a kind of WYSIWYG editor in the web page. There has been FCKEditor, TinyMCE, FreeTextBox,RadEditor and FreeRichTextEditor working for this. TinyMCE is the most popular component in it because it is free and easy to extend.

AjaxControlToolkit adopts the Editor by www.obout.com which is the website focuses on ASP.Net Ajax Controls. HTML Editor is a greate thing to work, but we have not enough documents to take as references in AjaxControlToolkit about this control. I got many questions about this control. For instance, how to insert an Image button into TopToolBar; how to override and assign a custom tooltip for one of button in TopToolBar; how about the client API we can use. Actually, we can't get valuable documents from AjaxControlToolkit, but we can access www.obout.com to check about it. However, in Ajaxcontroltoolkit HTML Editor, it has been changed somethings to match the behavior template, although it is coming from obout.com -- the method name is changed, property is changed and so on. So what we can do is checking the API or other documents in obout.com(http://www.obout.com/editor_new/index.aspx) and looking for the real method/property name in AjaxControlToolkit HTML Editor.debug.js.(The structure is same)

Also, I'll build the AjaxControlToolkit HTML Editor Client API documents or reference in my blog if some body want me to do.

Wednesday, May 13, 2009

LinkButton/Button embedded in a CollapsiblePanel Header - Click Events not fired.

If a LinkButton is in Header of CollapsiblePanel, you will find the onClick of it will not be fired. To resolve this issue, we can set SuppressPostBack="false" for CollapsiblePanel. Then the onClick event will be fired fine. But the new problem is coming -- the CollapsiblePanel is still working, when you click the LinkButton, the onclick event is working and the CollapsiblePanel is still expended.

If we'd like to achieve the LinkButton firing without CollapsiblePanel expending, we have to call some JavaScript to cancel the CollapsiblePanel expending.


<head runat="server">
    <title></title>
    <script type="text/javascript">
        var togger = 0;
        function pageLoad() {
            $find('CollapsiblePanelBehavior1').add_expanding(function(obj, e) {
                if (togger == 1) {
                    e.set_cancel(true);
                    togger = 0;
                }
            });       
        }  
    </script> 
   
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
        <ajaxToolkit:ToolkitScriptManager runat="Server" EnablePartialRendering="true" ID="ScriptManager1" />

     <asp:Panel ID="Panel1" runat="server" style="background-color:#EEEEEE;" Height="30px" >
            <div style="padding:5px; cursor: pointer; vertical-align: middle;">
                <div style="float: left;">check</div>
                <div style="float: left; margin-left: 20px;">
                    <asp:Label ID="Label1_SelectData" runat="server">(Hide checking...)</asp:Label>
                </div>
                <div style="float: right; vertical-align: middle;">
                    <asp:ImageButton ID="ImageButton1" runat="server" ImageUrl="~/images/expand_blue.jpg" AlternateText="(Show to search...)"/>
                    <asp:LinkButton ID="LinkButton1" runat="server" OnClientClick="togger=1;" OnClick="LinkButton1_Click">LinkButton</asp:LinkButton>
                </div>
            </div>
       </asp:Panel> 
      <asp:Panel ID="Panel1Data" runat="server" style="background-color:#EEEEEE;" Height="30px">
            text1
      
    </asp:Panel>
    <ajaxToolkit:CollapsiblePanelExtender ID="CollapsiblePanelExtender1" runat="Server"
        TargetControlID="Panel1Data"
        ExpandControlID="Panel1"
        CollapseControlID="Panel1" 
        Collapsed="false"
        TextLabelID="Label1_SelectData"
        ImageControlID="ImageButton1"   
        ExpandedText="(Hide searching...)"
        CollapsedText="(Show to search...)"
        ExpandedImage="~/images/collapse_blue.jpg"
        CollapsedImage="~/images/expand_blue.jpg"
        SuppressPostBack="false"
        SkinID="CollapsiblePanelDemo" BehaviorID="CollapsiblePanelBehavior1" />
       
       
    </div>
    </form>
</body>