InsertItemTemplate and ControlParameter

N

npei72

I have a simple Formview like this:

<%@ Page Language="C#" EnableViewState="true" AutoEventWireup="true"
CodeFile="HelloWorld.aspx.cs" Inherits="_HelloWorld"%>

<html>
<body>
<form runat="server">
<asp:FormView DataSourceID="myODS"
ID="ItemFormView"
runat="server">
<InsertItemTemplate>
<asp:DropDownList ID="myDDL" runat="server"></
asp:DropDownList>
</InsertItemTemplate>
</asp:FormView>

<asp:ObjectDataSource ID="myODS" runat="server" >
<SelectParameters>
<asp:ControlParameter ControlID="faa" />
</SelectParameters>
</asp:ObjectDataSource>

<asp:TextBox ID="faa" runat="server" visible="false"></
asp:TextBox>
</form>

</body>
</html>

and a codebehind file

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class _HelloWorld : System.Web.UI.Page
{

protected void Page_Load(object sender, EventArgs e)
{
ItemFormView.ChangeMode(FormViewMode.Insert);
if (!IsPostBack)
PopulateDDL();
}

private void PopulateDDL()
{
DropDownList ddl =
(DropDownList)ItemFormView.FindControl("myDDL");

ddl.Items.Add("aaa");
ddl.Items.Add("bbb");
ddl.Items.Add("ccc");
}

}


When I run this page, the dropdown list is expected to be populated,
but it's not. However, if I remove the line

<asp:ControlParameter ControlID="faa" />

, it then works. It also works perfectly if using other type of
parameter, like

<asp:parameter Name="woo"/>

So, I don't understand what happen behind the scene. Any idea???

Thanks in advance!

Nate
 
G

Guest

Hi there,

I had a look at the System.Web.UI.WebControls code, and it seems there’s a
tiny *bug* - ConvertEmptyStringToNull is not used in
ControlParameter.Evaluate() method. Let me explain the problem in details.
Every Parameter in ObjectDataSource.SelectParameters collection inherits from
System.Web.UI.WebControls.Parameter class, which defines virtual method
protected virtual object Evaluate(HttpContext context, Control control)
{
Return null;
}
As you can see default implementation returns null reference, but every
derived parameter class overrides this method with its own implementation
(this mechanism is called polymorphism). For instance, ControlParameter class
provides its own, following implementation:

protected override object Evaluate(HttpContext context, Control control)
{
if (control == null)
{
return null;
}
string text1 = this.ControlID;
string text2 = this.PropertyName;
if (text1.Length == 0)
{
throw new
ArgumentException(SR.GetString("ControlParameter_ControlIDNotSpecified", new
object[] { base.Name }));
}
Control control1 = DataBoundControlHelper.FindControl(control, text1);
if (control1 == null)
{
throw new
InvalidOperationException(SR.GetString("ControlParameter_CouldNotFindControl", new object[] { text1, base.Name }));
}
ControlValuePropertyAttribute attribute1 =
(ControlValuePropertyAttribute)
TypeDescriptor.GetAttributes(control1)[typeof(ControlValuePropertyAttribute)];
if (text2.Length == 0)
{
if ((attribute1 == null) || string.IsNullOrEmpty(attribute1.Name))
{
throw new
InvalidOperationException(SR.GetString("ControlParameter_PropertyNameNotSpecified", new object[] { text1, base.Name }));
}
text2 = attribute1.Name;
}
object obj1 = DataBinder.Eval(control1, text2);
if (((attribute1 != null) && string.Equals(attribute1.Name, text2,
StringComparison.OrdinalIgnoreCase)) && ((attribute1.DefaultValue != null) &&
attribute1.DefaultValue.Equals(obj1)))
{
return null;
}
return obj1;
}

A lot of code but nothing really interesting – the only thing missing is use
of ConvertEmptyStringToNull. However, the problem shows up somwehere else -
in ParameterCollection class. As you may know every BaseDataBoundControl
(including FormView) populates the data on prerender event. Before doing so,
control performs a simple check to see if the data needs to be (re)populated
(actually this task is delegated to SelectParameters member which is an
instance of ParameterCollection class, control is notified by handling
ParametersChanged event which occurrence indicates data should be retrieved
again):

public void ParameterCollection.UpdateValues(HttpContext context, Control
control)
{
foreach (Parameter parameter1 in base)
{
parameter1.UpdateValue(context, control);
}
}

And here comes the problem:

internal void Parameter.UpdateValue(HttpContext context, Control control)
{
object originalValue = this.ViewState["ParameterValue"];
object evaluatedValue = this.Evaluate(context, control);
this.ViewState["ParameterValue"] = obj2;
if (((evaluatedValue == null) && (originalValue!= null)) ||
((evaluatedValue!= null) && ! evaluatedValue.Equals(originalValue)))
{
this.OnParameterChanged();
}
}

In your case, originalValue is null, evaluatedValue is String.Empty
therefore parameter is considered as changed (TextBox.Text never returns null
reference but String.Empty, unfortunately, as I mentioned,
ControlPatameter.Eval() method does not replace empty string with null value
even if ConvertEmptyStringToNull == true. It doesn’t happen for Parameter
class because Parameter.Eval() always returns null, so the condition
if (((evaluatedValue == null) && (originalValue!= null)) ||
((evaluatedValue!= null) && ! evaluatedValue.Equals(originalValue)))
{
this.OnParameterChanged();
}
is never met.

There are two easy ways to solve the problem.
1. Do not use Page_Load event to popuate the dropdownlist, use pre_render:

protected void Page_PreRender(object sender, EventArgs e)
{
if (!IsPostBack)
PopulateDDL();
}

2. Create your own parameter class derived from ControlParameter

public class MyControlParameter : ControlParameter
{

protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}

protected override object Evaluate(HttpContext context, Control control)
{
object value = base.Evaluate(context, control);

if (value is String)
{
if (ConvertEmptyStringToNull && ((string)value) == String.Empty)
return null;
else
return value;
}
else
return value;
}
}


<asp:FormView ID="ItemFormView" DataSourceID="myODS" runat="server"
DefaultMode="Insert">
<InsertItemTemplate>
<asp:DropDownList ID="myDDL" runat="server"/>
</InsertItemTemplate>
</asp:FormView>

<asp:ObjectDataSource ID="myODS" runat="server">
<SelectParameters>
<cc1:MyControlParameter ControlID="faa" PropertyName="Text"/>
</SelectParameters>
</asp:ObjectDataSource>

<asp:TextBox ID="faa" runat="server" visible="true"/>
 
N

npei72

Hi there,

I had a look at the System.Web.UI.WebControls code, and it seems there's a
tiny *bug* - ConvertEmptyStringToNull is not used in
ControlParameter.Evaluate() method. Let me explain the problem in details.
Every Parameter in ObjectDataSource.SelectParameters collection inherits from
System.Web.UI.WebControls.Parameter class, which defines virtual method
protected virtual object Evaluate(HttpContext context, Control control)
{
Return null;}

As you can see default implementation returns null reference, but every
derived parameter class overrides this method with its own implementation
(this mechanism is called polymorphism). For instance, ControlParameter class
provides its own, following implementation:

protected override object Evaluate(HttpContext context, Control control)
{
if (control == null)
{
return null;
}
string text1 = this.ControlID;
string text2 = this.PropertyName;
if (text1.Length == 0)
{
throw new
ArgumentException(SR.GetString("ControlParameter_ControlIDNotSpecified", new
object[] { base.Name }));
}
Control control1 = DataBoundControlHelper.FindControl(control, text1);
if (control1 == null)
{
throw new
InvalidOperationException(SR.GetString("ControlParameter_CouldNotFindContro­l", new object[] { text1, base.Name }));
}
ControlValuePropertyAttribute attribute1 =
(ControlValuePropertyAttribute)
TypeDescriptor.GetAttributes(control1)[typeof(ControlValuePropertyAttribute­)];
if (text2.Length == 0)
{
if ((attribute1 == null) || string.IsNullOrEmpty(attribute1.Name))
{
throw new
InvalidOperationException(SR.GetString("ControlParameter_PropertyNameNotSpe­cified", new object[] { text1, base.Name }));
}
text2 = attribute1.Name;
}
object obj1 = DataBinder.Eval(control1, text2);
if (((attribute1 != null) && string.Equals(attribute1.Name, text2,
StringComparison.OrdinalIgnoreCase)) && ((attribute1.DefaultValue != null) &&
attribute1.DefaultValue.Equals(obj1)))
{
return null;
}
return obj1;

}

A lot of code but nothing really interesting - the only thing missing is use
of ConvertEmptyStringToNull. However, the problem shows up somwehere else -
in ParameterCollection class. As you may know every BaseDataBoundControl
(including FormView) populates the data on prerender event. Before doing so,
control performs a simple check to see if the data needs to be (re)populated
(actually this task is delegated to SelectParameters member which is an
instance of ParameterCollection class, control is notified by handling
ParametersChanged event which occurrence indicates data should be retrieved
again):

public void ParameterCollection.UpdateValues(HttpContext context, Control
control)
{
foreach (Parameter parameter1 in base)
{
parameter1.UpdateValue(context, control);
}

}

And here comes the problem:

internal void Parameter.UpdateValue(HttpContext context, Control control)
{
object originalValue = this.ViewState["ParameterValue"];
object evaluatedValue = this.Evaluate(context, control);
this.ViewState["ParameterValue"] = obj2;
if (((evaluatedValue == null) && (originalValue!= null)) ||
((evaluatedValue!= null) && ! evaluatedValue.Equals(originalValue)))
{
this.OnParameterChanged();
}

}

In your case, originalValue is null, evaluatedValue is String.Empty
therefore parameter is considered as changed (TextBox.Text never returns null
reference but String.Empty, unfortunately, as I mentioned,
ControlPatameter.Eval() method does not replace empty string with null value
even if ConvertEmptyStringToNull == true. It doesn't happen for Parameter
class because Parameter.Eval() always returns null, so the condition
if (((evaluatedValue == null) && (originalValue!= null)) ||
((evaluatedValue!= null) && ! evaluatedValue.Equals(originalValue)))
{
this.OnParameterChanged();
}
is never met.

There are two easy ways to solve the problem.
1. Do not use Page_Load event to popuate the dropdownlist, use pre_render:

protected void Page_PreRender(object sender, EventArgs e)
{
if (!IsPostBack)
PopulateDDL();

}

2. Create your own parameter class derived from ControlParameter

public class MyControlParameter : ControlParameter
{

protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}

protected override object Evaluate(HttpContext context, Control control)
{
object value = base.Evaluate(context, control);

if (value is String)
{
if (ConvertEmptyStringToNull && ((string)value) == String.Empty)
return null;
else
return value;
}
else
return value;
}

}

<asp:FormView ID="ItemFormView" DataSourceID="myODS" runat="server"
DefaultMode="Insert">
<InsertItemTemplate>
<asp:DropDownList ID="myDDL" runat="server"/>
</InsertItemTemplate>
</asp:FormView>

<asp:ObjectDataSource ID="myODS" runat="server">
<SelectParameters>
<cc1:MyControlParameter ControlID="faa" PropertyName="Text"/>
</SelectParameters>
</asp:ObjectDataSource>

<asp:TextBox ID="faa" runat="server" visible="true"/>
--
Milosz



I have a simple Formview like this:
<%@ Page Language="C#" EnableViewState="true" AutoEventWireup="true"
CodeFile="HelloWorld.aspx.cs" Inherits="_HelloWorld"%>
<html>
<body>
<form runat="server">
<asp:FormView DataSourceID="myODS"
ID="ItemFormView"
runat="server">
<InsertItemTemplate>
<asp:DropDownList ID="myDDL" runat="server"></
asp:DropDownList>
</InsertItemTemplate>
</asp:FormView>
<asp:ObjectDataSource ID="myODS" runat="server" >
<SelectParameters>
<asp:ControlParameter ControlID="faa" />
</SelectParameters>
</asp:ObjectDataSource>
<asp:TextBox ID="faa" runat="server" visible="false"></
asp:TextBox>
</form>

and a codebehind file
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _HelloWorld : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ItemFormView.ChangeMode(FormViewMode.Insert);
if (!IsPostBack)
PopulateDDL();
}
private void PopulateDDL()
{
DropDownList ddl =
(DropDownList)ItemFormView.FindControl("myDDL");


When I run this page, the dropdown list is expected to be populated,
but it's not. However, if I remove the line
<asp:ControlParameter ControlID="faa" />
, it then works. It also works perfectly if using other type of
parameter, like
<asp:parameter Name="woo"/>
So, I don't understand what happen behind the scene. Any idea???
Thanks in advance!
Nate- Hide quoted text -

- Show quoted text -

That explains!

Thanks Milosz.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top