Persisting Dynamic ReadOnly Field Past Initialisation - asp.net

I create a readonly textbox dynamically on page load/init and on the first load (not IsPostBack) I set the text. I also have a button on the page with a click event that changes the content of the textbox. Using another button, I read the text of the textbox. The problem is as follows:
If I click the button that changes the textbox and then click the button that reads the text of the textbox, it gets it fine.
If I just load the page and click the button that reads the text of the textbox, it brings back an empty string
I need both scenarios to bring back a result - whether it's the original text or the new programmatically changed text.
Example code:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim Textbox As New TextBox()
Textbox.ID = "Bob"
Textbox.ReadOnly = True
If Not IsPostBack Then
Textbox.Text = "Initial Text"
End If
Content.Controls.Add(Textbox)
Dim Button As New Button()
Button.Text = "Change text"
AddHandler Button.Click, AddressOf Button_Click
Content.Controls.Add(Button)
End Sub
Protected Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs)
'FindControlRecursive is locally defined - just finds a control based on root control and ID
CType(FindControlRecursive(Content, "Bob"), TextBox).Text = "Hmmmmm"
End Sub
Private Sub Submit_Click(sender As Object, e As EventArgs) Handles Submit.Click
Dim Textbox As TextBox = CType(FindControlRecursive(Content, "Bob"), TextBox)
MsgBox(Textbox.Text)
End Sub
I could just reinitialise the textbox each time but this is just an example and in practice I'll be pulling it from SQL and if I have several read only controls on the page I'd rather get the control to persist its text rather than going back to SQL each time.

I found two ways to solve this, the second being the more favoured approach (depending on situation):
Specifically add the initial text to the viewstate and then set the text on each postback using the value from viewstate:
If Not IsPostBack Then
Textbox.Text = "Initial Text"
ViewState("Bob") = Textbox.Text
Else
Textbox.Text = ViewState("Bob")
End If
Add a PreRender event to the textbox that just sets it text to itself. This takes place after the ViewState starts being tracked so it's added to the viewstate via the control and is persisted across postbacks:
If Not IsPostBack Then
Textbox.Text = "Initial Text"
AddHandler Textbox.PreRender, AddressOf PersistPastInitialLoad
End If
And add new sub:
Protected Sub PersistPastInitialLoad(ByVal sender As Object, ByVal e As EventArgs)
'This is just for example - would need refining for different types of controls
CType(sender, TextBox).Text = CType(sender, TextBox).Text
End Sub
Essentially they both do the same thing - hold the text in viewstate - but it depends on implementation which is the best to use. The first implementation might be a bit easier to read/work with but it would result in redundant viewstate bloat if you click the button to change the textbox text (i.e. you'll have the initial text in viewstate against the viewstate key you created as well as the current text added to viewstate by the control onchange).

Related

How to access a control after Edit is clicked in asp:datagrid editcommand

I need to access a label control after the edit is clicked to bindgrid based on the label text. How do I do that?
Private Sub ActionItems_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles ActionItems.EditCommand
ActionItems.EditItemIndex = e.Item.ItemIndex
Dim fieldtypelbl As Label = e.Item.FindControl("lblrcause")
FillActions(fieldtypelbl.text)
End Sub
I was able to resolve the same by using
source.Parent.fieldname
as the datagrid is a part of the control being called on the same page multiple times

Failure to update textbox.text

I have a relatively simple ASP.NET problem (I should think) that regrettably I am unable to solve by myself. What I am trying to do is the following:
On a page I load a number of controls (Text Boxes) programmatically;
following this load the user should be able to select a value to load into the Textbox from a panel control that is added to the page following the click of a button
Once the panel is closed, the selected text from the panel should be loaded into the textbox
However, in the vb.net statements below when run the "test" string never makes it to the textbox - any help with resolving this would be greatly appreciated.
Public Class test
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Controls_Load()
End Sub
Public Sub Controls_Load()
Dim ttf_tb As New TextBox With {.ID = "ttf_tb"}
Master_Panel.Controls.Add(ttf_tb)
Dim ttf_button As New Button
Master_Panel.Controls.Add(ttf_button)
AddHandler ttf_button.Click, AddressOf TTF_BUTTON_CLICK
End Sub
Public Sub TTF_BUTTON_CLICK(sender As Object, e As EventArgs)
Dim str As String = sender.id
Dim panel As New Panel
panel.ID = "TTF_Panel"
panel.Width = 300
panel.Height = 300
Master_Panel.Controls.Add(panel)
panel.BackColor = Drawing.Color.Black
panel.Style.Add(HtmlTextWriterStyle.Position, "absolute")
panel.Style.Add(HtmlTextWriterStyle.Left, "200px")
panel.Style.Add(HtmlTextWriterStyle.Top, "100px")
panel.Style.Add(HtmlTextWriterStyle.ZIndex, "100")
Dim CL_Button As New Button
CL_Button.ID = "TTF_Close_" & Replace(str, "TTF_Button_", "")
panel.Controls.Add(CL_Button)
AddHandler CL_Button.Click, AddressOf TTF_Close_Button_Click
End Sub
Public Sub TTF_Close_Button_Click(sender As Object, e As EventArgs)
Dim ttf_tb As TextBox = Master_Panel.FindControl("ttf_tb")
ttf_tb.Text = "Test"
Dim panel As Panel = FindControl("TTF_Panel")
Master_Panel.Controls.Remove(panel)
End Sub
End Class
I think you need to re-create your controls in the Page_Init method. It's been a while since I've done web forms but I think it's something like:
When a user clicks the button a post back is fired. This re-creates a new instance of your class, creates the controls on the page, assigns any form values then calls your Page_Load event.
The problem is you are creating your controls too late, so the forms values are never assigned correctly.
You should create / recreate your dynamic controls in the Init event:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
Controls_Load()
End Sub
This should allow you to maintain their state across PostBacks.
For more information about this topic, see the MSDN article on the ASP.NET Page Life Cycle.

RowDataBound DropDownList

I have a RowDataBound event handler that looks like this:
Public Sub CustomersGridView_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles GVHistoricNames.RowDataBound 'RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
Dim hyperlinkUSNHyperlink As HyperLink = CType(e.Row.FindControl("USNHyperlink"), HyperLink)
Dim ddl As DropDownList = CType(e.Row.FindControl("ddlUsercode"), DropDownList)
If ddl.SelectedValue = "" Then 'labLastUserCode.Text = "" Then
hyperlinkUSNHyperlink.NavigateUrl = ""
End If
End If
End Sub
...and a RowCreated event handler that looks like this:
Public Sub CustomersGridView_RowCreated(ByVal sender As Object, ByVal e As GridViewRowEventArgs) Handles GVHistoricNames.RowCreated 'RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Then
Dim ddl As DropDownList = CType(e.Row.FindControl("ddlUsercode"), DropDownList)
ddl.Items.Add("")
ddl.Items.Add(strUserName)
End If
End Sub
...and a RowUpdating event handler that looks like this:
Protected Sub GVHistoricNames_RowUpdating(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewUpdateEventArgs) Handles GVClearcoreHistoricNames.RowUpdating
Try
Dim ddl As DropDownList = CType(GVHistoricNames.Rows(e.RowIndex).FindControl("ddlUsercode"), DropDownList)
SQLHistoricNames.UpdateParameters("UserCode").DefaultValue = ddl.SelectedValue
Catch ex As Exception
Finally
End Try
End Sub
Please see line three of the RowUpdating event handler. The value of the SelectedValue property is never correct because the RowDataBound event handler is called after the RowUpdating event handler. How do I access SelectedValue? I want to set it as an update parameter.
One of the way could be to look into the actual request data. For example, in GVHistoricNames_RowUpdating code, use
Dim ddl As DropDownList = CType(GVHistoricNames.Rows(e.RowIndex).FindControl("ddlUsercode"), DropDownList)
SQLHistoricNames.UpdateParameters("UserCode").DefaultValue = Request(ddl.UiniqueID)
I often use such work-arounds when the control value is needed before post data could be loaded into control (or when controls are added/bound dynamically at a later event).
EDIT
ASP.NET uses Control.UniqueId to represent name property of corresponding html element. It (as well as ClientID) typically gets constructed by appending control's id to parent's (parent that is naming container) unique id, hence you get different unique ids (and client ids) for multiple drop-down lists in the grid (because each row acts as a naming container)
As far as your problem goes, you are probably creating drop-down list in design time template while you are loading your list items in row created. However, before row-created event is fired, the drop-down list would have been already added to page control tree and its POST events would have been already processed. In such case, there would be no items in the drop-down list at that time to set the selection. Hence the issue.

ASP.NET confirm delete in a grid

I need to add a confirm delete action to a grid. the problem is the way the "Delete" link is rendered.
my grid is built in code behind in vb.net.
i have this
colDelete.AllowDelete = True
colDelete.Width = "100"
AddHandler CType(gridSavedForLater, Grid).DeleteCommand, AddressOf dgDeleteSelectedIncident
and the sub is the following
Sub dgDeleteSelectedIncident(ByVal sender As Object, ByVal e As GridRecordEventArgs)
Dim message As String = "Are you sure you want to delete this incident?"
Dim caption As String = "Confirm Delete"
Dim result = System.Windows.Forms.MessageBox.Show(message, caption, Windows.Forms.MessageBoxButtons.OKCancel, Windows.Forms.MessageBoxIcon.Warning)
'If (result = Windows.Forms.DialogResult.Cancel) Then
'Else
' MessageBox("Are you sure you want to delete this incident?")
'Get the VehicleId of the row whose Delete button was clicked
'Dim SelectedIncidentId As String = e.Record("IncidentId")
''Delete the record from the database
'objIncident = New Incidents(SelectedIncidentId, Session("userFullName"))
'objIncident.DeleteIncident()
''Rebind the DataGrid
LoadSavedForLater()
'' End If
End Sub
i need to add a javascript confirm dialog when this sub is called. i can do it with a windows form messagebox but that does not work on the server.
pleae help
joe
You cannot show a MessageBox in ASP.NET since it would be shown on the server. So you need a javascript confirm onclick of the delete button. Therefor you don't need to postback to the server first. You can attach the script on the initial load of the GridView.
A good place would be in RowDataBound of the GridView:
Private Sub GridView1_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles GridView1.RowDataBound
Select Case e.Row.RowType
Case DataControlRowType.DataRow
' if it's a custom button in a TemplateField '
Dim BtnDelete = DirectCast(e.Row.FindControl("BtnDelete"), Button)
BtnDelete.OnClientClick = "return confirm('Are you certain you want to delete?');"
' if it's an autogenerated delete-LinkButton: '
Dim LnkBtnDelete As LinkButton = DirectCast(e.Row.Cells(0).Controls(0), LinkButton)
LnkBtnDelete.OnClientClick = "return confirm('Are you certain you want to delete?');"
End Select
End Sub
As an alternative, a common method of notifying users in a web page of a status change (save, delete, update, etc.) is having an update element in your HTML. Basically, a label (or whatever) that you'll set with updates to the user.
"Your changes have been successfully saved." or "An error was encountered when trying to save your update.", for example. You can set the color red for an error or whatever your programming heart desires, stylistically.
I kind of like this approach as a pop-up always feels more like a WinForm thing to me. Either works, so I just thought I'd suggest another approach.

Deleting Dynamically Populated images from a Directory (NOT GridView) ASP.NET (VB)

The code below displays a thumbnail of each image in a specific server directory and when I click on the image it pops up a nice full sized picture. It works perfectly.
I would however, like to be able to delete an image. I first thought I could have a button at the bottom of the page with a checkbox next to each image, giving it a uniqueID as per the filename but as they are dynamically created I couldn’t figure how to handle the Click Event on the button for a randomly named Checkbox ID. Then I tried adding a button next to each item and then tried an OnClick & OnServerClick to call a Sub but this didn’t work either.
Any/All suggestions welcomed :)
Private Sub ImageList()
If Directory.Exists(Server.MapPath("JobImages\" & DBC_JOB_JobID.Text)) Then
Dim MySB As New StringBuilder
Dim dirInfo As New DirectoryInfo(Server.MapPath("JobImages\" & DBC_JOB_JobID.Text))
MySB.Append("<ul class=""clearfix"">")
MySB.AppendLine()
For Each File In dirInfo.GetFiles()
MySB.Append("<li><a rel=""jobpic"" href=""JobImages\" & DBC_JOB_JobID.Text & "\" & File.Name & """><img src=""JobImages\" & DBC_JOB_JobID.Text & "\Thumbs\" & File.Name & """ width=""150"" height=""100"" /> <span class=""size"">" & File.Name & " </span></a></li>")
MySB.AppendLine()
Next
MySB.Append("</ul>")
MySB.AppendLine()
lblMyPictures.Text = MySB.ToString
End If
End Sub
OK what Kendrick is talking about (basically) is using server side controls to keep track of which file to delete. What you are doing right now is dumping markup into a Label control, which on postback won't fire an event on the server side. However you can accomplish this easily with server side controls.
The basic idea is you use a container control such as a Panel and add each child control to it. Then you hook events to each row with data identifying that row (such as filename).
Markup:
<asp:Panel ID="pnlList" runat="server">
</asp:Panel>
Code-Behind:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Directory.Exists(Server.MapPath("Files")) Then
Dim objDirInfo As New DirectoryInfo(Server.MapPath("Files"))
For Each objFile As FileInfo In objDirInfo.GetFiles()
Dim objLabel As New Label
objLabel.Text = objFile.Name
Dim objLinkButton As New LinkButton
objLinkButton.Text = "Delete"
objLinkButton.CommandName = "Delete"
objLinkButton.CommandArgument = objFile.Name
AddHandler objLinkButton.Command, AddressOf DeleteFile
Dim objLiteral As New LiteralControl
objLiteral.Text = "<br/>"
pnlList.Controls.Add(objLabel)
pnlList.Controls.Add(objLinkButton)
pnlList.Controls.Add(objLiteral)
Next
End If
End Sub
Public Sub DeleteFile(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.CommandEventArgs)
If e.CommandName = "Delete" Then
Dim strFileName As String = Server.MapPath("Files\" & e.CommandArgument)
If File.Exists(strFileName) Then
Dim objFile As New FileInfo(strFileName)
objFile.Delete()
End If
End If
End Sub
This would be an excellent example of where using a data aware would make your life a lot easier.
That said, if you didn't want to use a server-side control, you could assign an ID to each checkbox (i.e. DeleteImage_1) and then store the ID and associated image name in the viewstate on the page. Go through the checked checkboxes and refer back to the viewstate for the name that goes with each ID when they click the delete button.

Resources