Invalid viewstate error when posting back to same page - asp.net

I'm having some issues with an Invalid Viewstate error and I can understand why it's happening but I don't know how to fix it.
I have a page which is similar to this /story/?id=123 but I'm using a different page to Server.Transfer to this page.
So I've set up /info to Server.TransferRequest("/story/?id=123") and it works fine until the page does a postback to itself.
We have a login form on this page which simply reloads the page but when it does it seems to add /?id=123 onto the end of the URL so it ends up like this /info/?id=123 thus causing an Invalid Viewstate error.
I've already tried adding EnableViewStateMac="false" - this fixes the error but it doesn't log the user in as expected so it does not give the required result.
So my questions are:
Is there a better way to redirect to my page other than Server.TransferRequest but still keeping the nice URL? - I don't want to Response.Redirect if I can avoid it.
If not, is there an easy way to fix this error that doesn't require me adding EnableViewStateMac="false"?

http://support.microsoft.com/kb/316920
I believe that article will explain why you are having the problem and it gives a solution to fix it.
I know you don't want to use Response.Redirect, but I think that would also solve the problem.
PRB: "View State Is Invalid" Error Message When You Use Server.Transfer
This article was previously published under Q316920
Retired KB Content Disclaimer
This article was written about products for which Microsoft no longer
offers support. Therefore, this article is offered "as is" and will no
longer be updated.
SYMPTOMS
When you use HttpServerUtility.Transfer("page name", true), you
receive the following error message:
The View State is invalid for this page and might be corrupted
CAUSE
This problem occurs because the EnableViewStateMac attribute of the
<pages> element is set to true by default. When this attribute is
set to true, ASP.NET runs a message authentication check (MAC) on the
view state of the page when the page is posted back from the client.
This check determines if the view state of the page was modified on
the client. For security purposes, it is recommended that you keep
this attribute set to true.
When you call the Server.Transfer method and set the second
parameter to true, you preserve the QueryString and the Form
collections. One of the form fields is the hidden __VIEWSTATE form
field, which holds the view state for the page. The view state message
authentication check fails because the message authentication check
only checks each page. Therefore, the view state from the page that
calls Server.Transfer is not valid on the destination page.
View state is page scoped and is valid for that page only. View state
should not be transferred across pages.
RESOLUTION
To resolve this problem, use one of the following methods.
Resolution 1
Transfer values between pages to pass your server control values to
the other pages. For more information, refer to the following MSDN
documentation: Passing Server Control Values Between
Pages
This requires that you create public properties for each property of a
control that you want to access from the destination page.
If you have many controls, and if you want to access the properties of
these controls from another page, you can also declare those controls
as public variables. For example:
Page1.aspx
Public Class Page1
Public WithEvents TextBox1 As System.Web.UI.WebControls.TextBox
'Insert your code here.
End Class
Page2.aspx
Dim sourcePage As Page1
sourcePage = CType(Context.Handler, WebForm1)
Response.Write(sourcePage.TextBox1.Text)
Resolution 2
Do not pass the second parameter (which is false by default) when
you call Server.Transfer. For example:
Server.Transfer("<page name>")
This code does not send the QueryString and the Form fields to the
page that is called. When no data is transferred, ASP.NET does not run
the message authentication check.
MORE INFORMATION
Steps to Reproduce the Behavior
Create an .aspx page named WebForm1.aspx that transfers execution to another page. Add the following code to WebForm1.aspx:
<%# Page language="vb" AutoEventWireup="true" %>
<html>
<body>
<form id="WebForm1" method="post" runat="server">
<asp:TextBox id="txtName" runat="server">Your Name</asp:TextBox><br>
<asp:Button id="Button1" runat="server" Text="Submit" OnClick="Button1_Click"></asp:Button>
</form>
</body>
</html>
<script runat=server>
Sub Button1_Click(sender As Object, e As System.EventArgs)
Server.Transfer("WebForm2.aspx",true)
End Sub
</script>
Create another .aspx page named WebForm2.aspx, and then add the following code:
<%# Page language="vb" AutoEventWireup="true" %>
<html>
<body>
<form id="WebForm2" method="post" runat="server">
<asp:Label id="lblName" runat="server" >Web Form 2</asp:Label>
</form>
</body>
</html>
<script runat=server>
Sub Page_Load(sender As Object, e As EventArgs)
Dim thisPage As System.Web.UI.Page
Dim nameTextBox As TextBox
thisPage = CType(Context.Handler, System.Web.UI.Page)
nameTextBox = CType(thisPage.FindControl("txtName"), System.Web.UI.Control)
lblName.Text = "Your name is '" & nameTextBox.Text & "'."
End Sub
</script>
Open WebForm1.aspx in your browser, and then click Submit.

Related

How to do Code Behind for a User Control File

I have a dynamic menu that shows select items based on flags in my database. Currently I have the menu on my .aspx page and the code behind on my .aspx.vb page. I'm adding in alot more pages, so I'm trying to seperate the menu using a User Control. However, I'm not sure how to reference the code behind for the .ascx page.
Current code:
<ul>
<li id="form1" runat="server"><a title="a" href="a.aspx" target="_blank">A Form</a></li>
<li id="form2" runat="server"><a title="b" href="b.aspx" target="_blank">B Form</a></li>
</ul>
Code Behind:
Private Sub Customer_LoadData()
'DECLARE LOCAL VARIABLES
Dim objContract As Contract = Nothing
Dim objContractDL As New ContractDL
'RETRIEVE THE CUSTOMER'S CONTRACT INFORMATION
If objContractDL.Read(objContract, zintCustomerID) Then
Call DisplayCustomer(objContract)
Call DisplayForms(objContract)
End If
End Sub
Private Sub DisplayForms( _
ByVal objContract As Contract _
)
If (objContract.fieldFedLoansFlag = "0") Then
authform1.Visible = False
Else
authform1.Visible = True
End If
End Sub
I've never seen a .ascx.vb page before, so i don't think that is the solution.
From the comments above you are using the Page Directive and with Custom Controls you should be using the Control Directive
<%# Control Language="vb" AutoEventWireup="false" CodeFile="menu2.ascx.vb"
Inherits="menu2" %>
From the Microsoft site the difference between a user control and a page
A user controls differs from an ASP.NET Web page in these ways:
The file name extension for the user control is .ascx.
Instead of an # Page directive, the user control contains an # Control directive that defines configuration and other properties.
User controls cannot run as stand-alone files. Instead, you must add them to ASP.NET pages, as you would any control.
The user control does not have html, body, or form elements in it. These elements must be in the hosting page.

Caching a dynamically/programmatically added user control

I'm trying to learn about caching, and in particular, partial caching using controls.
My website is running slow on certain pages, so caching as much as possible will be helpful.
Having run a number of experiments from code I have found on SO and various other Google results, I am running into an issue with dynamically added controls.
I have set up a simple page, containing this code:
<%# Page Language="VB" Debug="true" %>
<%# Register TagPrefix="controls" TagName="control" Src="~/test/control.ascx" %>
<script runat="server">
Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Label2.Text = "Present Time: "
Label2.Text += DateTime.Now.ToString()
End Sub
</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<h2 style="color:Red">Output Caching</h2>
<asp:Label ID="Label2" runat="server"></asp:Label>
<controls:control ID='control1' runat='server' />
'------------------------------------------
<hr />
<div id='dyn2' runat='server' />
</div>
</form>
</body>
</html>
The control control.ascx looks like this:
<%# Control Language="VB" ClassName="control" %>
<%# OutputCache Duration="60" VaryByParam="r" %>
<script runat="server">
Sub Page_Load() Handles Me.Load
controlContent.InnerHtml = "Control time: " & DateTime.Now.ToString()
End Sub
</script>
<div id="controlContent" runat="server"></div>
This works well, and gives me a "live" time in the page, whilst the cached control shows me a time which is only updated after 60 seconds has passed, as per the OutputCache declaration.
I can see how I can use this for any application when I need to cache a part of a page and that part is explicitly entered into the page with a <controls> tag. The varyByParam option is useful to me too. (I've yet to investigate varyByCustom!)
However, in some cases I am loading a control into a page, programmatically based on specific needs.
In this case, I use code like this:
Dim theResult As test_control2 = CType(LoadControl("~\test\control2.ascx"), test_control2)
dyn2.Controls.Add(theResult)
This is programmatically adding my second test control, imaginatively entitled control2.ascx into the div with id "dyn2".
With no cache directive header in the control, or it's code-behind,l everything works fine, but I can't cache it (unless I cache the entire page).
However, if I add the cache header as per the control code above, I get this error:
Unable to cast object of type 'System.Web.UI.PartialCachingControl' to type 'test_control2'.
Googling doesn't seem to help me much with this, and investigating the PartialCachingControl types has lead me into further problems!
Can someone tell me what should be doing to enable me to cache these controls?
If it matters, I am coding in VB.net and also using .NET 2.0, so any advice on limitations on this platform would be appreciated too, if applicable.
Ah ha! Finally found another question on SO that helped
How to LoadControl a control that uses VaryByControl OutputCache, specifying values for properties
Basically, I was using the wrong Type when loading the control changin:
Dim theResult As test_control2 = CType(LoadControl("~\test\control2.ascx"), test_control2)
dyn2.Controls.Add(theResult)
to
Dim theResult As PartialCachingControl = DirectCast(LoadControl("~\test\control2.ascx"), PartialCachingControl)
dyn2.Controls.Add(theResult)
Sorted it!

ASP.NET TextBox (HTML input field) populates with username automatically when form loads

I have a TextBox control in a form which is still pulling in data when the HTML form renders. I tried setting the AutoCompleteType to "None" but I think that just controls whether or not it will find previously entered data for that field, not what actually fills into that input field when the page loads. Why would this textbox be pulling in data? It's causing another larger issue. This TextBox is inside of a control (*.ascx file). It's loaded from another control dynamically--not sure if that matters. It's only happening in Mozilla Firefox. When I check txtKeywords.Text in the Page_Load event of the control that contains the TextBox, the value is null. So the value is obviously coming from the browser, not the server. What would cause this??
<asp:TextBox id="txtKeywords" runat="server" Width="125px" AutoCompleteType="None" autocomplete="False"></asp:TextBox>
Rendered HTML:
<input type="text" style="width: 125px;" autocomplete="False" id="ExplorerPageHtmlLeft_ctl01_txtKeywords" name="ExplorerPageHtmlLeft$ctl01$txtKeywords" gtbfieldid="77">
Parent control code behind (searchPanel1 control contains the TextBox):
Private Sub Page_Load(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles MyBase.Load
navigation1 = CType(LoadControl("ExplorerNavigation1.ascx"), ExplorerNavigation1)
searchPanel1 = CType(LoadControl("SearchPanel.ascx"), SearchPanel)
navigation2 = CType(LoadControl("ExplorerNavigation2.ascx"), ExplorerNavigation2)
Me.PlaceHolder1.Controls.Add(navigation1)
Me.PlaceHolder1.Controls.Add(searchPanel1)
Me.PlaceHolder1.Controls.Add(navigation2)
End Sub
I couldn't find any suspicious JavaScript yet, after Massimiliano Peluso's comment. However, I did notice that the value in this input field is ALWAYS the same as the user's id. The value of the "gtbfieldid" attribute on the TxtUser field always seems to change when I inspect the element in Firebug. This TxtUser field is always different from our txtKeywords input field. They are on different pages. And they seem to populate with different values each time. So I didn't see a correlation there. I still don't know what this attribute is for. This attribute doesn't show in Internet Explorer, so it must be inserted into Firefox for some reason.
<input type="text" id="TxtUser" name="TxtUser" gtbfieldid="37">
Thanks to Johnny #5's input, we proved that the server was not setting this text. This is not the answer to my question, but this was very helpful. I put a breakpoint into the setter, and it did not get called. Only the getter got called. To clarify things, this is a custom web server control (.vb file), NOT TO BE CONFUSED with a custom user control (.ascx file, *.ascx.vb file and sometimes *.ascx.Designer.vb file). I added Render event to this class. I also had to import 2 namespaces. The next thing I'd like to figure out is a way to be able to extend the browser's ability to set text in this field (via client side), like we did below for the server side. That would be a frickin' awesome methodology for this type of problem. Not sure if Firebug has that capability though. I still don't know what's setting this text. So my question is still up for grabs.
Imports System.Web
Imports System.Web.UI.WebControls
Public Class MyTextBox
Inherits System.Web.UI.WebControls.TextBox
Public Overrides Property Text() As String
Get
Return MyBase.Text
End Get
Set(ByVal value As String)
MyBase.Text = value
End Set
End Property
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Render(writer)
End Sub
End Class
In my page where I'm implementing this custom web server control, I need to register this class.
<%# Register Assembly="Acu.LIMS.UI.Web" Namespace="Acu.LIMS.UI.Web" TagPrefix="MyTextBox" %>
<MyTextBox:MyTextBox id="txtKeywords" runat="server" Width="125px" AutoCompleteType="None" AutoComplete="False"></MyTextBox:MyTextBox>
Well, I'm not crazy! It's not server side code and it's not client side code causing this. It's Mozilla Firefox's > Options > Security > Remember password checkbox causing the problem. BUT I'M STILL CONFUSED. :-\
On the computer causing the issue, I kept toggling this checkbox, and could not get the browser to ask me the question, "Do you want Firefox to remember your password?" (don't remember the exact wording). But on another computer, I got asked immediately. And when I clicked "Remember" and went to that page, "x" shows in this input field (aka TextBox web control). Both my user name and password are both "x", so I'm not sure which it is pulling. Despite the value it pulls, my culprit TextBox doesn't have either id (of the user id or password) as it's id. Next question: How does Firefox determine when to fill in the username and password? One interesting thing about this form is that another field does have the password textbox, but I DO NOT have the user id in this form. I wonder if Firefox puts the user name in the first field it finds??
=======================
Answer
So I put in a dummy field in the SearchPanel control, and guess where my user name filled in. I was reading on this link about the Mozilla Password Manager, and apparently Mozilla uses its intelligence to determine the best place to put the user id (sometimes referred to as user name). In my case, I have a label for the user name (not the user id) in my Authentication control, but I don't want a user id field. Since the user is already logged in, they shouldn't be entering any user id. So that's why the user name label is in front of the password field. We audit changes in our application, so that is the purpose for having it inside the application.
https://wiki.mozilla.org/Firefox%3aPassword_Manager
So I did a test to validate Mozilla's functionality. I put another dummy field after the older culprit TextField (with id "txtKeywords", so that it's placement is the last input field preceding the password input field. And guess where Mozilla Firefox put the user id? Yep, in the dummy TextBox/input field. When I turn off the "Remember password for sites" option under Mozilla > Options > Security, it doesn't populate, nor does my password field. So this is my answer. I guess for now, I'll just put a hidden TextBox field in my Authentication control so Mozilla populates that one instead, but is not visible.
SearchPanel.ascx control:
<tr>
<td nowrap class="categoryverticalNav">
<asp:Label id="lblSearch" runat="server">Search</asp:Label><BR>
<asp:DropDownList id="ddlCategory" runat="server" Width="164px"></asp:DropDownList><BR>
<asp:TextBox id="txtKeywords" runat="server" Width="125px" AutoCompleteType="None" AutoComplete="False"></asp:TextBox>
<asp:TextBox id="TextBox1" runat="server" Width="125px" AutoCompleteType="None" AutoComplete="False"></asp:TextBox>
<asp:Button id="btnGo" runat="server" Width="28px" Text="Go" CausesValidation="False" />
</td>
</tr>
Yep, putting in a hidden input field solved it. I suppose I could have either put in a TextBox also, but I just chose an input field with runat="server".. since the CLR doesn't need to interact with it in ASP.NET. Once I did that, the "x" (user id) disappeared from the SearchPanel control txtKeywords input field. :-)
<td>
<b><asp:Label ID="LblUserDisplayName" Runat="server" EnableViewState="False"></asp:Label></b>
</td>
<td>Password</td>
<td>
<input runat="server" type="text" style="display:none;">
<asp:TextBox id="TxtPassword" Runat="server" Width="80" EnableViewState="False" TextMode="Password"></asp:TextBox>
</td>
Simple solution: set autocomplete property of text box to "off"
<asp:TextBox id="TxtPassword" Runat="server" Width="80" autocomplete="off" TextMode="Password"></asp:TextBox>
In the ASP.NET life cycle, there are two events which are responsible for the maintain data after postback. One is LoadViewState and another is LoadPostBackData. If a control which implement IPostBackDataHandler interface gets loaded by the values from Http Post data in the LoadPostBackData event. A TextBox control does not get its value from the view state but from the post data in the form in this event. So even if you disable view state for the TextBox control, it can get its value from the HTTP post data if it implements IPostBackDataHandler interface.
If Page.IsPostBack(){
yourTextBox.Text=string.empty;
}
http://forums.asp.net/t/1104194.aspx/1
have also a look at the below:
http://www.codeproject.com/KB/aspnet/ASPViewStateandPostBack.aspx
If you want to find if the text is setted on server side, you could try this :
public class MyTextBox : System.Web.UI.WebControls.TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Use this text box inside your user control instead of the original one, then put a break point on the setter, and check the call stack when you got there.
MacGyver's Notes:
Adding some clarification. This does NOT answer my question, but was VERY useful in proving that the text was not getting set on the server side. This is a custom web server control (.vb file), not to be confused with a custom user control (.ascx file, *.ascx.vb file and sometimes *.ascx.Designer.vb file). I added Render event to this class. I also had to import 2 namespaces.
Imports System.Web
Imports System.Web.UI.WebControls
Public Class MyTextBox
Inherits System.Web.UI.WebControls.TextBox
Public Overrides Property Text() As String
Get
Return MyBase.Text
End Get
Set(ByVal value As String)
MyBase.Text = value
End Set
End Property
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Render(writer)
End Sub
End Class
In my page where I'm implementing this custom web server control, I need to register this class.
<%# Register Assembly="Acu.LIMS.UI.Web" Namespace="Acu.LIMS.UI.Web" TagPrefix="MyTextBox" %>
<MyTextBox:MyTextBox id="txtKeywords" runat="server" Width="125px" AutoCompleteType="None" AutoComplete="False"></MyTextBox:MyTextBox>
See the bottom of my question for the Answer. It's all explained. Essentially Mozilla Firefox Password Manager places the user id into the last input field preceding the stored input field (based on what's stored in the Mozilla Firefox Password Manager). So I'm just going to place a hidden dummy textbox (or input) field inside my Authentication control that precedes my password input field.
This article has one quote that sort of explains it, but I think I have a better explanation.
https://wiki.mozilla.org/Firefox%3aPassword_Manager
Quote from link above:
"Then uses the usernamefield/passwordfield values as hints to find the appropriate elements within a webpage by matching them to the "name" attribute."
Copy & paste below code into your page head section.
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script type="text/jscript">
$(document).ready(function () {
setTimeout(function () {
$("input[type='password'],input[type='text']").val('');
}, 50);
});
</script>

Form tag on ASP.net page

I have a web application that has a page that loads the content from the database. I want to be able to put a form in the dynamic content, but .net doesn't let the inside form perform it's action. Is there a way to allow this or some other way I can get a form on a dynamic content page?
--EDIT--
I think I need to clarify something. This is an aspx page that loads content from the database. As far as I know, the text I pull from the db and stick in the Label is never compiled or processed by the .net wp, thus I can't use the code behind to fix this issue.
This is a common problem, when you want to have a non-postback form to a 3rd party site (like a PayPal button, for example).
The problem occurs because HTML doesn't let you have form within a form, and most ASP.NET pages have a <form runat="server" /> "high up" in the HTML (or in the Master page).
My favorite solution is to hide the "high up" form tag, while still showing all of the content. Then you can feel free to dump any tags you want in the body. If you do this dynamically you can choose on a page-by-page basis which pages have custom forms.
I created a class called GhostForm.cs to handle this. You can read all about it here:
http://jerschneid.blogspot.com/2007/03/hide-form-tag-but-leave-content.html
There can only be one form on the page (the asp form); you have to use that form somehow.
To clarify, there can only be one form processed.
Not with webforms, no. You have to work within the one, full page form by using an event handler connected to a Button to LinkButton. Fortunately, it's pretty easy to do:
foo.aspx:
...
<asp:TextBox id="txtFoo" runat="server" />
<asp:Button id="btnFoo" runat="server" onclick="btnFoo_Click />
...
foo.aspx.cs:
...
protected void btnFoo_Click(object sender, EventArgs e)
{
string s = txtFoo.Text;
// do something with s
}
...
Dino Esposito has an article from MSDN magazine that covers handling multiple forms or "simulating" sub forms in ASP.Net that might just answer all your questions.
http://msdn.microsoft.com/en-us/magazine/cc164151.aspx
Any work around would be hacky and very ugly. By design asp.net uses a form tag to post and get data. This is why they call it a Web Forms Application. Html does not allow nested forms. What you want to do is use a WebRequest in your code behind.
If you are trying something like a paypal button you could simply use something like this.
Markup:
<div id="PayPalButtonContainer" runat="server"></div>
Code Behind:
public static string GetPayPalButtonMarkup()
{
const string markup = #"https://www.paypal.com/cgi-bin/webscr
?cmd=_xclick&business={0}
&item_name=Widget
&amount={1}
&currency_code=USD";
return markup;
}
PayPalButtonContainer.InnerHtml = string.format(GetPayPalButtonMarkup,"YOUR PAYPAL USER NAME", "YOUR PRICE VALUE");
you either have to deal with the postback by adding a server side click event handler to what you want to be the "sub forms" submit button (this is how web formas deals with multiple submit type buutons on the same page) or do soemthing clever with AJAX if you dont want a full post back
I've run across this issue before. One workaround that I have done is to place my code that I want my action to be done upon inside of an asp:Panel. With the panel you can set the attribute of "DefaultButton" to a button inside of the panel, and clicking the button (or pressing "enter") will fire that button's click event. I've found this quite handy when wanting to submit a "form" by pressing enter when I have a master page that contains the only allowable asp:Form.
Hope this helps.
When I first came across this problem, I found the simplest solution for me was to simple COPY and PASTE the Master page and give it a slightly different name, something like:
SiteNameMasterPage 'Default page with FORM tag
SiteNameMasterPageNF 'No Form tag
And then depending on wether I wanted a FORM tag or or not, simply change the masterpage link at the top of my CONTENT-PAGES, like this
<%# Page Title="" Language="VB" MasterPageFile="~/SiteName.master" %>
<%# MasterType VirtualPath="~/SiteName.master" %>
<!-- This masterpage has the default FORM tag -->
or
<%# Page Title="" Language="VB" MasterPageFile="~/SiteNameNF.master" %>
<%# MasterType VirtualPath="~/SiteNameNF.master" %>
<!-- This masterpage does NOT have the default FORM tag -->
and then in the content page, wherever I want to place my form I can include the <form> tag

FormView on a Master Page can't see databound controls through ContentPlaceHolder boundary

I have a number of similarly structured FormViews. In an effort to avoid duplicate markup, I've created a master page that contains the FormView, and placed a ContentPlaceHolder inside the FormView. The specific databound controls - which are the only thing that change from page to page - are then on the page that uses that master page.
So I have a master page that looks something like this:
<%# master ... %>
...
<form runat=server>
...
<asp:formview runat="server" ... >
<edititemtemplate>
... Lots of common markup ...
<asp:contentplaceholder id='FormRows' runat='server' />
... Lots more common markup ...
</edititemtemplate>
</asp:formview>
...
</form>
and a page using that master page that looks something like this:
<%# page masterpagefile="Form.Master" ... %>
<asp:content contentplaceholderid="FormRows" runat="server" >
...
<p>
Field One:
<asp:textbox runat=server text='<%#Bind("Field1")%>' id='Field1' />
</p>
<p>
Field Two:
<asp:textbox runat=server text='<%#Bind("Field2")%>' id='Field2' />
</p>
...
</asp:content>
With an existing record, the FormView sees through to the databound controls (Field1, etc) and populates them with the correct data. But when inserting or updating, it doesn't see them, and they're not included in the insert or update. In the FormView_ItemInserting event, e.Values is empty; likewise in the FormView_ItemUpdating event, e.NewValues is empty.
So:
Is there a way to provoke the FormView on the master page to see through to the databound controls inside the ContentPlaceholder?
Failing that, is there a straightforward way of identifying controls that are databound with <%#Bind(...)%> so that I can add them manually to the values bag?
There are a couple of things that come to mind why this setup will not work and may lead to more code than markup.
If you have a datasource defined in the master page it will not handle the different data bound controls from each page without adding more logic to the master page to change the query etc.
All form views will be coupled together increasing the complexity of changes down the road
I would go with separate pages for each FormView reducing the complexity of code, debugging and the ability to change
Just my two cents
I think this will prove difficult, if not possible; in fact I'm surprised that the databinding works at all!
You may want to try a different method of encapsulating your FormView control.
You could try placing the FormView control in an .ascx control with a PlaceHolder where you now have the ContentPlaceHolder.
Then on each ASPX page, you could have a mirror ASCX page that contains the filler for the placeholder. You could give them the same names (Page1.aspx uses Page1.ascx) or set up a naming convention like Page1-Content.ascx, so that your FormView ascx would figure out what it's filler control is named, use Page.LoadControl() to load the control by path, and plug that content in during the Init phase.
Now, your content controls have the advantage of being able to have public properties, so you could bind to those public properties, and have the properties shuttle the data to and from the appropriate server controls in the filler .ascx file.
Unfortunately it's double the files (because of the ASPX and ASCX required for each page) but fairly work-unintensive compared to the alternative (duplicating all that code)
Of course, you haven't told us what all your common markup is, but your common markup could go into a CommonMarkupHeader.ascx and CommonMarkupFooter.ascx as well and included on each page's unique FormView.
Where do you have server form tag? May be in content place holder insted of master page, so your values not send to server page after submit
You might be able to do something like this...
Define an interface for your "data pages" that has a method signature that returns a bindable data source..
public interface IFormViewChild {
IEnumerable GetFormData();
}
Then you can have your "data pages" implement that interface...
public class ChildDataPage : Page, IDataPage {
public IEnumerable GetFormData() {
// code to return stuff here
}
}
Finally, in your masterpage's Load() event...
if (Page is IFormViewChild) {
myFormViewControl.DataSource = ((IFormViewChild)Page).GetFormData();
myFormViewControl.DataBind();
}
Please keep in mind that this is all psudo code typed directly into this web form editor.. so it's probably wrong. But it might not be :)
Here's a provisional solution - not elegant, but it works. In the code-behind for Form.Master I have something along these lines:
Private Sub FormView1_ItemInserting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.FormViewInsertEventArgs) Handles FormView1.ItemInserting
ManuallyAddValues(e.Values)
End Sub
Private Sub FormView1_ItemUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.FormViewUpdateEventArgs) Handles FormView1.ItemUpdating
ManuallyAddValues(e.NewValues)
End Sub
Private Sub ManuallyAddValues(ByRef Values As IOrderedDictionary)
For Each Field As Core.Field In FormView1.DataSourceControl.IncludedFields
If Values(Field.Name) Is Nothing Then
Dim DataboundControl As Control = FormView1.FindControl("FormRows").FindControl(Field.Name)
Values.Add(Field.Name, GetValue(DataboundControl))
End If
Next
End Sub
This isn't so elegant because
I have to know the names of all databound controls
This relies on the assumption that the ID of each control matches the fieldname
The 'GetValue' function (not included here) is a clumsy solution: it checks for various types (textbox, dropdownlist, checkbox, etc.) and gets the string value from the appropriate property (textbox.text, dropdownlist.selectedvalue, checkbox.checked, etc.).
I'd still love to at least have a way of knowing what's bound with the '<%#Bind("Foo")%>' syntax and getting that information directly.

Resources