dynamically hiding linkbuttons in a gridview - asp.net

Here is my template field with 2linkbuttons in a gridview.
How do I hide one of the lnkbuttons depending on the user selection
<asp:TemplateField HeaderText="Action">
<ItemTemplate>
<table>
<tr>
<td align="center">
<asp:LinkButton ID="LinkButton1" runat="server" CommandName= "Publish" CommandArgument="<%# Container.DataItemIndex %>"></asp:LinkButton>
</td>
<<td align="center">
<asp:LinkButton ID="LinkButton2" runat="server" CommandName= "Block" HeaderText="Block" CommandArgument="<%# Container.DataItemIndex %>">Block</asp:LinkButton>
</td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
protected void GridView1_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if(Status=="Posted")
{
LinkButton lbtn2 = (LinkButton)e.Row.FindControl("LinkButton2");
lbtn2.Visible = false;
}
if(Status=="Publish")
{
LinkButton lbtn1 = (LinkButton)e.Row.FindControl("LinkButton1");
lbtn1.Visible = false;
}
}
I am doing this using 2 linkbuttons in the template field.
Here if the Status == Posted, then hide LinkButton2.
If ItemType == Published, then hide LinkButton1.
I don't mind using only Linkbutton and set the commandName and commandArgument in aspx.cs`
I am getting an error at...
LinkButton lbtn2 = (LinkButton)e.Row.FindControl("LinkButton2");
lbtn2.Visible = false;
Error: Object reference not set to an instance pointing to lnbt2.Visible = false;
Any clues,
Thanks
Sun

Check to be sure that the row in question is a data row, not a header, etc. The issue is probably because the current row is a header or footer, so it doesn't contain your controls and throws an exception.
protected void GridView1_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
if(Status=="Posted")
{
LinkButton lbtn2 = (LinkButton)e.Row.FindControl("LinkButton2");
lbtn2.Visible = false;
}
if(Status=="Publish")
{
LinkButton lbtn1 = (LinkButton)e.Row.FindControl("LinkButton1");
lbtn1.Visible = false;
}
}
}

I know this is old but I was looking for the same thing; I was able to do it this way also and posting just for reference:
This is in a listview but should be same for gridview:
<asp:LinkButton ID="btnForms" runat="server" Text='MyButton' CommandName="MyCommandName"
Style='<%#GetHiddenTag(Eval("Status").ToString())%>' CommandArgument='<%#Eval("Status")%>' OnClick="MyOnClickCodeBehind" />
Codebehind:
public string GetHiddenTag(string Status)
{
if (Status.ToLower() == "completed")
return "visibility:hidden;";
return "visibility:visible;";
}

Related

Custom validator message not displaying

VS2013, WebForms, .NET 4.51
I have a FormView defined and in my EditTemplate I have as follows:
<div class="form-group">
<asp:Label ID="lblClientClassification" CssClass="col-md-2 control-label" runat="server" for="cblClientClassifications" Text="Kind"></asp:Label>
<div class="col-md-5">
<asp:CheckBoxList ID="cblClientClassifications" runat="server"></asp:CheckBoxList>
<asp:CustomValidator ID="cfvClientKinds" runat="server" Display="Dynamic" CssClass="label label-danger" ErrorMessage="XXXX" ValidationGroup="Default" OnServerValidate="cfvClientClassifications_OnServerValidate"></asp:CustomValidator>
</div>
and then in the code behind:
protected void cfvClientClassifications_OnServerValidate(object aSource, ServerValidateEventArgs aArgs)
{
CustomValidator cvCheckBoxKinds = aSource as CustomValidator;
int checkedCount = 0;
if (cvCheckBoxKinds != null)
{
CheckBoxList cblClientClassifications = GuiClientClassificationsFind();
foreach (ListItem listItem in cblClientClassifications.Items)
{
if (listItem.Selected)
{
checkedCount++;
}
}
if (checkedCount == 0)
{
aArgs.IsValid = false;
cvCheckBoxKinds.ErrorMessage = "Select client kind.";
}
}
}
The OnServerValidate is firing and I am getting to set the validator to invalid as well as setting the error message (Page.IsValid is also false as expected). However, the error text is not displaying. When I view the page source I see:
<span id="ctl00_cphMainContent_fvData_cfvClientKinds" class="label label-danger" style="display:none;">XXXX</span>
instead of the error message I set as well as the fact that it is not visible.
Has anyone got any pointers here on how to track this down? I have looked at similar questions of SO but none of the comments seem to apply. Is this related to FormView perhaps?
Try your control without the CssClass="label label-danger" bootstrap first, and use the code below to check your boxes:
protected void cfvClientKinds_ServerValidate(object aSource, ServerValidateEventArgs aArgs)
{
aArgs.IsValid = cblClientClassifications.SelectedItem != null;
cfvClientKinds.ErrorMessage = "Hey! this is a new message";
}
and I guess you call this line before you fire the above event:
protected void btnValidate_Click(object sender, EventArgs e)
{
Page.Validate();
}
In Short, I think that your problem is either related to your way of finding cblClientClassifications checkBoxList or other code that you haven't stated above.
CheckBoxList cblClientClassifications = GuiClientClassificationsFind();
I decided to try out your case and created a new webform added formview and bind it to northwind categories table then inside edititemtemplate I added a checkboxlist and populated it manually. added CustomValidator double clicked it copied your codebehind and it works for me except for the findcontrol part: GuiClientClassificationsFind();
Here is the formview:
<asp:FormView ID="FormView1" runat="server" DataKeyNames="CategoryID" DataSourceID="SqlDataSource1">
<EditItemTemplate>
...
<asp:CheckBoxList ID="cblClientClassifications" runat="server">
<asp:ListItem>Bir</asp:ListItem>
<asp:ListItem>iki</asp:ListItem>
<asp:ListItem>Üç</asp:ListItem>
<asp:ListItem>Dört</asp:ListItem>
</asp:CheckBoxList>
<asp:CustomValidator ID="cfvClientKinds" runat="server" Display="Dynamic" CssClass="label label-danger" ErrorMessage="CustomValidator" OnServerValidate="cfvClientKinds_ServerValidate"></asp:CustomValidator>
<br />
<asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update" Text="Update" />
<asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel" />
</EditItemTemplate>
</asp:FormView>
And codebehind with your code:
protected void cfvClientKinds_ServerValidate(object aSource, ServerValidateEventArgs aArgs)
{
CustomValidator cvCheckBoxKinds = aSource as CustomValidator;
CheckBoxList cblClientClassifications = (CheckBoxList)FormView1.FindControl("cblClientClassifications");
int checkedCount = 0;
if (cvCheckBoxKinds != null)
{
foreach (ListItem listItem in cblClientClassifications.Items)
{
if (listItem.Selected)
{
checkedCount++;
}
}
if (checkedCount == 0)
{
aArgs.IsValid = false;
cvCheckBoxKinds.ErrorMessage = "Select client kind.";
}
}
}
I Ali Shahrokhi's method is shorter and works as well as yours..
protected void cfvClientKinds_ServerValidate(object aSource, ServerValidateEventArgs aArgs)
{
CustomValidator cvCheckBoxKinds = aSource as CustomValidator;
CheckBoxList cblClientClassifications = (CheckBoxList)FormView1.FindControl("cblClientClassifications");
aArgs.IsValid = cblClientClassifications.SelectedItem != null;
cvCheckBoxKinds.ErrorMessage = "Select client kind.";
}
if you check as you entered edititemtemplate in formview you'll see before submitting that your customvalidators span will be there just as you mentioned that's because server hasn't sent it to the client yet somehow.

How to get Reference to the label in repeater item in code behind

<asp:repeater id="rpt" run="server">
<ItemTemplate>
<asp:LinkButton id="Delete" runat="server" OnCommand="Delete_Command"></asp:linkButton>
<asp:label id="lblMessage" run="server">
</ItemTemplate>
</asp:repeater>
Code Behind:
protected void Delete_Command(object sender, CommandEventArgument e)
{
}
how i get the reference to the "lblMessage" in Delete_Command.
Try this:
protected void Delete_Command(object sender, CommandEventArgs e)
{
LinkButton button = (LinkButton)sender;
Label label = (Label)button.NamingContainer.FindControl("lblMessage");
// do something with the label
}
If you:
Have bound the repeater
Have ViewState enabled
Do not re-bind the repeater earlier in the post back
this should work. If not, please verify that the id of the label is indeed exactly the same as in the ...FindControl("lblMessage");. Also make sure that runat="server" is set on all the controls involved.
Edit: One more thing to check: Search the markup file (the .aspx file) and check if there are any other controls that also use the same event in the code behind. If another control is using the same event handler and that control is not in the repeater, the label will not be found.
means are you want find a lable in Delete_Command event?
in aspx
<asp:Repeater ID="rpt" runat="server">
<ItemTemplate>
<asp:LinkButton ID="Delete" runat="server" OnCommand="Delete_Command"></asp:LinkButton>
<asp:Label ID="lblMessage" run="server">
</ItemTemplate>
</asp:Repeater>
in aspx.cs
protected void Delete_Command(object sender, CommandEventArgs e)
{
foreach (RepeaterItem item in rpt.Items)
{
Label lblMessage = item.FindControl("lblMessage") as Label;
if (lblMessage != null)
{
lblMessage.Text = "";
}
}
}
If You want to make it in your way use following code in
protected void Repeater1_ItemCommand(object source, CommandEventArgs e)
{
(((LinkButton)source).NamingContainer).FindControl("lblName")
}
Another approach.. But something that you can buy
aspx
<asp:Repeater ID="Repeater1" runat="server"
onitemcommand="Repeater1_ItemCommand">
<ItemTemplate>
<asp:Label ID="lblName" runat="server" Text='<%=Eval("Name") %>' ></asp:Label>
<asp:LinkButton runat="server" CommandName="Delete_Command" Text="sd"></asp:LinkButton>
</ItemTemplate>
</asp:Repeater>
.cs
protected void Delete_Command(object sender, CommandEventArgument e)
{
if(e.CommandName != null)// Conditional Check
{
Label label = e.Item.FindControl("lblMessage");
// do something with the label
}
}

Repeater inside repeater not binding properly

I have a repeater inside another. Like so :
<asp:Repeater ID="CategoryRepeater" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<div class="groupbox">
<fieldset>
<legend><%# Container.DataItem %></legend>
<table>
<asp:Repeater ID="ItemRepeater" runat="server">
<ItemTemplate>
<tr>
<td>
<asp:CheckBox id="chkItem" runat="server" Text='<%# Eval("Text")%>' />
<asp:Button ID="btnXRefs" Text="x-refs" runat="server" CssClass="xRefButton" OnClick="btnSelectXRefs_Click" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
</fieldset>
</div>
</ItemTemplate>
</asp:Repeater>
CODE BEHIND :
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
using (var db = new DbContext())
{
CategoryRepeater.DataSource = db.Categories.OrderBy(x => x.Heading).Select(x => x.Heading).Distinct();
CategoryRepeater.DataBind();
}
}
}
protected void ItemBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ItemRepeater");
var item = args.Item as RepeaterItem;
using (var db = new DbContext())
{
childRepeater.DataSource = db.Categories.Where(x => x.Heading == item.DataItem).OrderBy(x => x.Text);
childRepeater.DataBind();
}
}
}
My goal is to first make many groupboxes using the top level, then bind items into each one. So I end up with many small stacked lists of checkboxes.
Problem is, all the top level boxes appear, yet only the first one contains checkbox items, ie. only the first one is bound internally, and the ItemBound method is only called once for the first one.
Any ideas why?
This line
if (args.Item.ItemType == ListItemType.Item)
Should be like this
if(args.Item.ItemType = ListItemType.Item ||
args.Item.ItemType == ListItemType.AlternatingItem)

Why do I have to click a button twice in an ASP.NET repeater to get the command to fire?

I have an ASP.NET page with the following 3 main areas:
1 - list of checkboxes on the left for fitlering results
2 - Repeater that displays the matching results in the middle (with a button for each item)
3 - Repeater that displays the selected items on the right
On initial page load the page will show the data bound checkboxes and will show all results (since nothing has been checked in the filters). As the user checks or unchecks the checkboxes, the page will reload and the matching results will change. So far this part works great.
In the Results Repeater, each item has a Button. When the user clicks the button for an item in the Results the idea is that the item will get added to the Selected Repeater on the right. What is happening is that after I check or uncheck filter checkboxes - the first time that I then try and click on the buttons in the Results repeater, nothing happens. The page just reloads. Then if I click the button a second time, the Repeater Command will fire and the item will get added to the Repeater on the right hand side. Then, as long as I don't change any of the checkboxes I can click on one of the command buttons and it will work right away. But if I check one of the checkboxes in the filters area (which causes the Results to get re-bound) then I have to click one of the buttons twice to get it to fire.
I have a sense that this has something to do with ViewState but I have no idea. Does anyone know why this would be happening?
Below is my code for both the ASPX page and the code behind.
ASPX Code:
<h3>Filters</h3>
<asp:Repeater ID="rptTechnologies" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Technology</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptVerticals" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Vertical</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptIndustries" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Industry</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<asp:Repeater ID="rptSolutions" runat="server" OnItemDataBound="rptFacet_ItemDataBound">
<HeaderTemplate><h4>Solution</h4></HeaderTemplate>
<ItemTemplate><asp:CheckBox ID="chkFacet" runat="server" AutoPostBack="true" OnCheckedChanged="chkFacet_Changed" /><br /></ItemTemplate>
</asp:Repeater>
<h3>Results</h3>
<asp:Repeater ID="rptMatchingSlides" runat="server" OnItemDataBound="rptMatchingSlides_ItemDataBound" OnItemCommand="rptMatchingSlides_Command">
<ItemTemplate>
<h4><asp:Literal ID="litName" runat="server"></asp:Literal></h4>
<asp:Button ID="btnSelect" runat="server" Text="Select" CommandName="Select" />
</ItemTemplate>
<SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>
<h3>Selected</h3>
<asp:Repeater ID="rptSelectedSlides" runat="server" OnItemDataBound="rptSelectedSlides_ItemDataBound">
<ItemTemplate>
<h4><asp:Literal ID="litName" runat="server"></asp:Literal></h4>
</ItemTemplate>
<SeparatorTemplate><hr /></SeparatorTemplate>
</asp:Repeater>
Here is the code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.BindData();
}
}
public List<string> SelectedSlides
{
get
{
if (Session["SelectedIDs"] != null)
{
string[] _ids = Session["SelectedIDs"].ToString().Split(new char[] { '|' });
List<String> _retVal = new List<string>();
foreach (string _id in _ids)
{
_retVal.Add(_id);
}
return _retVal;
}
else
{
return new List<string>();
}
}
set
{
//Set the session value
string _val = "";
foreach (string _id in value)
{
if (_val == "")
{
_val = _id;
}
else
{
_val += "|" + _id;
}
}
Session["SelectedIDs"] = _val;
}
}
protected void BindData()
{
//Filters
rptTechnologies.DataSource = Repository.GetTaxonomyItems();
rptTechnologies.DataBind();
rptVerticals.DataSource = Repository.GetTaxonomyItems();
rptVerticals.DataBind();
rptIndustries.DataSource = Repository.GetTaxonomyItems();
rptIndustries.DataBind();
rptSolutions.DataSource = Repository.GetTaxonomyItems();
rptSolutions.DataBind();
this.BindMatchingSlides();
}
protected void BindMatchingSlides()
{
...build list of ids from checkboxes...
rptMatchingSlides.DataSource = Repository.GetMatchingSlides(_selectedIDs);
rptMatchingSlides.DataBind();
}
protected void BindSelectedSlides()
{
if (this.SelectedSlides.Count > 0)
{
rptSelectedSlides.DataSource = this.SelectedSlides;
rptSelectedSlides.DataBind();
}
else
{
divSelectedSlides.Visible = false;
}
}
protected void rptMatchingSlides_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Literal _litName = (Literal)e.Item.FindControl("litName");
Button _btnSelect = (Button)e.Item.FindControl("btnSelect");
_litName.Text = ...set name here...
_btnSelect.CommandArgument = ...use unique ID of item from database...
_btnSelect.ID = "btnSelect_" + e.Item.ItemIndex;
}
}
protected void rptMatchingSlides_Command(object sender, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Select")
{
Item _slide = ...get data from database based on Command Argument...
if (_slide != null)
{
List<string> _selectedSlides = this.SelectedSlides;
_selectedSlides.Add(_slide.ID.ToString());
this.SelectedSlides = _selectedSlides;
}
this.BindSelectedSlides();
}
}
Thanks to Jeremy - removing the line of code where I was setting the ID fixed it. Doh! Somewhere else I had read that you needed to set a unique value for the IDs of the buttons in a repeater. So that must have been the culprit. Thanks to Jeremy.

How to hide a TemplateField column in a GridView

How can I hide a TemplateField column in a GridView?
I tried the following:
<asp:TemplateField ShowHeader="False" Visible='<%# MyBoolProperty %>' >
<ItemTemplate>
<asp:LinkButton ID="attachmentButton" runat="server" ... />
</ItemTemplate>
but it didn't work and gives the following error:
Databinding expressions are only supported on objects that have a DataBinding event.
System.Web.UI.WebControls.TemplateField does not have a DataBinding event.
I tried also to hide it programmatically, but seems it's not possible to get a column by the name because there iss no name for TemplateField column.
protected void OnRowCreated(object sender, GridViewRowEventArgs e)
{
e.Row.Cells[columnIndex].Visible = false;
}
If you don't prefer hard-coded index, the only workaround I can suggest is to provide a HeaderText for the GridViewColumn and then find the column using that HeaderText.
protected void UsersGrid_RowCreated(object sender, GridViewRowEventArgs e)
{
((DataControlField)UsersGrid.Columns
.Cast<DataControlField>()
.Where(fld => fld.HeaderText == "Email")
.SingleOrDefault()).Visible = false;
}
For Each dcfColumn As DataControlField In gvGridview.Columns
If dcfColumn.HeaderText = "ColumnHeaderText" Then
dcfColumn.Visible = false
End If
Next
If appears to me that rows where Visible is set to false won't be accessible, that they are removed from the DOM rather than hidden, so I also used the Display: None approach. In my case, I wanted to have a hidden column that contained the key of the Row. To me, this declarative approach is a little cleaner than some of the other approaches that use code.
<style>
.HiddenCol{display:none;}
</style>
<%--ROW ID--%>
<asp:TemplateField HeaderText="Row ID">
<HeaderStyle CssClass="HiddenCol" />
<ItemTemplate>
<asp:Label ID="lblROW_ID" runat="server" Text='<%# Bind("ROW_ID") %>'></asp:Label>
</ItemTemplate>
<ItemStyle HorizontalAlign="Right" CssClass="HiddenCol" />
<EditItemTemplate>
<asp:TextBox ID="txtROW_ID" runat="server" Text='<%# Bind("ROW_ID") %>'></asp:TextBox>
</EditItemTemplate>
<FooterStyle CssClass="HiddenCol" />
</asp:TemplateField>
GridView1.Columns[columnIndex].Visible = false;
try this
.hiddencol
{
display:none;
}
.viscol
{
display:block;
}
add following code on RowCreated Event of GridView
protected void OnRowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Cells[0].CssClass = "hiddencol";
}
else if (e.Row.RowType == DataControlRowType.Header)
{
e.Row.Cells[0].CssClass = "hiddencol";
}
}
Am I missing something ?
If you can't set visibility on TemplateField then set it on its content
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton Visible='<%# MyBoolProperty %>' ID="foo" runat="server" ... />
</ItemTemplate>
</asp:TemplateField>
or if your content is complex then enclose it into a div and set visibility on the div
<asp:TemplateField>
<ItemTemplate>
<div runat="server" visible='<%# MyBoolProperty %>' >
<asp:LinkButton ID="attachmentButton" runat="server" ... />
</div>
</ItemTemplate>
</asp:TemplateField>
A slight improvement using column name, IMHO:
Private Sub GridView1_Init(sender As Object, e As System.EventArgs) Handles GridView1.Init
For Each dcf As DataControlField In GridView1.Columns
Select Case dcf.HeaderText.ToUpper
Case "CBSELECT"
dcf.Visible = Me.CheckBoxVisible
dcf.HeaderText = "<small>Select</small>"
End Select
Next
End Sub
This allows control over multiple column.
I initially use a 'technical' column name, matching the control name within.
This makes it obvious within the ASCX page that it's a control column.
Then swap out the name as desired for presentation.
If I spy the odd name in production, I know I skipped something.
The "ToUpper" avoids case-issues.
Finally, this runs ONE time on any post instead of capturing the event during row-creation.
protected void gvLogMessageDetail_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.Header)
{
if (rdlForImportOrExport.SelectedIndex == 1)
{
e.Row.Cells[3].Visible = false;
e.Row.Cells[4].Visible = false;
e.Row.Cells[5].Visible = false;
}
else
{
e.Row.Cells[3].Visible = true;
e.Row.Cells[4].Visible = true;
e.Row.Cells[5].Visible = true;
}
}
if (e.Row.RowType == DataControlRowType.DataRow) //skip header row
{
try
{
if (rdlForImportOrExport.SelectedIndex == 1)
{
e.Row.Cells[3].Visible = false;
e.Row.Cells[4].Visible = false;
e.Row.Cells[5].Visible = false;
}
else
{
e.Row.Cells[3].Visible = true;
e.Row.Cells[4].Visible = true;
e.Row.Cells[5].Visible = true;
}
}
catch
{
ClientScript.RegisterStartupScript(GetType(), "Expand", "<SCRIPT LANGUAGE='javascript'>alert('There is binding problem in child grid.');</script>");
}
}
}
This can be another way to do it and validate nulls
DataControlField dataControlField = UsersGrid.Columns.Cast<DataControlField>().SingleOrDefault(x => x.HeaderText == "Email");
if (dataControlField != null)
dataControlField.Visible = false;

Resources