locked
Custom login control RRS feed

  • Question

  • User853066379 posted

    Hi,

    I'm trying to create a custom login control that is wrapped with a div rather than a table. I found this (http://www.sidesofmarch.com/index.php/archive/2006/02/28/removing-tables-in-microsofts-aspnet-20-controls/) article in c# and have tried to recreate it in vb but keep getting an error, so I'm probably doing something pretty obvious wrong! My code so far is:

    Namespace WebControls
        Public Class membershipLogin
    
            Inherits Login
    
            Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
                MyBase.Render(writer)
    
                Dim myDiv As New WebControl(HtmlTextWriterTag.Div)
    
                LayoutTemplate.InstantiateIn(myDiv)
                Controls.Clear()
                Controls.Add(myDiv)
                myDiv.CopyBaseAttributes(Me)
                myDiv.CssClass = Me.CssClass
                myDiv.RenderControl(writer)
    
            End Sub
    
        End Class
    End Namespace

    When I debug the above I get an Object reference not set to an instance of an object error at LayoutTemplate.InstantiateIn(myDiv). 

    Any help with this is greatly appreciated.

    Cheers

    Matt
    Friday, October 30, 2009 6:17 AM

Answers

  • User1633691049 posted

    I've made some tests, the Login control uses an internal LoginContainer as container for the login form. Been an internal / private class, we have no access to override the rendering TagKey of this container. So whatever you'll try, the login form will always be wrapped by the html tags generated by this LoginContainer. Unfortunatelly the login container rendering tag is a Table.

    I bring back the subject of CSS Friendly adapters. You can download the source code from here:

    http://cssfriendly.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=2159

    Check the LoginAdapter class, RenderContents method, to see how do they render the login control as div. Basically they rewrite the html tags for the entire controls collection.


    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 8:41 AM
  • User1633691049 posted

    The example

    Ok, thanks for the info.


    What I still don't get is how someone has managed to do this is c# here:

    http://www.sidesofmarch.com/index.php/archive/2006/02/28/removing-tables-in-microsofts-aspnet-20-controls/



    The example you gave works only when you use the LayoutTemplate. When you use the login control default rendering it is not working.

    What they did is very simple. They instantiate the LayoutTemplate in a newly created Div.

    WebControl div = new WebControl( HtmlTextWriterTag.Div );

    LayoutTemplate.InstantiateIn( div );


    That solution is incomplete. LayoutTemplate property is null when you use the default Login control template, so that code will fail.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 10:36 AM
  • User853066379 posted

    Managed to do want I wanted by doing the following:


    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
    
                writer.WriteBeginTag("div")
                writer.WriteAttribute("class", Me.CssClass)
                writer.Write(HtmlTextWriter.TagRightChar)
    
                Dim strB As New StringBuilder
                Dim strW As New StringWriter(strB)
                Dim htmlTW As New HtmlTextWriter(strW)
    
                MyBase.Render(htmlTW)
    
                Dim str As String
                str = strB.ToString
    
                str = Regex.Replace(str, "<table[^>]*>", "<div>")
                str = Regex.Replace(str, "</table>", "</div>")
                str = Regex.Replace(str, "<tr[^>]*>", "<div>")
                str = Regex.Replace(str, "</tr>", "</div>")
                str = Regex.Replace(str, "</?td[^>]*>", String.Empty)
                str = Regex.Replace(str, "</?thead[^>]*>", String.Empty)
                str = Regex.Replace(str, "</?tbody[^>]*>", String.Empty)
    
                writer.Write(str)
                writer.WriteEndTag("div")
    
    End Sub


    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 11:28 AM

All replies

  • User1633691049 posted

    Why don't you use the login control Layout Template. You need to drag and drop a Login control in a page. Right click on it and choose from the context menu 'Convert to Template'. You may customize it in any way you may want. It has also the advantage of the design-time support. I add here the html markup of a one minute customized login control.

            <asp:Login ID="Login1" runat="server">
                <LayoutTemplate>
                    <div>
                                <h3>Log In</h3>
                                
                                <asp:Label ID="UserNameLabel" runat="server" AssociatedControlID="UserName">User Name:</asp:Label>
                                <asp:TextBox ID="UserName" runat="server"></asp:TextBox>
                                <asp:RequiredFieldValidator ID="UserNameRequired" runat="server" ControlToValidate="UserName"
                                                ErrorMessage="User Name is required." ToolTip="User Name is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator>
                                <br />          
                                <asp:Label ID="PasswordLabel" runat="server" AssociatedControlID="Password">Password:</asp:Label>
                                <asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox>
                                <asp:RequiredFieldValidator ID="PasswordRequired" runat="server" ControlToValidate="Password"
                                                ErrorMessage="Password is required." ToolTip="Password is required." ValidationGroup="Login1">*</asp:RequiredFieldValidator>
                                <br />
                                <asp:CheckBox ID="RememberMe" runat="server" Text="Remember me next time." />
                                <br />
                                <span style="color: red">
                                            <asp:Literal ID="FailureText" runat="server" EnableViewState="False"></asp:Literal>
                                </span>
                                <br />
                                <asp:Button ID="LoginButton" runat="server" CommandName="Login" Text="Log In" ValidationGroup="Login1" />
                    </div>
                </LayoutTemplate>
            </asp:Login>




    I hope this will help.

    Cheers,

    Florin

    Friday, October 30, 2009 7:10 AM
  • User377791177 posted

    1. You'll not be able to override its UI generation code coz you've to give call to

       MyBase.Render(writer), so it'll render a table for sure.


    2. what is LayoutTemplate


    3. You can create a control of your own from scratch, for e.g. i've ajaxed the same login control so that validation occurs on the client side

    http://shashankbhide.blogspot.com/2009/03/ajaxing-login-control.html


    Friday, October 30, 2009 7:10 AM
  • User853066379 posted

    Hi, thanks for the reply.


    Unfortunately this isn't an option for me, I need to create it as a custom control.



    Friday, October 30, 2009 7:17 AM
  • User1633691049 posted

    You may try to use Adaptive rendering provided by CSS Friendly Adapters, see here the login control rendered as div:

    http://www.asp.net/CSSAdapters/Membership/Login.aspx

    Cheers,

    Florin


    Friday, October 30, 2009 7:21 AM
  • User853066379 posted

    Hi, thanks again for your reply.


    I've looked at the cssadapters, the problem I'm having with thos is that I need to extend a couple and I'm struggling there too.


    I can do everything I need to with the standard controls the only problem I have is the fact they render with tables rather than divs.

    Friday, October 30, 2009 7:30 AM
  • User377791177 posted

    I can do everything I need to with the standard controls the only problem I have is the fact they render with tables rather than divs.

    You've to accept that when you call

    MyBase.Render(writer), the framework renders the UI code and prepares a table, and this behaviour can't be overridden.

    Friday, October 30, 2009 8:05 AM
  • User853066379 posted

    Hi,


    Even when removing MyBase.Render(writer) I still get the same error as in my original post, MyBase.Render(writer) isn't the problem.

    Friday, October 30, 2009 8:16 AM
  • User377791177 posted

    Hi,


    Even when removing MyBase.Render(writer) I still get the same error as in my original post, MyBase.Render(writer) isn't the problem.

    AFAIK my dear friend, u don't have a choice to call base class's render, you've to call it. Otherwise its like i'll Inherit from Textbox and create the datagrid.

    Friday, October 30, 2009 8:23 AM
  • User1633691049 posted

    I've made some tests, the Login control uses an internal LoginContainer as container for the login form. Been an internal / private class, we have no access to override the rendering TagKey of this container. So whatever you'll try, the login form will always be wrapped by the html tags generated by this LoginContainer. Unfortunatelly the login container rendering tag is a Table.

    I bring back the subject of CSS Friendly adapters. You can download the source code from here:

    http://cssfriendly.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=2159

    Check the LoginAdapter class, RenderContents method, to see how do they render the login control as div. Basically they rewrite the html tags for the entire controls collection.


    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 8:41 AM
  • User853066379 posted

    I've made some tests, the Login control uses an internal LoginContainer as container for the login form. Been an internal / private class, we have no access to override the rendering TagKey of this container. So whatever you'll try, the login form will always be wrapped by the html tags generated by this LoginContainer. Unfortunatelly the login container rendering tag is a Table.

    I bring back the subject of CSS Friendly adapters. You can download the source code from here:

    http://cssfriendly.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=2159

    Check the LoginAdapter class, RenderContents method, to see how do they render the login control as div. Basically they rewrite the html tags for the entire controls collection.



    Ok, thanks for the info.


    What I still don't get is how someone has managed to do this is c# here:

    http://www.sidesofmarch.com/index.php/archive/2006/02/28/removing-tables-in-microsofts-aspnet-20-controls/


    Friday, October 30, 2009 9:39 AM
  • User1633691049 posted

    The example

    Ok, thanks for the info.


    What I still don't get is how someone has managed to do this is c# here:

    http://www.sidesofmarch.com/index.php/archive/2006/02/28/removing-tables-in-microsofts-aspnet-20-controls/



    The example you gave works only when you use the LayoutTemplate. When you use the login control default rendering it is not working.

    What they did is very simple. They instantiate the LayoutTemplate in a newly created Div.

    WebControl div = new WebControl( HtmlTextWriterTag.Div );

    LayoutTemplate.InstantiateIn( div );


    That solution is incomplete. LayoutTemplate property is null when you use the default Login control template, so that code will fail.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 10:36 AM
  • User853066379 posted

    I see.

    Thanks for your help.

    Friday, October 30, 2009 10:50 AM
  • User853066379 posted

    Managed to do want I wanted by doing the following:


    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
    
                writer.WriteBeginTag("div")
                writer.WriteAttribute("class", Me.CssClass)
                writer.Write(HtmlTextWriter.TagRightChar)
    
                Dim strB As New StringBuilder
                Dim strW As New StringWriter(strB)
                Dim htmlTW As New HtmlTextWriter(strW)
    
                MyBase.Render(htmlTW)
    
                Dim str As String
                str = strB.ToString
    
                str = Regex.Replace(str, "<table[^>]*>", "<div>")
                str = Regex.Replace(str, "</table>", "</div>")
                str = Regex.Replace(str, "<tr[^>]*>", "<div>")
                str = Regex.Replace(str, "</tr>", "</div>")
                str = Regex.Replace(str, "</?td[^>]*>", String.Empty)
                str = Regex.Replace(str, "</?thead[^>]*>", String.Empty)
                str = Regex.Replace(str, "</?tbody[^>]*>", String.Empty)
    
                writer.Write(str)
                writer.WriteEndTag("div")
    
    End Sub


    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, October 30, 2009 11:28 AM
  • User1633691049 posted

    One small remark. You should replace this code:

    1.             writer.WriteBeginTag("div")  
    2.             writer.WriteAttribute("class"Me.CssClass)  
    3.             writer.Write(HtmlTextWriter.TagRightChar) 

    with:

    AddAttributesToRender(writer)
    writer.RenderBeginTag(HtmlTextWriterTag.Div)
    

    The first line will allow you to preserve all the attributes from the login control level to the div level. That include the class attribute and the login control ID attribute. Your code is removing now the login ID.

    Friday, October 30, 2009 11:47 AM