GridView not changing to edit mode when EditIndex is set on RowEditing - asp.net

I am using Visual Studio 2010, VB.NET, target framework .NET 4.0.
I have a GridView which I am binding to some object collection, with a CommandField column that should allow edit of the selected row.
I am setting the EditIndex property correctly in the RowEditing eventhandler.
The problem is: When I click the "Edit" link that is generated, nothing apparently happens, the row gets rendered again in "view mode", not "edit mode".
But if I do some random postback, like clicking my "doNothing" button, the row gets rendered in "edit mode" in the next postback.
I have managed to reproduce the problem in the following code:
ASPX:
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="MyForm.aspx.vb" Inherits="MySandBox.MyForm" %>
<%# Register Src="MyControl.ascx" TagName="MyControl" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:GridView ID="gvInfrator" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:CommandField ShowEditButton="true" />
<asp:BoundField HeaderText="MyField" DataField="MyField" />
</Columns>
</asp:GridView>
<asp:Button ID="btnDoNothing" runat="server" Text="Just do a postback" />
</form>
</body>
</html>
Code Behind:
Public Class MyDto
Public Property MyField As String
End Class
Public Class MyForm
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.BindMyData()
End Sub
Private Sub BindMyData()
Dim myData As New MyDto
myData.MyField = "My field value"
Me.gvInfrator.DataSource = New MyDto() {myData}
Me.gvInfrator.DataBind()
End Sub
Protected Sub gvInfrator_RowEditing(sender As Object, e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles gvInfrator.RowEditing
Me.BindMyData()
Me.gvInfrator.EditIndex = e.NewEditIndex
End Sub
End Class
Am I doing something wrong? Is this a bug in .NET Framework 4? Is there any workaround?

You need to bind your data after you set the EditIndex of your GridView.
Protected Sub gvInfrator_RowEditing(sender As Object, e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles gvInfrator.RowEditing
Me.gvInfrator.EditIndex = e.NewEditIndex
Me.BindMyData()
End Sub

Related

Dynamically Add Text Files to DDL in ASP & VB

I am looking to update one of my DDL's functionality by making it dynamically update so if the user adds more files, the drop down will pick this up.
At present my drop down list is pulling from VB code behind, as shown below:
Public Sub DDL_SelectedIndexChanged(sender As Object, e As EventArgs)
Dim ddl As DropDownList = CType(sender, DropDownList) 'item is already dropdownlist
Dim ctl As TextBox = DirectCast(ddl.NamingContainer.FindControl("eTemplate"), TextBox)
If ddl.SelectedValue = 1 Then
ctl.Text = File.ReadAllText("e:Documents\Visual Studio 2013\Projects\Web\Templates\Down.txt")
ElseIf ddl.SelectedValue = 2 Then
ctl.Text = File.ReadAllText("e:Documents\Visual Studio 2013\Projects\Web\Templates\Up.txt")
Else
ctl.Text = ""
End If
End Sub
At the moment I have hard coded in the functionality for the VB to grab specific .txt files, how can I get this to update dynamically from a folder of .txt files?
Thanks for looking.
Here is some sample code for you. This demo uses an UpdatePanel and a Timer to refresh the DropdownList every 5 seconds.
Add a new aspx file to your Web Application and the following code:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="Demo.aspx.vb" Inherits="Zpk_Test2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Asynchronous Update Demo</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager runat="server" ID="ScriptManager1" />
<asp:UpdatePanel runat="server" ID="UpdatePanel1">
<ContentTemplate>
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true" /><br />
<asp:Timer runat="server" ID="Timer1" Interval="5000" Enabled="true" />
</ContentTemplate>
<Triggers>
<asp:PostBackTrigger ControlID="DropDownList1" />
</Triggers>
</asp:UpdatePanel>
<asp:TextBox ID="TextBox1" runat="server" TextMode="MultiLine" Width="300" Height="250" />
</form>
</body>
</html>
This is the code-behind:
Partial Class Demo
Inherits System.Web.UI.Page
Private Const FolderName As String = "C:\Temp" '<-- replace with your folder name
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
RefreshDropDownList()
OpenSelectedFile()
End If
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
' this event is fired everytime a timer ticks.
' refresh your dropdown list here.
RefreshDropDownList()
End Sub
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
OpenSelectedFile()
End Sub
Private Sub RefreshDropDownList()
Dim currentSelected As String = DropDownList1.SelectedValue
DropDownList1.DataSource = IO.Directory.GetFiles(FolderName, "*.txt").Select(Function(f) IO.Path.GetFileName(f)).ToList
DropDownList1.DataBind()
DropDownList1.SelectedValue = currentSelected
End Sub
Private Sub OpenSelectedFile()
Dim fileName As String = IO.Path.Combine(FolderName, DropDownList1.SelectedValue)
TextBox1.Text = IO.File.ReadAllText(fileName)
End Sub
End Class

Understanding UpdatePanels

I am trying to understand UpdatePanels and best practise for using them.
I am using .Net4.0 with VB.Net.
The idea is to create a conversation app for a clients website and so I have control called Convo.ascx. Code added below.
<asp:UpdatePanel runat="server">
<ContentTemplate>
<h2>Conversation</h2>
<p><asp:Literal ID="lit1" runat="server" /></p>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
</ContentTemplate>
</asp:UpdatePanel>
Convo.ascx.vb
Partial Class Convo
Inherits System.Web.UI.UserControl
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
lit1.Text = lit1.Text & "<p>" & TextBox1.Text & "</p>"
End Sub
End Class
On a load page (Default.aspx) I have:
<%# Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>
<%# Reference Control="~/Convo.ascx" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:scriptmanager ID="Scriptmanager1" runat="server"></asp:scriptmanager>
<div>
<asp:UpdatePanel runat="server">
<ContentTemplate>
<asp:Button ID="Button1" runat="server" Text="Add Conversation" />
<asp:PlaceHolder ID="phConversation" runat="server">
</asp:PlaceHolder>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
With Codebehind Default.aspx.vb as
Partial Class _Default
Inherits System.Web.UI.Page
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
AddConvo()
End Sub
Private Sub AddConvo()
Dim getPh As New PlaceHolder
getPh = CType(Me.FindControl("phConversation"), PlaceHolder)
Dim ucConvo As New Convo
ucConvo = CType(LoadControl("~/Convo.ascx"), Convo)
getPh.Controls.Add(ucConvo)
End Sub
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
AddConvo()
End Sub
End Class
So the Convo I add OnLoad remains on the page after extra as been added been any convo added after load is gone once the button on Convo is hit.
So my question is, how can I have these add and remain? Eventually they will be added to database but right now I am trying to understand UpdatePanels as they will become the foundation for this app.
Is there a very good explanation of multi-use UpdatePanels anywhere?
Thanks in advance
PS, im a hobbiest so only VB responses please
The issue actually isn't with the UpdatePanel, but with ASP.NET. ASP.NET web forms uses a control hierarchy for the entire page, and you are adding the controls to the hierarchy "dynamically". Since you are doing it that way, ASP.NET requires you add the control back into the control hierarchy on every postback to the server. The UpdatePanel is a way to post back to the server, and therefore you must re-add the old user controls and new ones to that hierarchy.
Essentially the UpdatePanel was added to make AJAX easy, but you still have to work within the rules of ASP.NET.

Bind Property of ASP.NET User Control to One of the Parent Control's Fields

If I need access to the value of a user control's property BEFORE PreRender, would I need to base the custom control off of one of the preexisting data controls (repeater, listview, etc.)?
One of my user controls features a gridview control that is configured based on the user control's properties on which it resides. Several of the key properties alter the SQL Statement for the underlying recordsource. I'm now in a situation where the property that sets the WHERE statement for the SQL Statement needs to tied to a value in the user control's parent FormView. Think of the formview as displaying a customer detail record. The user control takes the customer's account number and then displays data from a related table such as Customer Contact Names. Since the gridview is created before the control's prerender event, working within the prerender event doesn't seem efficient.
See this question as a reference:
Stumped With Custom Property on User Control
What I said that you can assign value to the parent control when user control binds.
TestIt.ascx - Markup
<%# Control Language="VB" AutoEventWireup="false"
CodeFile="TestIt.ascx.vb" Inherits="usercontrols_TestIt" %>
<asp:Label
ID="lblOne"
runat="server">
</asp:Label>
TestIt.ascx.vb
Partial Class usercontrols_TestIt
Inherits System.Web.UI.UserControl
Public Property No As Integer
Get
Dim mno As Integer = 0
Integer.TryParse(lblOne.Text, mno)
Return mno
End Get
Set(value As Integer)
lblOne.Text = value.ToString()
End Set
End Property
Public ReadOnly Property Square As Integer
Get
Return No * No
End Get
End Property
Protected Sub Page_PreRender(sender As Object, e As System.EventArgs) Handles Me.PreRender
'Get ref. of parent control
Dim row As FormViewRow = CType(Parent.Parent, FormViewRow)
'Find control in parent control
Dim sqLabel As Label = row.FindControl("Label2")
'Assign value
sqLabel.Text = Square.ToString()
End Sub
End Class
ASPX - Markup
<%# Page Language="VB" AutoEventWireup="false" CodeFile="VbDefault2.aspx.vb" Inherits="usercontrols_VbDefault2" %>
<%# Register src="TestIt.ascx" tagname="TestIt" tagprefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:FormView ID="FormView1" runat="server" AllowPaging="True">
<ItemTemplate>
No :
<uc1:TestIt ID="TestIt1" runat="server" No='<%#Eval("No") %>'
ClientIDMode="AutoID" />
<br />
Square :
<asp:Label ID="Label2" runat="server" ></asp:Label>
</ItemTemplate>
</asp:FormView>
</div>
</form>
</body>
</html>
ASPX.vb
Partial Class usercontrols_VbDefault2
Inherits System.Web.UI.Page
Public Class TestData
Public Property No As Integer
End Class
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
BindData()
End If
End Sub
Sub BindData()
Dim nos As New List(Of TestData)
nos.Add(New TestData() With {.No = 10})
nos.Add(New TestData() With {.No = 20})
FormView1.DataSource = nos
FormView1.DataBind()
End Sub
Protected Sub FormView1_PageIndexChanging(sender As Object, e As System.Web.UI.WebControls.FormViewPageEventArgs) Handles FormView1.PageIndexChanging
FormView1.PageIndex = e.NewPageIndex
BindData()
End Sub
End Class

Declarative event handling from ASP.NET user control to page

I am trying to figure out how to declaratively pass in a event handler into
a user control, but I am stumped. All I can make work is the user control's
event handler.. I can't seem to bubble up the caught event into the parent
page. Ideas would be quite welcome. Here is my code:
Default.aspx:
<%# Page Language="VB" %>
<%# Register TagPrefix="rpt" TagName="filter" Src="WebUserControl.ascx" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Test Controls</title>
</head>
<body>
<form id="form1" runat="server">
<rpt:filter ID="DataView1Filters" runat="server" SelectedIndexChanged="DropDown_SelectedIndexChanged" />
<asp:Label ID="Label1" runat="server" />
</form>
<script runat="server">
Public Sub DropDown_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
Label1.Text = String.Format("Inside declarative event handler. {0}<br>", Label1.Text)
End Sub
</script>
</body>
</html>
WebUserControl.ascx:
<%# Control Language="VB" ClassName="WebUserControlTest" %>
<asp:Panel ID="TestPanel" runat="server"></asp:Panel>
<script runat="server">
Private AllEvents As New System.ComponentModel.EventHandlerList
Public Custom Event SelectedIndexChanged As EventHandler
AddHandler(ByVal value As EventHandler)
AllEvents.AddHandler("SelectedIndexChanged", value)
End AddHandler
RemoveHandler(ByVal value As EventHandler)
AllEvents.RemoveHandler("SelectedIndexChanged", value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
Dim value As EventHandler = CType(AllEvents("SelectedIndexChanged"), EventHandler)
If Not value Is Nothing Then
value.Invoke(sender, e)
End If
End RaiseEvent
End Event
Private Sub _SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim ctrl As DropDownList = Me.FindControl("TestDropDownList")
If Not ctrl Is Nothing Then
Me.ViewState("ItemSelection") = ctrl.SelectedIndex
End If
Dim Label1 As Label = Parent.FindControl("Label1")
Label1.Text = String.Format("Inside user control event handler. {0}<br>", Label1.Text)
RaiseEvent SelectedIndexChanged(sender, e)
End Sub
Private Overloads Sub OnLoad(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim ctrl As New DropDownList
With ctrl
.ID = "TestDropDownList"
.Items.Clear()
.AutoPostBack = True
AddHandler .SelectedIndexChanged, AddressOf _SelectedIndexChanged
.Items.Add(New ListItem("-- Select --", String.Empty))
.Items.Add(New ListItem("Item 1", "1"))
.Items.Add(New ListItem("Item 2", "2"))
If Not Me.ViewState("ItemSelection") Is Nothing Then
.SelectedIndex = CInt(Me.ViewState("ItemSelection"))
Else
.SelectedIndex = 0
End If
End With
TestPanel.Controls.Add(ctrl)
End Sub
</script>
Thanks!
See this previous post:
Handling User Control Events on Containing Page
Edit - added based on your comment
I should have read the question more clearly.
As far as having a UserControl raise an event that the containing page can respond to, I do not believe that this can be done declaratively.
Unless my knowledge is just lacking, the only way to accomplish this is by explicitly creating an event in the control and then handling it (by coding the event handler) on the parent page, as shown in the example I linked to.
I was recently having this same issue in C#. When you set up an event called SelectedIndexChanged asp.net will bind the attribute OnSelectedIndexChanged when using the declarative syntax.
So if you change
<rpt:filter ID="DataView1Filters" runat="server" SelectedIndexChanged="DropDown_SelectedIndexChanged" />
To
<rpt:filter ID="DataView1Filters" runat="server" OnSelectedIndexChanged="DropDown_SelectedIndexChanged" />
It should work.

How do I apply AddHandler using OnCommand in ASP.NET

I've been racking my brain trying to get this to work. My event for my LinkButton isn't firing. I'm figuring that it has SOMETHING to do with ViewState and that the button is not there when it tries to fire the event after the Postback or something.
When I click the Add button,it adds the link to the page and then when I click the Diplay Time" linkbutton it should fire the event and display the CommandArgument data but it's not and i can't figure out why.
Here's my code:
<%# Page Language="VB" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
End Sub
Protected Sub btnDelete_OnCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.CommandEventArgs)
Response.Write(e.CommandArgument)
End Sub
Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim btn As New LinkButton()
btn.ID = "lbn"
btn.Text = "Display Time"
btn.ValidationGroup = "vgDeleteSigner"
AddHandler btn.Command, AddressOf btnDelete_OnCommand
btn.CommandArgument = Now.TimeOfDay.ToString()
btn.EnableViewState = True
Panel1.Controls.Add(btn)
End Sub
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
<asp:Panel ID="Panel1" runat="server">
</asp:Panel>
</div>
</form>
</body>
</html>
The reason that's happening is that your dynamic button "lbn" needs to be drawn again on post back when it's clicked because the button doesn't exist after you click it.
Basically you just have to dynamically add the button to the page again on post back of click of that button.
I would recommend having the button already on the page but visible = false and then just showing it when you click the other button.

Resources