Validator Disappears on PostBack Inside Composite Control - asp.net

This should be a simple problem to fix, as it uses the same way I fixed my last problem with FooControl (below).
Basically, I want to add a derived validator I made to this composite control. It works fine but on postback it just disappears in the markup, making me think it's lost its ViewState.
I am probably doing something wrong with instantiating it, but I've tried setting only the ControlToValidate, moving things around, and nothing works.
I've provided some surrounding code to see what's working and then what's not.
Private FooControl As IFooControl
Private Validator As MyValidator
Protected Overrides Sub CreateChildControls()
FooControl = FooControlProvider.CreateFooControl(blah)
Me.Controls.Add(FooControl.RetrieveControl())
' Begin Not Working
Validator = New MyValidator()
Me.Controls.Add(Validator)
Validator.ID = "MyValidatorID"
Validator.ControlToValidate = FooControl.ID
Validator.IsRequired = True ' Custom property
Validator.ErrorMessage = "Please select an answer"
' End Not Working
If Not DataSource Is Nothing Then
FooControlProvider.AssignDataSource(DataSource, FooControl)
End If
End Sub

I've found the problem. Apparently, on PostBack, no matter how you order the creation of the Validator, its display is set to None. I found this by breaking on the Render method and checking the Validator variable.
The solution (hack?) is to set the validator to your desired Display during the Render method.
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
Validator.Display = ValidatorDisplay.Dynamic
MyBase.Render(writer)
End Sub

Related

Setting ID twice caused LinkButton's click event to only fire after two clicks

I had a situation where I was dynamically creating LinkButtons basically with no properties set on PageInit, and then later on adding the properties about those LinkButtons on another separate button's click event.(i.e. text, adding a click eventhandler, etc).
The problem was, I had to click the LinkButton twice for the click event handler to fire. Bear in mind, all of this is inside an update panel.
After looking at it up and down, I realized I was setting the ID for it to the same thing twice (on PageInit and also when I later set the properties). I saw that and figured it would muck up things in the control hierarchy and I understand it was the problem...but what I don't fully understand is the why.
Can someone explain to me what the technical cause was for having to click the LinkButton twice and why setting the ID to same thing twice caused this?
CODE
This occurs on CreateChildControls()
Private Sub InitializeLinkBreadCrumbPlaceHolders()
Dim counter As Integer = 0
'Adding the handlers has to take place before/on Page.Init...
For counter = 0 To LEVEL_CAP
_linkDynamic = New LinkButton()
'Add all the links
Me._placeHolder.Controls.Add(_linkDynamic)
With _linkDynamic
AddHandler .Click, AddressOf Link_Click
.Style.Add("display", "none")
.ID = String.Format("lbl{0}", counter)
End With
Next
End Sub
And this occurs when a regular button is pressed (keep in mind all of this is inside an update panel)
Private Sub SetHyperLinkBreadCrumbValues(Optional ByVal ShouldAddAsLink As Boolean = True)
'Don't add a new link if we went backwards
If ShouldAddAsLink Then
Me.Links(Me.CurrentLevel) = Me.LinkHeader
End If
'Go through the collection to set the values of the existing linkbuttons
For Each element As DictionaryEntry In Me.Links
'Links 1-based index
With CType(Me._placeHolder.Controls.Item(CInt(element.Key) - 1), LinkButton)
.Font.Name = "Arial"
.Font.Size = 11
If CInt(element.Key) > 1 Then
.Text = String.Format(" > {0}", CStr(element.Value))
Else
.Text = CStr(element.Value)
End If
.Visible = True
.Style.Add("display", "inline")
End With
Me.TrimDescriptionLink(CType(Me._placeHolder.Controls.Item(CInt(element.Key) - 1), LinkButton))
Next
End Sub
I have seen (and even caused) this behavior from time to time. Inevitably you are not always adding the control (or wiring up the OnClick event in PageInit. The first click causes a PostBack and on the second server page life cycle, you add the control during PageInit. On that second time around (and second button click), the event is wired up and fires the correct event. Are you possibly not wiring up the OnClick event every time during PageInit ?
Can you share some code?

ASP.NET Custom Control - Template Allowing Literal Content

I want my User Control to be able to have Literal Content inside of it. For Example:
<fc:Text runat="server">Please enter your login information:</fc:Text>
Currently the code for my user control is:
<ParseChildren(True, "Content")> _
Partial Public Class ctrFormText
Inherits UserControl
Private _content As ArrayList
<PersistenceMode(PersistenceMode.InnerDefaultProperty), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
TemplateInstance(TemplateInstance.Single)> _
Public Property Content() As ArrayList
Get
If _content Is Nothing Then
Return New ArrayList
End If
Return _content
End Get
Set(ByVal value As ArrayList)
_content = value
End Set
End Property
Protected Overrides Sub CreateChildControls()
If _content IsNot Nothing Then
ctrChildren.Controls.Clear()
For Each i As Control In _content
ctrChildren.Controls.Add(i)
Next
End If
MyBase.CreateChildControls()
End Sub
End Class
And when I put text inside this control (like above) i get this error:
Parser Error Message: Literal content ('Please enter your login information to access CKMS:') is not allowed within a 'System.Collections.ArrayList'.
This control could have other content than just the text, so making the Content property an attribute will not solve my problem.
I found in some places that I need to implement a ControlBuilder Class, along with another class that implements IParserAccessor.
Anyway I just want my default "Content" property to have all types of controls allowed in it, both literal and actual controls.
You need to set the ParseChildren attribute to "False", otherwise any content in your control will be parsed into the "Content" property. This does not meet your needs, as you want to have controls AND content.
In order to accomplish this, override the AddParsedSubObject method. Check for the parsed control type, and if it's a literal control, add it to your UserControl's Content property. If it's some other control, just add it to the Controls collection like usual.
At the end of parsing all your sub-objects, simply display the Content property in a literal control or a panel or something.

How do I Prevent FormView from clearing user's entered Values after Insert Method has fired?

I have been struggling with getting FormViews to work the way Microsoft expects me to for about a day and have figure a bunch of great stuff out.
I can catch e.Exception and e.ReturnValue in the ObjectDataSource.Inserting Event Handler and I can even cheat and check other properties of the Object in the ObjectDataSource.ObjectDisposing by checking the e.ObjectInstance ... and I even learned that FormView's Inserting Handler Runs AFTER the ObjectDisposing Handler so If there is a problem found I still have time to react to it and st the e.KeepInInsertMode to true on the FormView.
My problem is, it seems that the values entered by the user into the Insert form are cleared regardless.
So, How do I Prevent a FormView from clearing after it's Insert Method has fired?
(Using ASP.NET + VB)
I don't think posting my code here will really do much good and i would have to modify it to trim out confidential business logic stuff... so I'll skip it for now.
edit:
I have found a temporary and admittedly terribly cludgy solution (in case no one ever finds a REAL solution to the problem).
I have a page variable defined as:
Dim eInsertArgs As FormViewInsertedEventArgs
And then I do the following in my ItemInserted handler
If boolInsertErrorOccurred = False Then
e.KeepInInsertMode = True
eInsertArgs = e
Else
eInsertArgs = Nothing
End If
Then on each of the controls I have something like this in that controls databinding event:
If IsNothing(eInsertArgs) = False Then
Dim _sender As TextBox = sender
_sender.Text = eInsertArgs.Values("_FieldName")
End If
The effect of this is that I am setting the values BACK to the submitted values AFTER ASP.NET binds the FormView to the default (blank) Template.
Please help me find a less terrible solution. :)
You need to create your own server control which inherits from the FormView control.
Public Class MyFormView
Inherits FormView
Protected Overrides Sub OnDataSourceViewChanged(ByVal sender As Object,
ByVal e As EventArgs)
If (MyBase.CurrentMode = FormViewMode.Insert) Then
MyBase.RequiresDataBinding = False
Else
MyBase.OnDataSourceViewChanged(sender, e)
End If
End Sub
End Class
Please take a look at this page: http://www.dotnetmonster.com/Uwe/Forum.aspx/asp-net/76885/FormView-Insert

ASP.NET Dynamic User Controls

I'm sure this question has been asked a million times, however I haven't been able to find an answer that solves my problem.
I am programmatically adding some custom user controls to a PlaceHolder which I have on a simple aspx page. All of the user controls Postback's work correctly except for one which has a Gridview on it.
For some reason any postback that gets fired from within this control, does not call the specified event on the first click, however all future clicks it will work fine. I have no idea why this is the case, but many solutions I have found, suggest adding an ID to the ascx User Control, however this doesn't work in my case.
I've taken a look at the source file for the page that gets generated before and after the first click, javascript used for calling the postback changes, i.e
Before first click: onclick="javascript:__doPostBack('tmpControlID$sgvPrimaryEmploymentHistory','Select$0')"
After first click: onclick="javascript:__doPostBack('OFFHome1$tmpControlID$sgvPrimaryEmploymentHistory','Select$0')"
OFFHome1 is the parent user control which exists on the aspx page. All other controls are added to a placeholder in this control, i.e.
<%# Control Language="vb" AutoEventWireup="false" CodeBehind="OFFHome.ascx.vb" Inherits="UmbracoUserControls.OFFHome" %>
<asp:PlaceHolder ID="phOFFSection" runat="server"></asp:PlaceHolder>
Nothing to complicated. Then in the code behind the controls are loaded into the placeholder using the following:
Private Sub LoadNextOFFStep()
Dim ControlName As String = "TestControl.ascx"
phOFFSection.Controls.Clear()
If ControlName IsNot Nothing AndAlso ControlName <> String.Empty Then
Dim NewControl As Object = LoadControl(ControlName)
With NewControl
.ID = USERCONTROLNAME
Dim StepCompleted As Boolean = .FillControl()
If StepCompleted Then
Exit Sub
End If
Dim AllowSkip As Boolean = .AllowSkip()
btnSkip.Visible = AllowSkip
End With
phOFFSection.Controls.Add(NewControl)
End If
End Sub
Again, nothing overly complicated. The USERCONTROLNAME is just a const with the value "tmpControlID" in it.
The control that is giving me trouble is a little complicated, I was originally using a custom GridView control that we have created, but have removed it and replaced it with the standard asp one to see if the problem still occurs, and it does.
Any button, on control which fires off a postback will fail the first time, and all future click will work correctly. On the first click the Page_Load event will get called, but that is it.
What am I doing wrong??
After far too much time spent on this, I have finally worked it out.
It was to do with the order of events, however just not where I had thought. The FillControl function was getting called before User Control had been added to the PlaceHolder. I changed this so that it gets called after the User Control was added to the PlaceHolder and now it works first time.
Basically the code looks like this now:
Private Sub LoadNextOFFStep()
Dim ControlName As String = "TestControl.ascx"
phOFFSection.Controls.Clear()
If ControlName IsNot Nothing AndAlso ControlName <> String.Empty Then
Dim NewControl As Object = LoadControl(ControlName)
With NewControl
.ID = USERCONTROLNAME
Dim AllowSkip As Boolean = .AllowSkip()
btnSkip.Visible = AllowSkip
End With
phOFFSection.Controls.Add(NewControl)
Dim StepCompleted As Boolean = CType(phOFFSection.Controls(0), Object).FillControl()
If StepCompleted Then
LoadNextOFFStep()
Exit Sub
End If
End If
End Sub
Thanks for everyone's help.
Well... the answer to the first question is simple: It fails the first time because the ID of the control (or rather, in the postback script) is different on subsequent page loads. It works on subsequent clicks because the control ID stays the same.
Now as to WHY that is... much tougher! But probably something to do with the order of operations here.
Try explicitly setting the NamingContainer value for NewControl:
With NewControl
.NamingContainer = OffHomeOne; // whatever
.ID = USERCONTROLNAME

How to enable/disable web elements of a parent aspx page from the child ascx page?

I have an aspx page with three web controls: one to control the List Users page, one to control the Edit Users page, and one to control the Add User page. I have discovered a method for accessing these elements, but it seems to be limited. Here is what I have done:
Protected Sub editUser(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs)
'set selected user from gridview.
Dim index As Integer = e.NewEditIndex
Dim userId As Integer = gvListUsers.DataKeys(index).Value
Session.Item("SelectedUserId") = userId
'show edit page, hide list and add page.
With Page.Form.Controls(1)
.Controls(getControlId("loadAddUser")).Visible = False
.Controls(getControlId("loadEditUser")).Visible = True
.Controls(getControlId("loadListUser")).Visible = False
End With
End Sub
The getControlId function looks like this:
Public Function getControlId(ByVal control As String) As Integer
Dim enumer As System.Collections.IEnumerator = Page.Form.Controls.Item(1).Controls.GetEnumerator
Dim i As Integer
For i = 0 To (Page.Form.Controls.Item(1).Controls.Count - 1)
If Page.Form.Controls(1).Controls.Item(i).ID = control Then
Return i
End If
Next
Return Nothing
End Function
This works in most cases. However, I am unable to access the "enabled" attribute of these web controls. Why is this, and how might I access that attribute?
Thanks :)
You could raise events from your UserControls which you subscribe to in the parent ASPX page. In the parent page event action you could enable/disable your controls,
Here's an example of events in UserControls: http://codebetter.com/blogs/brendan.tompkins/archive/2004/10/06/Easily-Raise-Events-From-ASP.NET-ASCX-User-Controls.aspx
Something else to think about: are you getting any benefit from moving this code into usercontrols? Would any of the individual controls be re-usable on their own? Creating tightly coupled controls that rely on each other being present doesn't give you much re-usability of the individual controls.
Visible is a property provided by the System.Web.UI.Control class, which is why you can access it directly. Enabled is not a property on this class, so you need to map the control object to a variable of the type of your custom control class if you want to access the Enabled property.
Dim myControl As TheAddUserControl
With Page.Form.Controls(1)
myControl = .Controls(getControlId("loadAddUser"))
myControl.Enabled = False
.Controls(getControlId("loadEditUser")).Visible = True
.Controls(getControlId("loadListUser")).Visible = False
End With
To expose an Enabled property in you user control:
Public Property Enabled As Boolean
Get
Return (Child1.Enabled And Child2.Enabled And Child3.Enabled)
End Get
Set(ByVal value As Boolean)
Child1.Enabled = value
Child2.Enabled = value
Child3.Enabled = value
End Set
End Poperty

Resources