Hiding/Unhiding Control in Gridview’s column - shifting problem - c#

This is a follow up to my previous question:
link text
In gridview's column i have a linkbutton and a label under it.
I want to hide/unhide label when linkbutton is clicked. I use javascript because i don't want any postbacks.
The code:
protected void gvwComments_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
LinkButton lButton = ((LinkButton)e.Row.Cells[2].FindControl("lbtnExpand"));
Label label = ((Label)e.Row.Cells[2].FindControl("lblBody"));
lButton.Attributes.Add("onclick", string.Format("HideLabel('{0}'); return false;", label.ClientID));
}
}
function HideLabel(button) {
var rowObj = document.getElementById(button);
if (rowObj.style.display == "none") {
rowObj.style.display = "block";
}
else {
rowObj.style.display = "none";
}
}
The problem is that when I unhide the label by clicking on button, linkbutton is shifted a a bit upper it's original position in the cell.
Is it possible to preserve linkbutton's position in the gridviews cell?

I would suggest you change your CSS from display:none to display:hidden which will hide the control but maintain the space preventing the jumping around.

The trick is to use the keyword "this" and then get a reference to the row and change the label from there.
I have a post here, where I have a GridView with a CheckBox column and a Name column. When the CheckBox is checked the background color of the Name on that row changes. I do this starting with this attribute in the CheckBox's column:
onclick="toggleSelected(this)"
then I have a JavaScript function that finds the row and changes the next cell:
function toggleSelected(sender) {
// note that at this point the "this" is now "sender" -which is the checkbox
// get a reference to the row using the helper function - see below.
var row = GetParentElementByTagName(sender, "TR");
if (sender.checked)
row.cells[1].className = "selected";
else
row.cells[1].className = '';
}
This uses the helper function:
function GetParentElementByTagName(element, tagName) {
var element = element;
while (element.tagName != tagName)
element = element.parentNode;
return element;
}
You have a slightly different requirment so you would use something like:
var lbl = row.cells[1].childNodes[0].getElementsByTagName('label')
to get a reference to your label.

Related

Multiple link button on same cell of gridview - click event is not firing - c#

I have created multiple linkbutton on same cell of gridview row. But it's click event is not firing. On click event, I have to get StudentID defined in RowDataBound of Gridview.
protected void gvStudent_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//loop through the cell.
for (int j = 1; j < e.Row.Cells.Count; j++)
{
string[] arrLinks = null;
if (!string.IsNullOrEmpty(e.Row.Cells[j].Text.ToString()) && e.Row.Cells[j].Text.ToString() != " ")
{
arrLinks = e.Row.Cells[j].Text.Split(',');//Rahul-3495,Meera-2323
}
if (arrLinks != null)
{
for (int i = 0; i < arrLinks.Length; i++)
{
LinkButton btnLink = new LinkButton();
string StudentName= (arrLinks[i].Split('-').First()).ToString();//Rahul
string StudentID = (arrLinks[i].Split('-').Last()).ToString();//3495
btnLink.ID ="btn_" + StudentID;
btnLink.Text = StudentName + "<br>";
// btnLink.Click += new EventHandler(StudentButtonsclick);
btnLink.CommandName = "btnLink";
e.Row.Cells[j].Controls.Add(btnLink);
}
}
}
}
}
protected void gvStudent_RowCommand(sender s, GridViewCommandEventArgs e)
{
if (e.CommandName == "btnLink")
{ }
}
<asp:GridView ID="gvStudent" runat="server" AutoGenerateColumns="true"
CssClass="gridview_alter"
OnRowDataBound="gvStudent_RowDataBound" OnRowCommand="gvStudent_RowCommand">
</asp:GridView>
Ok, the problem is that controls that require events that are created "after" the page has been rendered cannot really be wired up. You would have to move the code to a earlier event. So you are free to add controls, but they will in "most" cases be rendered TOO LATE to have events attached. Thus when you click on the link button, nothing fires.
So there are two solutions I can think of that will work.
First, set the control to have a a post back URL, and include a parameter on that post back.
eg this:
Dim lnkBtn As New LinkButton
lnkBtn.Text = "<br/>L" & I
lnkBtn.ID = "cL" & I
lnkBtn.PostBackUrl = "~/GridTest.aspx?r=" & bv.RowIndex
If you put a PostbackUrl, then when you click on the button, the page will post back. However, the grid row events such as rowindex change, or row click event etc. will NOT fire. So, if you willing to have a parameter passed back to the same page as per above, then you can pass the 1-3 (or 1-N) values you have for each control.
Of course that means you now have a parameter on the web page URL (and users will see this). You of course simply pick up the parameter value on page load with the standard
Request.QueryString["ID"] or whatever.
However, another way - which I think is better is to simple wire up a OnClickClick() event in js, and thus do this:
I = 1 to N
Dim lnkBtn As New LinkButton
lnkBtn.Text = "<br/>L" & I
lnkBtn.ID = "cL" & I
lnkBtn.OnClientClick = "mycellclick(" & I & ");return false;"
Now in above note how I am passing "I" to the js routine. You would pass your 200, 300 or whatever value you want.
then you script will look like this:
<script>
function mycellclick(e) {
__doPostBack("MySelect", e);
}
</script>
So above simply takes the value passed from the cell click (and linkbutn), and then does the postback with a dopostback. I used "MySelect", and you can give that any name you want.
Now, in the on-load event, you can simply go like this:
If Request("__EVENTTARGET") = "MySelect" Then
Dim mypassvalue As String = Request("__EVENTARGUMENT").ToString
Debug.Print("row sel for MySelect = " & mypassvalue)
End If
So, you are 100% correct - clicking on those controls does NOT fire server side event, and they are wired up too late for this to occur. so you can and often do say add some columns or controls to a gridview, but they are created and rendered TOO LATE for the events to be wired up (and thus they don't fire when clicked on).
But, you can add a postback to the lnkbutton, and you can also add a OnClickClick() event (JavaScript function call) and they will both work. I don't like parameters in the URL appearing when you click, so I think the js script call as per above works rather nice.
So while in the comments I noted (and suggested) that you have to set the CommandName="Select". This suggesting still holds true (without CommandName = select, then the rowindex will not fire. You can't use just ANY name - it MUST be select. However this ONLY works if the control is part of the grid and not added on the fly. As noted, it might be possible to move the grid event to "earlier" event (page initialize) but it going to be a challenge and will require you to re-organize the page. The most clean, and one that does not require parameters in the URL is adding that js OnClientClick() event. You can however set the controls postbackurl and along with a parameter in the URL, and that also can work well if you open to URL with parameters (I don't like them).
I would recommend using CommandName and OnRowCommand event for GridView. Here is how you should do it:
protected void gvStudent_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//loop through the cell.
for (int j = 1; j < e.Row.Cells.Count; j++)
{
string[] arrLinks = null;
if (!string.IsNullOrEmpty(e.Row.Cells[j].Text.ToString()) && e.Row.Cells[j].Text.ToString() != " ")
{
arrLinks = e.Row.Cells[j].Text.Split(',');//Rahul-3495,Meera-2323
}
if (arrLinks != null)
{
for (int i = 0; i < arrLinks.Length; i++)
{
LinkButton btnLink = new LinkButton();
string StudentName= (arrLinks[i].Split('-').First()).ToString();//Rahul
string StudentID = (arrLinks[i].Split('-').Last()).ToString();//3495
btnLink.ID = "btn_" + StudentID; // Good to concatenate a string instead just a number in the ID.
btnLink.Text = StudentName + "<br>";
btnLink.CommandName = "btnLink"; // Add a CommandName
e.Row.Cells[j].Controls.Add(btnLink);
}
}
}
}
}
protected void GridView1_RowCommand(sender s, GridViewCommandEventArgs e)
{
if (e.CommandName == "btnLink")
{
// Link Button was clicked.
var linkButton = (LinkButton)sender;
if (linkButton != null)
{
var studentId = linkButton.ID.Replace("btn_", ""); // Remove the concatenated string from the id.
// Do stuff with the student id.
// I would highly not recommend getting the id from a button element, as it could be modified using browser inspect elements. Instead use, GridView DataKeys.
}
}
}
You should add RowCommand event in your GridView as well to get it going. e.g:
<asp:GridView runat="server" ID="GridView1" OnRowCommand="GridView1_RowCommand">
<!-- Rest of the elements -->
</asp:GridView>

Link button is not being disabled, how to disable link button in grid-view?

I have a Gridview with a link button in the first column. Once the link button is clicked I want to open a window, but I also want to disable the link button.
Not working
Once I click on the link button the window behavior is the expected one. However, the link button is not disabled. Therefore is permitting me to click on it over and over again.
The front end is:
<asp:LinkButton runat="server" ID="lnkbtnView" CommandArgument='<%# Eval("Id")%>' OnCommand="GetViewOnClientClick" >View<br/></asp:LinkButton>
GetviewonClientClick method:
protected void GetViewOnClientClick(object sender, EventArgs e)
{
LinkButton lb = (LinkButton)sender;
GridViewRow row = (GridViewRow)lb.NamingContainer;
if (row != null)
{
int index = row.RowIndex;
LinkButton link = (LinkButton)row.FindControl("lnkbtnView");
link.Enabled = false;
}
}
Why is not working as expected?
That's what I want to solve. I suspect I might have to do a rebinding or something related, but I don't quite understand what is really going on. Therefore, I don't know how to implement it.
I never found the solution to edit the control in that method. What I did instead was to read on the cycles of the grid view events.
https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.gridview_events(v=vs.110).aspx
If you read the previous link, you'll know that once you click on a record it is going to trigger a call to the RowDataBound event. Therefore; in the RowDataBound event you'll have access to the control and apply logic to edit the control's properties (such as color, disabled, enabled...).
Therefore, because I have two types of records in this grid view, I needed to store and to make persistent what records were clicked.
This is my fix to keep track of the clicked rows:
protected void gvList_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
gvs.SetHeaderArrows(e);
}
if (e.Row.RowType == DataControlRowType.DataRow)
{
int Id = (int)(e.Row.RowIndex);
int? inspID = convert.ToIntQ(DataBinder.Eval(e.Row.DataItem, "InspectionID"));
string rowclicked = string.Format("clickedrow{0}", Id);
if (convert.ToIntQ(Session[rowclicked]) != null)
{
if (Id == Convert.ToInt32(Session[rowclicked]))
{
LinkButton button = (LinkButton) e.Row.FindControl("lnkbtnView");
button.ForeColor = Color.Gray;
button.Enabled = false;
}
else
{
LinkButton button = (LinkButton)e.Row.FindControl("lnkbtnView");
button.ForeColor = Color.DarkBlue;
button.Enabled = true;
}
}

How to find all textboxes in a gridview row?

I have a GridView on a website, that have different controls in each row (eg.: textbox, label, dropdownlist). I need to find all textboxes and set the enabled property to false, so the user won't be able to edit them. I tried the code below, but it dosn't work, 'c' never recognised as a textbox, so it never changes the property.
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (a)
{
foreach (Control c in e.Row.Controls)
{
if (c is TextBox)
{
((TextBox)(c)).Enabled = false;
}
}
}
}
I think you should try like this:
TextBox tb = e.Row.FindControl("textbox_name") as TextBox;
tb.Enabled = false;
Your textboxes must be nested within other controls, most likely cells inside the row. That is why you cannot find them just iterating through immediate children.
If you have a list of IDs of the text boxes, you should use FindControl:
((TextBox)e.Row.FindControl("TextBoxID")).Enabled = false;
Otherwise you will need to recursively find your controls of necessary type. See this thread for code sample.
One more option, if a is relatively easy to calculate, is to use in the markup directly, like so:
<asp:TextBox ... Enabled='<%# a %>' />
This depends a lot on the details of how a is derived. If it is a protected or public field of the page class, just the code above should work. If it is calculated based on row, you may need to turn it into protected method and pass params into it:
Enabled='<%# GetEnabled(Eval("Prop1"), Eval("Prop2")) %>'
And also want to put some updation.
There are different types of rows in gridview (header,footer,datarow,etc)
so for making little faster for the control find.
Try below (check the if condition)
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Find the TextBox control.
TextBox txtName = (e.Row.FindControl("txtName") as TextBox);
txtName.Enabled = false;
//or
TextBox txtName1 = (TextBox)e.Row.FindControl("txtName");
txtName1.Enabled = false;
}
}
You should search controls inside the cell instead of Row.
foreach (TableCell cell in e.Row.Cells)
{
foreach (Control c in cell.Controls)
{
if (c is TextBox)
{
((TextBox)(c)).Enabled = false;
}
}
}

Hiding gridview rows takes two tries

I created a gridview with textboxes in the footer for the insert function. I have the footer defaulted to hidden. I also added an 'Add' button to the first column of the header that I will use to show the footer. I also want this button to hide the gridview rows and the button itself, effectively keeping them from hitting the add button when they're already in insert mode, and bringing the footer to the top of the gridview.
(VS2008 C#)
protected void btnNewUser_Click(object sender, EventArgs e)
{
for (int i = 0; i < GridView2.Rows.Count; i++)
{
GridView2.Rows[i].Visible = false;
}
GridView2.ShowFooter = true;
GridView2.HeaderRow.FindControl("btnNewUser").Visible = false;
}
When I click the button the first time, the footer displays, but the rows and button stay visible. When I click the button a second time, the rows and button become hidden. How do I get this all to work in one click?
Edit: If I comment out GridView2.ShowFooter = true; it will hide the rows just fine. Is there some kind of a postback/refresh/databind, etc.happening when I show the footer?
Yes, changing the value of the ShowFooter property marks the gridview for rebinding.
The implementation looks as follows:
set
{
bool showFooter = this.ShowFooter;
if (value != showFooter)
{
this.ViewState["ShowFooter"] = value;
if (base.Initialized)
{
base.RequiresDataBinding = true;
}
}
}
A possible workaround could be to set to footer visibility instead. (And always have ShowFooter set to true)
GridView2.FooterRow.Visible = true/false;

Add links to all cells in gridview

I have a data source with an unknown number or rows and columns. I am using a grid view which is set to auto generate columns. I need to turn each item in each cell into a link button to post back for processing. I know how to dynamically add a control to a known row and cell but when i don't know the column name it makes it difficult. Any suggestions on how to do produce these results?
Try this as a starting point:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
foreach (TableCell cell in e.Row.Cells)
{
HyperLink myLink = new HyperLink();
myLink.NavigateUrl = "somewhere.aspx";
if (cell.Controls.Count > 0)
{
while (cell.Controls.Count > 0)
{
myLink.Controls.Add(cell.Controls[0]);
}
}
else
{
myLink.Text = cell.Text;
}
cell.Controls.Add(myLink);
}
}
}
Note: I've written the solution up in C#, as per your tag, but I notice your last comment is in VB. Let me know if you need me to re-post in VB (in which case you should update the tag).

Categories