I've developed a page with postbacks but recently I decided to change it to use callbacks
MasterPage
Page
Repeater
td1
literal to show description
td2
EditControl*** (starts on view mode, on postback to edit mode with save button)
So I have 100 records and for each repeater item I have my control in view mode with the Edit button(linkbutton with href:javascript:void and the callback function Edit('',''))
When I click on Edit, it raise the callback event but at this point, imagine that I click on the 15th control in the repeater but the callback event thinks it's the first control in the repeater, I mean 1st row.
So how can I do to work the accurate control that I really click on it?
Because at the end, I return the control with HtmlTextWriter(RenderControl) and I send it to the client-side receiver data function to render it with innerHtml that works great but just with the first control in the first row.
You need to pass in a CommandArgument to the control so that when the event is raised, you can retrieve the args and know which button was clicked.
Your repeater needs an ItemCommand event:
<asp:Repeater ID="repeater" runat="server" OnItemDataBound="repeater_OnItemDataBound" OnItemCommand="repeater_ItemCommand">
<ItemTemplate>
<asp:LinkButton ID="lnkBtnEdit" runat="server" Text="Edit" />
</ItemTemplate>
</asp:Repeater>
Inside the repeaters ItemDataBound event you would pass the link button a command argument:
protected void repeater_OnItemDataBound(object sender, RepeaterItemEventArgs e)
{
if ((e.Item.ItemType == ListItemType.Item) || (e.Item.ItemType == ListItemType.AlternatingItem))
{
LinkButton lnkBtnEdit = (LinkButton)e.Item.FindControl("lnkBtnEdit");
lnkBtnEdit.CommandArgument = "foo args";
}
}
Next in the ItemCommand event which is what will be raised when one of the link buttons is clicked, you get the Command Argument which tells you which linkbutton was clicked:
protected void repeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
//This is how you know which button was clicked
string foo = e.CommandArgument;
}
In reality, you'd probably want to pass in the object ID of the item you're clicking, this would likely redirect to an edit page with the ID as a querystring param. In the edit page you would pull in data about that object by the ID.
Related
I have a page (company.aspx) that when you click a button on the page the button click event (btnShowDeptsUserControl_Click) dynamically creates a user control and adds to a placeholder. The page reloads with the user control displayed. That works fine.
The issue I am having is that when I click the button on the user control itself, the btnEmailDepts_Click event is not fired, but the main page (company.aspx) is reloaded.
I have read that one way to resolve this is to create the dynamic user control in Page_Init, but I am creating the user control from the main page button click. Also, I am not declaring the user control in the code behind of the main page as you would normally, but do have a placehoder within which the user control is added.
I have also read that you can add a delegate to the main page for the user control button click, but the example seemed to have so much necessary code for such a simple thing that I figured there must be a better way.
Here are the relevant code snippets:
company.aspx:
<asp:Button id="btnShowDeptsUserControl" runat="server" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>
<asp:PlaceHolder ID="phUserControls" runat="server"></asp:PlaceHolder>
company.aspx.cs:
protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
{
CreateDeptsUserControl();
}
private void CreateDeptsUserControl()
{
phUserControls.Controls.Clear();
var uc = (UserControl)LoadControl("~/controls/ucDepartments.ascx");
phUserControls.Controls.Add(uc);
}
ucDepartments.ascx:
<asp:Button ID="btnEmailDepts" runat="server" Text="Send Email" OnClick="btnEmailDepts_Click" />
ucDepartments.ascx.cs:
protected void btnEmailDepts_Click(object sender, EventArgs e)
{
EmailDepts(); // breakpoint here is never hit
}
private void EmailDepts()
{
// do something
}
If you read the MSDN, you will notice:
However, the added control does not catch up with postback data processing. For an added control to participate in postback data processing, including validation, the control must be added in the Init event rather than in the Load event.
You are adding your control in the click event which happens after both init and load events, so the postback will not work.
You can call your CreateDeptsUserControl function in the ini event, but there you will have to detect if the btnShowDeptsUserControl was clicked by yourself. It's not hard, you will need to check the submitted values collection and see if there is an item for btnShowDeptsUserControl.
Just wanted to post what I did to make this work. Racil Hilan's answer helped me to arrive at this solution. I did away with the dynamic user control, and went with the more common declared user control in the aspx, but set it to Visible="False" by default.
Notice that the main page button event is empty. Also notice that the user control Page_Load event is empty. All the checking is done in the user control OnInit event, which is executed with each main page load.
In the user control OnInit event I have a couple guard conditions that check to see what EVENTTARGET, if any, caused the page / user control to load. If the EVENTTARGET (control ID) is that of the main page button, then execution will continue and will call LoadSomeData() as well as set the user control Visible = true. Otherwise, if either guard condition evaluates as false, we exit and the user control does not get loaded, so no wasted db / service calls.
company.aspx:
<asp:Button id="btnShowDeptsUserControl" runat="server" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>
<uc1:ucDepartments ID="ucDepartments" runat="server" Visible="False" />
company.aspx.cs:
protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
{
// empty, just need the event for inspection later in user control OnInit event.
}
ucDepartments.ascx:
<asp:Button ID="btnEmailDepts" runat="server" Text="Send Email" OnClick="btnEmailDepts_Click" />
ucDepartments.ascx.cs:
protected override void OnInit(EventArgs e)
{
if (string.IsNullOrWhiteSpace(Request.Params["__EVENTTARGET"]))
return;
var controlName = Request.Params["__EVENTTARGET"];
if (controlName != "btnShowDeptsUserControl")
return;
LoadSomeData(); // call method to load the user control
this.Visible = true;
}
protected void Page_Load(object sender, EventArgs e)
{
// empty
}
private void LoadSomeData()
{
// get data from database
// load table / gridview / etc
// make service call
}
protected void btnEmailDepts_Click(object sender, EventArgs e)
{
EmailDepts(); // this event is now executed when the button is clicked
}
private void EmailDepts()
{
// do something
}
Variation that includes jquery to scroll to user control after postback:
In the main page button click event you can also do something like set a hidden var to a value that can be inspected on main page doc ready to do some jquery stuff, such as scrolling the user control into view if it is far down the page (which I am actually doing in my current task).
Not only will this scroll the now loaded user control into view after clicking the main page button, but notice the code in setupDepts(). I hide the asp button that does a postback to load the user control, and show a regular html button. They both look the same (both say Show Depts), but the regular html button, when clicked, will fire jquery to toggle the div that contains the user control to close, click again, it will open, click again it will close, etc.
This is so that you only load the user control once (make db / service calls once) when the main page button is clicked, and then can toggle show or hide it with subsequent clicks of the alternate button. This approach can be used with multiple buttons or links so long as they all have the same class ids. For example, you may have a Show Depts button / link at top of page and another at the bottom of the page, which is the case in my current task.
company.aspx:
<asp:Button id="btnShowDeptsUserControl" runat="server" class="btnShowDepts" OnClick="btnShowDeptsUserControl_Click">Show Depts</asp:Button>
<button class="btnToggleDepts" style="display: none;">Show Depts</button>
<div id="divShowDepts" style="display: none;">
<asp:HiddenField runat="server" id="hdnShowDepts"/>
<uc1:ucDepartments ID="ucDepartments" runat="server" Visible="False" />
</div>
<script language="javascript" type="text/javascript">
$(function () {
toggleDeptsUserControl();
if ($('#<%=hdnShowDepts.ClientID%>').val() === "show")
setupDepts();
});
function setupDepts() {
$('.btnShowDeptsUserControl').hide();
$('.btnToggleDeptsUserControl').show();
scrollToDepts();
}
function scrollToDepts() {
$('#divShowDepts').toggle(700, function () {
if ($(this).is(":visible")) {
$('html, body').animate({ scrollTop: ($(this).offset().top) }, 'slow');
}
});
}
function toggleDeptsUserControl() {
$('.btnToggleDeptsUserControl').on('click', function (event) {
event.preventDefault();
scrollToDepts();
});
}
</script>
company.aspx.cs:
protected void btnShowDeptsUserControl_Click(object sender, EventArgs e)
{
hdnShowDepts.Value = "show";
}
I am using linkbutton to insert data in the asp.net grid view through textbox. But in spite of correct code my button is not performing action.
Here is my ASPX code
<FooterTemplate>
<asp:LinkButton ID="Insertkey" OnClick="Insertkey_Click" runat="server">INSERT</asp:LinkButton>
</FooterTemplate>
Here is my C# code
protected void Insertkey_Click(object sender, EventArgs e)
{
SqlDataSource1.InsertParameters["Title"].DefaultValue = ((TextBox)GridView1.FooterRow.FindControl("Tbox")).Text;
SqlDataSource1.InsertParameters["Pic"].DefaultValue = ((TextBox)GridView1.FooterRow.FindControl("Pbox")).Text;
SqlDataSource1.InsertParameters["Content"].DefaultValue = ((TextBox)GridView1.FooterRow.FindControl("Cbox")).Text;
}
Please help me to solve my problem.
Try using the command event instead of click, then handle the grid's RowCommand event and see what the command name is.
Controls that postback, such as the link button, behave differently when in templated controls such as the gridview, primarily since they can't really be declared in the same way since they're in a template.
I've created a user control that displays the header (not < head >, I mean the title, datetime page is created and so on) on each page on my website. In that user control I also have one link. A link that will be displayed on the page IF (user = admin).
Currently this link is a pure link, and I have no problems to change the "navigateUrl" to the correct value for each page. (Each page includes this user control, and from each page I set the value for navigateUrl.)
But!
On some of my pages I use a linkbutton instead of a hyperlink. But then I have to add that linkbutton from the page.aspx instead of usercontrol.ascx
My problem is that, I want to change the hyperlink in my user control to a linkbutton instead, so I can call methods with that link. (Method is on page.aspx, not within the user control).
What method to call differ from page to page, so I want to set which method to call each time I include the user control.
If I have inside my user control
<asp:LinkButton ID="LinkButton1" runat="server">LinkButton</asp:LinkButton>
And for now I set values within the usercontrol by:
mainPageHeader1.headTitle = "text";
How do I set what method to call for the linkbutton?
Update
In the markup of your UserControl, specify the handler for the click event
<asp:LinkButton OnClick="LinkButton1_Clicked" runat="server" ID="LinkButton1" />
Declare a custom event in your usercontrol
public class MyUserControl
{
public event System.EventHandler LinkButtonClicked;
//add handler for your LinkButton
protected void LinkButton1_Clicked(object sender, EventArgs e)
{
//Raise your custom event here that can be handled in any page that use your control
LinkButtonClicked(sender, e);
}
}
In your page.aspx, add handler for your custom event
protected void MyUserControl2_LinkButtonClicked(object sender, EventArgs e)
{
//handle the event here
}
Update
In your page where you add your control,
<custom:MyUserControl ID="MyUserControl2" runat="server"
LinkButtonClicked="MyUserControl_LinkButtonClicked" />
That's all
Update
Subscribing for the event in code-behind did the work. I have not figured out why it didn't work from the markup.
From the Page_Load of the page in which the UserControl is, do this
MyUserControl2.LinkButtonClicked += new EventHandler(MyUserControl_LinkButtonClicked);
and it should work.
Checks to see if the event has been subscribed to Either codebehind (eventhandler += EventHandler(sender, e) or the aspx markup OnClick="EventHandlerMethodName") - this would be null if it wasn't subscribed to somewhere
I'm trying to change the visibility of a pair of server controls (specifically a LinkButton and Label) that are nested inside the ItemTemplate part of a repeater during the onclick event of the same LinkButton. I've actually already found a solution, but I don't understand why it works and not the way I was trying before. What I was trying originally is as follows:
Nested inside the ItemTemplate of a repeater
<asp:LinkButton ID="lnAdd" CommandArgument='<%#Eval("index") %>' runat="server" Text="Add" OnClick="lnAdd_Click"> </asp:LinkButton>
<asp:Label Visible="false" runat="server" ID="videoAdded" Text="Video Added"></asp:Label>
Then inside of that lnAdd_Click function I have:
Repeater1.FindControl("lnAdd").Visible = false;
Repeater1.FindControl("videoAdded").Visible = true;
where Repeater1 is the id of the repeater these controls are in. This appears to do nothing (though it compiles). I assume this is because for some unknown reason those controls are not found.
What does work is the last answer of the following question: Server controls in an asp.net repeater
The solution appears to be doing something very similar what I've tried to do. Its even using FindControl on a repeater. The only difference (that I can see) is that it obtains the repeater via the sender object. Hmmm, actually, now that I think about it the id of the elements are changed by .Net to some nonesense like ctl00_ContentPlaceHolder1_Repeater1_ctl02_lnAdd when this is actually run, so perhaps that's why its not finding it. But at the same time, in the working solution I just give the normal ID like "lnAdd". Is .Net automatically translating it? Why would it do it for this solution and not for how I was trying to do it originally?
Anyway, I feel like there's something fundamental here that I'm not quite grasping and would appreciate some enlightenment :D
The problem with using Repeater1.FindControl(..)... is that you can't tell which particular Label or LinkButton is being targeted. It's a repeater, so each such item gets assigned a unique identifier as part of the RepeaterItemCollection. In the markup you name it "lnAdd" but that's not what it will be once generated etc.
For reference, here's the code you said worked from the other post:
protected void btnUpdate_OnClick(object sender, EventArgs e)
{
Button b = sender as Button;
if (b != null)
{
RepeaterItem ri = b.Parent as RepeaterItem;
if (ri != null)
{
string name = null;
//Fetch data
TextBox txtName = ri.FindControl("txtName") as TextBox;
I'll explain the reason the above works. First, the button that was clicked is cast from the sender object. We know it's a button since it's the Update button's click handler. Next, we also know that the button appears in a repeater, so it's parent is a RepeaterItem. That's how the ri variable is initialized and cast. With ri available to us, using FindControl on it will now find the given control in that particular RepeaterItem.
You need to call the FindControl on the RepeaterItem that contains the button that was clicked, you do this in the OnItemCommand handler of the repeater, so you get the instance of the RepeaterItem that caused the command.
You have to add a OnItemCommand="ProcessCommands" in the repeater header and then add a ProcessCommand member:
protected void ProcessCommands(object source, RepeaterCommandEventArgs e)
{
LinkButton button = (LinkButton)e.Item.FindControl("lbAdd");
button.Visible = false;
...
}
Hope this helps
I have a page with a repeater in it. I'm writing an event handler so that when the user clicks my WebControl button, the event handler for said button iterates through the items in the repeater using FindControl, then uses some of the controls' values. It seems though, that after the page is loaded, the repeater items populate, but when the button is clicked to post this back, as I iterate through the repeater items, I'm seeing that they're all empty. I don't completely understand the sequencing, but I'm assuming it's because my iteration code is trying to access RepeaterItems that haven't been set yet.
The repeater code is in my OnLoad method. Outside of that, I have my event handler trying to iterate through those items after being clicked. This is essentially what I was trying to do:
protected void MyButton_Click(object sender, EventArgs e)
{
foreach(RepeaterItem item in MyRepeater.Items)
{
MyLabel = (Label)item.FindControl("MyLabel");
}
}
The button is located in the FooterTemplate of the repeater.
<asp:Button runat="server" OnClick="SubmitChecklist_Click" cssclass="BlueSubmit" id="SubmitChecklist" text="Submit" />
Thanks in advance.
Edit: To clarify, the exact error I'm getting is NullReferenceException, when I try to do something, for instance, Response.Write(MyLabel.Text)
Edit: After looking into it more today, this is what I understand to be happening: The repeater is databound on postback. When I then make selections from the generated dropdownlists and hit my button, it posts back again. At this point, the repeater is databound again to it's initial values. So, if I must postback in order to get the users' selections, how can I go about this in the button's eventhandler so that I can get the selected values before that repeater gets databound again?
THe problem, it sounds like, is that you may be binding the data to your repeater on load, but not first checking to make sure it isnt a post back.
example:
You request the page. On Load Fires. You bind the data to the repeater.
You maniupulate the data in the reapter then click your button
The page refreshes with the postback, firing the onload event. The data is rebound to your repeater and all previous data entered has been nullified.
the onclick event is triggered and your code tries to retrieve values that no longer exist.
Make sure your databinding code in your onLoad event is nested within an postback check
if (!Page.IsPostBack)
{
Repeater.DataSource = Datatable;
Repeater.DataBind();
}
I've seen the same thing. I don't understand why, but the data doesn't actually get bound until after all events have fired. I ended up making my data source available at the class level and then indexing.
private DataTable myTable;
protected void Page_Load(object sender, EventArgs e)
{
//populate dataTable
if (!IsPostBack)
{
//databind to repeater
}
}
protected void Submit_Click(object sender, EventArgs e)
{
foreach (RepeaterItem item in repeater1.Items)
{
DataRow row = myTable.Rows[item.ItemIndex];
}
}
Ideal? Certainly not but it works.
Instead of relying on the IsPostBack in my OnLoad, I just seperated all of the different states by putting the databinding of the repeater inside of an event handler after the user selects the first option, rather than relying on the IsPostBack of OnLoad. It was a bit convoluted, but I think I'm doing it the right way this time.