locked
CustomPages not working CONTINUED and Continuing RRS feed

  • Question

  • User1432255915 posted

    Hi,

    This thread was getting so long I decided to create another one. http://forums.asp.net/p/1766929/4826520.aspx/1?Re+CustomPages+not+working+CONTINUED

    After debugging my app for many days, I think I have finally discovered what is wrong with it.

    DD does not have any support for binary data. It does not have any web controls to display and edit binary data.

    I have a Password field in the database that uses binary (byte[]). It is not nullable. So whe I was trying to edit an entity I was getting an error that was not clear. Please see other thread.

    This is how it looks like. Please note for now that I have added a DataType.Text so I can see something in DD.

    [DataType(DataType.Text)]
    public byte[] Password { get; set; }

    <Property Name="Password" Type="binary" Nullable="false" MaxLength="64" />
    <Property Name="Password" Type="Binary" Nullable="false" MaxLength="64" FixedLength="true" />
    <ScalarProperty Name="Password" ColumnName="Password" />

    I have created a custom web control called tblUser_Edit.ascx under entity templates folder so that I can display this field and other fields to edit.

    Please could someone shed some light on what I have to do to achieve the following:

    1 - Get the binary value of this field from the database and run it through a local function called ConvertPassword(byte[] pass)  so that I can convert it to a string and display in a text box.

    2 – As the user edit this field, before submitting back to the database, I have to run it through another local function to convert it back to binary.

    Where is the place to do it?

    Cheers

    C

     

     

     

     

    Wednesday, February 15, 2012 9:46 AM

Answers

  • User1641955678 posted

    Glad you got it working!

    1. you need to redirect to the same page that the link from Default.aspx would take you. By default, it will be something like ~/YourTableName/List.aspx.

    2. I just checked and this works in the default DD site. e.g. after setting EnablePartialRendering="true", if I go the the List page and sort by clicking on a column header, it doesn't do a full post back. Does that scenario work for you? If not, you'd need to isolate the differences from the default site.

    3. Hard to know for sure without knowing in what way it wasn't working. ConvertEditedValue is a convenience method to deal with the ConvertEmptyStringToNull and NullDisplayText. You may not need it at all in your scenario.

    4. FieldValueString calls FormatFieldValue(FieldValue), which does its best to turn the object into a string. It looks at the various formatting options: DataFormatString, ConvertEmptyStringToNull, NullDisplayText, HtmlEncode. It probably doesn't work for your custom data type for the same reason that #3 doesn't work, as the two are more or less symmetrical. If you're happy with what's being displayed, you can bypass this method.

    Note that it might be better to start separate discussion for each topic, as it can become hard to track many different things in one thread. Technically, these 4 questions are not directly related to the use of a Custom Page :)

    David

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 17, 2012 12:45 PM

All replies

  • User1043796333 posted

    I think that there is an ancient tutorial video in which Scott Hanselmann shows how to post pictures into your Dynamic Data web site

    You might Google it for if it has already been removed from the official ASP.Net page

    I think you can find it in YoTube

    Good luck

    Wednesday, February 15, 2012 10:11 AM
  • User1043796333 posted

    P.S. I must add once more than it seems that there is a lot of reluctancy from SOME contributors to "really help us"

    I can't see a reason why a person have to wait too long for not getting an answer to some kind of trivial questions

    Tired of it!!

    Wednesday, February 15, 2012 10:12 AM
  • User1432255915 posted

    Thanks Topolov,

     

    I will see if I can find the tutorial you mentioned.

    However, in my case I will not have to display any images. I just have to convert the password that is stored as binary in the database to a string to display in DD.

    I wish someone with more experience could provide some simple code sample of how to create a new control to display this binary data.

    Cheers

    C

     

     

    Wednesday, February 15, 2012 11:07 AM
  • User1432255915 posted

    Hi,

    I have found the link to the webcast you suggested.

    http://blogs.msdn.com/b/scothu/archive/2008/04/09/sample-for-displaying-images-updated-screencast.aspx

    However, he does not explain how to create the ascx control itself.

    I am not sure how to access the data I need in a new ascx control.

    Would you be able to explain to me what methods, events I could use in the control to access my data value and convert it to a string before displaying it in a text box to be edited?

    Cheers

    C

    Wednesday, February 15, 2012 11:38 AM
  • User1043796333 posted

    I am not an expert on this as to be giving advise but I can tell you what I think ... or guess

    I am mostly using ListDetails.aspx template instead of the other ones, but there is something that it is for sure common to all pages

    If you open your page you'll find a DataGrid and a FormView, both with their own DataSource Control

    If you choose to activate any of the available methods for the data source which interests you, from the properties window (double click on the event you wnat to activate) and a new method will be appended to your ListDetails.aspx.cs page

    That method will have certain particular characteristics which you must explore in order to see if it is giving you the choices you want

    Yuo can check this out by means of using the common variable "e" followed by a dot. It'll display all things available (Intellisense)

    From there on, you can see if it has what you need

    As I told you, I'm not quite an expert on this but I opened my mouth so I'm trying to get you involved in solving this ASAP for you

    Keep me posted anyway ... which might as well not be good enough for you :(

    For converting variables into different type you can explore the usage of the "Convert" statement but if is about converting a number into a string you'll have enough by adding, let's say TextBox1.Text = myVariable.ToString(); and that will do it

    Wednesday, February 15, 2012 12:14 PM
  • User1432255915 posted

    Thanks Topolov,

    I will create a new ascx control in the FieldTemplates folder and use the UIHint to use this control for the property called Password.

    Then I will try to add the event handler as you suggested and see if I get the binary value from the database.

    Then I can use my funciton to convert the binary value to a string since this is an existing function.

    For me the only problem is to figure out where I get the data that came from the database. Also, after clicking the update button to send the updated values to the database,  I need to figure what event to use to change the value of the Password back to binary before submitting it to the database since it is expecing a binary value.

    Cheers

    C

    Wednesday, February 15, 2012 12:45 PM
  • User1043796333 posted

    In this 2 methods you have the data available for you

    protected void FormView1_ItemUpdated(object sender, FormViewUpdatedEventArgs e)

         var new  = e.NewValues;

         var old = e.OldValues;

    protected void FormView1_ItemInserted(object sender, FormViewInsertedEventArgs e)

        var vals = e.Values;

    You must however link this method to your control by double clicking the event (the ones displayed with a lightning in the properties window) as it will be recognized. It's not enough just writing the code YOU MUST LINK IT

    Wednesday, February 15, 2012 1:01 PM
  • User1432255915 posted

    Hi,

    I have good news and bad news.

    The good news is that I am now able to display the Password field in the GridView (List.aspx). Since this field is binary (byte[]) in the database and DD does not have support for binary data, I have created a new FieldTemplate called Binary.ascx. Here is the code.

    I use a UIHint in the domain service metadata class to use the new binary control.

     [UIHint("Binary")]
     public byte[] Password { get; set; } 

    Binary.ascx

    <%@ Control Language="C#" CodeBehind="Binary.ascx.cs" Inherits="MarsUserManager.BinaryField" %>

    <asp:Literal runat="server" ID="Literal1"/>

     Binary.ascx.cs

    public partial class BinaryField : FieldTemplateUserControl
    {
           
            protected override void OnDataBinding(EventArgs e)
            {
                base.OnDataBinding(e);

                if (this.FieldValue == null)
                {
                    return;
                }

                Literal1.Text = MarsUserManager.Utils.Tools.DecryptPassword((byte[])FieldValue).Replace("\0", "");

            }

            //public override string FieldValueString
            //{
            //   ....
            //   
            //}

            public override Control DataControl
            {
                get
                {
                    return Literal1;
                }
            }

    }

    Please note that I have removed the override string FieldValueString from the because I seem not to need it in there. Instead I have used the FieldValue to set the value of the Literal control.

    1 - Is this all right? Please correct me if I am wrong.

    This seems to be working well.

    The problem starts with the Edit.aspx. I have created a Binary_Edit.ascx to display the Password binary field when in edit mode. I display it in a text box for editing:

    Binary_Edit.ascx

    <%@ Control Language="C#" CodeBehind="Binary_Edit.ascx.cs" Inherits="MarsUserManager.BinaryEditField" %>

    <asp:TextBox ID="TextBox1" runat="server" CssClass="DDTextBox"></asp:TextBox>

    <asp:RequiredFieldValidator runat="server" ID="RequiredFieldValidator1" CssClass="DDControl DDValidator" ControlToValidate="TextBox1" Display="Static" Enabled="false" />
    <asp:RegularExpressionValidator runat="server" ID="RegularExpressionValidator1" CssClass="DDControl DDValidator" ControlToValidate="TextBox1" Display="Static" Enabled="false" />
    <asp:DynamicValidator runat="server" ID="DynamicValidator1" CssClass="DDControl DDValidator" ControlToValidate="TextBox1" Display="Static" />
    <asp:DomainValidator runat="server" ID="DomainValidator1" CssClass="DDControl DDValidator" Display="Static" />

     Binary_Edit.ascx.cs

     public partial class BinaryEditField : FieldTemplateUserControl
     {
            protected void Page_Load(object sender, EventArgs e)
            {
                if (Column.MaxLength < 20)
                {
                    TextBox1.Columns = Column.MaxLength;
                }
                TextBox1.ToolTip = Column.Description;

                SetUpValidator(RequiredFieldValidator1);
                SetUpValidator(RegularExpressionValidator1);
                SetUpValidator(DynamicValidator1);
                this.SetUpDomainValidator(DomainValidator1);
                SetUpValidator(DomainValidator1);
            }

            protected override void OnDataBinding(EventArgs e)
            {
                base.OnDataBinding(e);
                if (Column.MaxLength > 0)
                {
                   
                    TextBox1.MaxLength = Math.Max(FieldValueEditString.Length, Column.MaxLength);
                }

                var temp = MarsUserManager.Utils.Tools.DecryptPassword((byte[])FieldValue).Replace("\0", "");

                TextBox1.Text = temp;
            }

            protected override void ExtractValues(IOrderedDictionary dictionary)
            {
                //dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text);
                dictionary[Column.Name] = MarsUserManager.Utils.Tools.EncryptPassword(TextBox1.Text);
            }

            public override Control DataControl
            {
                get
                {
                    return TextBox1;
                }
            }

        }
    }

    I have also created a tblUser_Edit.ascx web control under the EntityTemplates folder so that I can limit the fields to display for editing:

    tblUser_Edit.ascx

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="tblUser_Edit.ascx.cs" Inherits="MarsUserManager.DynamicData.EntityTemplates.tblUser_Edit" %>

    <tr>
        <td>Name: </td>
        <td><asp:DynamicControl Mode="Edit" runat="server" DataField="FirstName" ID="fname"  ></asp:DynamicControl></td>
    </tr>

    <tr>
        <td>Password: </td>
        <td>
            <asp:DynamicControl Mode="Edit" runat="server" DataField="Password" ID="DynamicControl3" />
        </td>
    </tr>

    tblUser_Edit.ascx.cs 

     public partial class tblUser_Edit : System.Web.DynamicData.EntityTemplateUserControl
     {
            protected void Page_Load(object sender, EventArgs e)
            {
              

            }
     }

    The password is shown in the text box fine.

    However, when I click the update link button in the Edit.aspx page I get an error straight away and an asterisk beside the Password text box:

    List of validation errors
    The value is not valid.

    Password:   aaa111    *

    2 - I cannot figure out what is wrong. Can someone shed some light?

    I have tried commenting out the SetUpValidator method in the Binary_Edit.ascx.cs to see if I could see a better error message

    //SetUpValidator(RequiredFieldValidator1);
    //SetUpValidator(RegularExpressionValidator1);
    //SetUpValidator(DynamicValidator1);

    //this.SetUpDomainValidator(DomainValidator1);
    //SetUpValidator(DomainValidator1);

    I got this error:

    List of validation errors
    The data has been deleted in the underlying store.
    •The data has been deleted in the underlying store.

    Password:   aaa111          The data has been deleted in the underlying store. 

     

    I would appreciate very much if someone could help me to sort this out. I could send my whole project if someone is willing and kind to have a look at it if it is easier that way.

     

    Cheers

    C

     

     

    Thursday, February 16, 2012 12:01 PM
  • User1641955678 posted

    Hi Claudio,

    If you can post a small app that demonstrates the issue, I can try to take a look. But please make sure that the app is in a ready-to-run form that doesn't require external DB's to exist, etc, as that would make it difficult to run it.

    And sometimes, the process of reducing the repro from a big complex to a more minimal one can help make the issue clearer as well.

    thanks,
    David 

    Thursday, February 16, 2012 6:45 PM
  • User1432255915 posted

    Hi David,

    Thanks for your reply and help.

    I am glad to let you know that after debugging for many days I finally figured out where the problem was. The UserId property on my metadata class had a ReadOnly attribute set to true. This was causing the error since the Domain service class and EF did not know how to update the entity.

    After removing this attribute all seem to be working fine :-)

    [ReadOnly(true)]
    public int UserId { get; set; }

    I have a couple of minor issues now and would like to ask you a few questions about these and also what some methods in DD do:

    1 - when I run tha application, I do not need to go to the Default.aspx page since I am only scaffolding one table - tblUser.

    How can I redirect the user straight to the custom List.aspx page I have created under the CustomPagesFolder?

    I have tried using a Response.Redirect in the code behind of the defaul.aspx page but it is not working

    protected void Page_Load(object sender, EventArgs e)
    {
                System.Collections.IList visibleTables = Global.DefaultModel.VisibleTables;
                if (visibleTables.Count == 0)
                {
                    throw new InvalidOperationException("There are no accessible tables. Make sure that at least one data model is registered in Global.asax and scaffolding is enabled or implement custom pages.");
                }
                else
                {
                    Response.Redirect("~/DynamicData/CustomPages/" + visibleTables[0].ToString()+"/List.aspx");
                }

                Menu1.DataSource = visibleTables;
                Menu1.DataBind();
    }

     

    2 - ENABLING AJAX - I have enabled EnablePartialRendering in the ScriptManager in the Site.Master file. However, the site seems not to recognise that since it is following a normal post back. What else to I have to do to use ajaxise the site?

    <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true"/>

    Code - With the except of default.aspx, all of them use the UpdatePanel and ContentTemplate.

    Default.aspx

    <%@ Page Language="C#" MasterPageFile="~/Site.master" CodeBehind="Default.aspx.cs" Inherits="MarsUserManager._Default" %>

    <asp:Content ID="headContent" ContentPlaceHolderID="head" Runat="Server">
    </asp:Content>

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server" />

        <asp:GridView ID="Menu1" runat="server" AutoGenerateColumns="false"
            CssClass="DDGridView" RowStyle-CssClass="td" HeaderStyle-CssClass="th" CellPadding="6">
            <Columns>
                <asp:TemplateField HeaderText="Name" SortExpression="TableName">
                    <ItemTemplate>
                        <asp:DynamicHyperLink ID="HyperLink1" runat="server"><%# Eval("DisplayName") %></asp:DynamicHyperLink>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>
    </asp:Content>


    List.aspx

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
        <%--<asp:DynamicDataManager ID="DynamicDataManager1" runat="server" AutoLoadForeignKeys="true">
            <DataControls>
                <asp:DataControlReference ControlID="GridView1" />
            </DataControls>
        </asp:DynamicDataManager>--%>

         <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
               .............
            </ContentTemplate>
        </asp:UpdatePanel>


    Edit.aspx

    <%@ Page Language="C#" MasterPageFile="~/Site.master" CodeBehind="Edit.aspx.cs" Inherits="MarsUserManager.Edit" %>

    <asp:Content ID="headContent" ContentPlaceHolderID="head" Runat="Server">
    </asp:Content>

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
       
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:ValidationSummary ID="ValidationSummary1" runat="server" EnableClientScript="true"
                    HeaderText="List of validation errors" CssClass="DDValidator" />
                <asp:DomainValidator runat="server" ID="DetailsViewValidator" ControlToValidate="FormView1" Display="None" CssClass="DDValidator" />

                <asp:FormView runat="server" ID="FormView1" DataSourceID="DetailsDataSource" DefaultMode="Edit"
                    OnItemCommand="FormView1_ItemCommand"
                    OnItemUpdated="FormView1_ItemUpdated" RenderOuterTable="false"
                    onitemupdating="FormView1_ItemUpdating">

           </ContentTemplate>
        </asp:UpdatePanel>

     

    3 - What does the ExtractValues method do? Where does the dictionary comes from? What about the ConvertEditedValue method, what is it used for?

    In my case I had to comment out the line that uses the ConvertEditedValue and use my EncryptPassword method to convert the value the user types in the password text box. It is all working but I would like to understand why I had to it this way.

    protected override void ExtractValues(IOrderedDictionary dictionary)
    {
                //dictionary[Column.Name] = ConvertEditedValue(TextBox1.Text);
                dictionary[Column.Name] = MarsUserManager.Utils.Tools.EncryptPassword(TextBox1.Text);
    }

    4 - What does the FieldValueString method do? I had to comment this method out and use the FieldValue directly to set the value of the Literal control in my custom Field template. What is the difference between FieldValueString and FieldValue

    Literal1.Text = MarsUserManager.Utils.Tools.DecryptPassword((byte[])FieldValue).Replace("\0", "");

    //public override string FieldValueString
    //{
    //}

    I really appreciate your help.

    Cheers

    C

    Friday, February 17, 2012 10:27 AM
  • User1641955678 posted

    Glad you got it working!

    1. you need to redirect to the same page that the link from Default.aspx would take you. By default, it will be something like ~/YourTableName/List.aspx.

    2. I just checked and this works in the default DD site. e.g. after setting EnablePartialRendering="true", if I go the the List page and sort by clicking on a column header, it doesn't do a full post back. Does that scenario work for you? If not, you'd need to isolate the differences from the default site.

    3. Hard to know for sure without knowing in what way it wasn't working. ConvertEditedValue is a convenience method to deal with the ConvertEmptyStringToNull and NullDisplayText. You may not need it at all in your scenario.

    4. FieldValueString calls FormatFieldValue(FieldValue), which does its best to turn the object into a string. It looks at the various formatting options: DataFormatString, ConvertEmptyStringToNull, NullDisplayText, HtmlEncode. It probably doesn't work for your custom data type for the same reason that #3 doesn't work, as the two are more or less symmetrical. If you're happy with what's being displayed, you can bypass this method.

    Note that it might be better to start separate discussion for each topic, as it can become hard to track many different things in one thread. Technically, these 4 questions are not directly related to the use of a Custom Page :)

    David

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, February 17, 2012 12:45 PM
  • User1432255915 posted

    Hi David,

    Thanks for your reply.

    "1. you need to redirect to the same page that the link from Default.aspx would take you. By default, it will be something like ~/YourTableName/List.aspx."

    This worked like a charm - Response.Redirect("~/" + visibleTables[0].ToString()+"/List.aspx");

    "2. I just checked and this works in the default DD site. e.g. after setting EnablePartialRendering="true", if I go the the List page and sort by clicking on a column header, it doesn't do a full post back. Does that scenario work for you? If not, you'd need to isolate the differences from the default site."

    I have used the default DD template to create the site and if I go the the List page and sort by clicking on a column header, it doesn't do a full post back. It works for me as well.

    However, when I click on the Edit button inside the grid or the cancel or update button in the form view then it does a full post back to go to the Edit.aspx page.

    Ok, thanks for the hint. I will start separate discussion for the other topics.

     

    Monday, February 20, 2012 10:50 AM