Lost Focus Events for a Control Within GridView - asp.net

I have multiple textboxes and dropdown lists within my GridView. For one particular textbox I need trigger a server event which gets data from the database and fills it in other columns of the Grid. Is there a simple way to do it or a slightly complicated way as detailed here
I have no problems implementing the above method or thinking of a work around but then thought that there is Cell Lost Focus in a grid control surprises me a little. Am I missing something ? Any help on this would appreciated.

You can set AutoPostBack to true and handle it's TextChanged event.
<asp:GridView ID="GridView1" runat="server" EmptyDataText="It's Empty.">
<Columns>
<asp:TemplateField HeaderText="Name">
<ItemTemplate>
<asp:TextBox ID="txtName"
runat="server"
Text='<%#Eval("Name") %>'
AutoPostBack="true"
OnTextChanged="NameChanged" >
</asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</GridView>
in codebehind:
protected void NameChanged(Object sender, EventArgs e)
{
var txtName = (TextBox) sender;
var row = (GridViewRow) txtName.NamingContainer;
// you could find other controls in this GridViewRow via
// row.FindControl("ControlID") in case of a TemplateField or
// row.Cells[0].Text (0 = index of column) in case of a BoundField
}

Related

Why does GridView inside a Repeater (or other similar controls) has the event PageIndexChanging as NOT IMPLEMENTED

Simply I want to know WHY!
Is it the DataSource Type of the GridView? or the Repeater inner implementation?
The error text:
The GridView 'grdArticles' fired event PageIndexChanging which wasn't handled.
Here's the Markup code, I think it's familiar enough for everyone.
<asp:Repeater ID="rptCategories" DataSourceID="ldsCategories" runat="server">
<ItemTemplate>
<asp:GridView runat="server" ID="grdArticles" AllowPaging="true" GridLines="None" DataKeyNames="id" AutoGenerateColumns="false" DataSource='<%# Eval("Articles") %>'>
<Columns>
<asp:TemplateField ShowHeader="false" ItemStyle-Width="100%" FooterStyle-Width="100%">
<ItemTemplate>
<div class="article-menu-item">
<h1>
<asp:HyperLink ID="lnkTitle" CssClass="article-menu-title" runat="server" Text ='<%# Eval("title") %>'
NavigateUrl='<%# Vars.ArticleUrl + "?action=view&id=" + Eval("id") %>' ></asp:HyperLink>
</h1>
<!-- Date -->
<div class="article-menu-date">
<asp:Label ID="Label1" runat="server" Text='<%# Eval("date") %>'></asp:Label>
</div>
<!-- Meta Content -->
<div class="article-menu-meta">
<asp:Label ID="lblContent" runat="server" Text='<%# Bind("meta") %>'></asp:Label>
</div>
<div class="article-menu-delete">
<asp:LinkButton ID="btnDelete" Text="Delete" runat="server" OnClick="btnDelete_Click" TargetID='<%# Eval("id") %>' />
</div>
<!-- Line -->
<div style="border-bottom: 1px solid #ccc"></div>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
<EmptyDataTemplate>
<p>No items to view!</p>
</EmptyDataTemplate>
</asp:GridView>
<br />
</ItemTemplate>
</asp:Repeater>
EDIT:
If I implement the OnPageIndexChanging event using the code (And modified the aspx gridview markup to handle the event):
ASPX:
<asp:GridView runat="server" ID="grdArticles" AllowPaging="true" OnPageIndexChanging="grdArticles_PageIndexChanging" GridLines="None" DataKeyNames="id" AutoGenerateColumns="false" DataSource='<%# Eval("Articles") %>'>
cs:
protected void grdArticles_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView grd = sender as GridView;
grd.PageIndex = e.NewPageIndex;
grd.DataBind();
}
Another exception thrown:
Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.
Edit #2:
After following Mr #Garrison solution, and handling the Repeater itemDataBound event using the code:
protected void rptCategories_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
GridView grd = e.Item.FindControl("grdArticles") as GridView;
if (grd != null)
{
DatabaseDataClassesDataContext dc = new DatabaseDataClassesDataContext();
grd.DataSource = dc.Articles.Where(a => a.category_id == (e.Item.DataItem as Category).id);
grd.DataBind();
}
}
Got another problem: No exceptions thrown, but when navigating to another page, the GridView shows NO ROWS!!
I really think that there is an issue in the repeater core implementation!
You have two options:
Remove Paging: Set AllowPaging="false" in your GridView.
Implement Paging: Set OnPageIndexChanging="grdArticles_PageIndexChanging"
Create a method in your code behind that looks like this:
protected void grdArticles_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
grdArticles.PageIndex = e.NewPageIndex;
grdArticles.DataBind();
}
Now that we're past the first issue, let's tackle the second one. You need to handle your Repeater's ItemDataBound event. Inside there, first find your GridView with the following code:
var grdArticles = (GridView)e.Item.FindControl("grdArticles");
Now you have access to the GridView, but you've got to find the set of Articles you want to data bind to the GridView. I don't know how you're retrieving your data, so I leave that portion up to you, but once you find your list of articles to bind, use the following code:
grdArticles.DataSource = relevantArticles; // relevantArticles is a stand-in variable name, because I don't know how you're going to do it
grdArticles.DataBind();
I've figured out -with the help of others answers- that's the problem is with the DataSource type, which is -in my example- the Eval("Items"), I don't know really what's the type of it, but it's not supporting auto paging.
So, one way to step out of it -without rewriting paging logic and writing lots of code and get some mess with Sessions and querying- is to handle the ItemDataBound of the container of the GridView, I mean the Repeater or DataList, or you can handle the OnPreRender of the GridView and create the DataSource that supports the auto paging like LinqDataSource.
You may also need to handle the PageIndexChanging of the GridView like what #Garrison told us:
protected void grdArticles_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
grdArticles.PageIndex = e.NewPageIndex;
grdArticles.DataBind();
}
After that I got everything work :)

ASP.Net, Datagrid: render column differently based upon value?

I've looked and looked and I'm not seeing the answer to this.
I have a datetime value that's getting bound to a datagrid. I want to display the column as either the datetime, or if said datetime is null, an asp:button for the users to click.
I can't seem to find specific information on how to do it. My initial approach was <% if blah blah then %>, but I can't seem to get at the dataitem in that manner. I've also looked at events, but nothing is jumping out at me as being the solution (I'm sure I'm wrong, I'm just not seeing it).
Any suggestions?
Assuming that you actually mean GridView instead of DataGrid, otherwise it would work similarly(ItemDataBound etc.).
You could use a TemplateField with a Label and a Button and switch visibility of both controls in RowDataBound:
protected void Grid_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var row = ((DataRowView)e.Row.DataItem).Row;
DateTime? date = row.Field<DateTime?>("DateColumn");
var lblDate = (Label)e.Row.FindControl("LblDate");
var btnDate = (Button)e.Row.FindControl("BtnDate");
lblDate.Visible = date.HasValue;
btnDate.Visible = !date.HasValue;
if (date.HasValue) lblDate.Text = date.ToString();
}
}
aspx:
<asp:GridView ID="GridView1" AutoGenerateColumns="false" OnRowDataBound="Grid_RowDataBound" runat="server">
<Columns>
<asp:TemplateField HeaderText="DateColumn">
<ItemTemplate>
<asp:Label ID="LblDate" runat="server"></asp:Label>
<asp:Button ID="BtnDate" Text="click me" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
You should use Gridview if you are using ASP.NET 3.5 or higher. Using a templatefield column, add a button and label. In the rowdatabound event, you should show only the button or the label, using the Visible property, depending on whether there's a date or not.

ASP .Net Gridview bind to data only when clicking update

Here's how I want the control to work. Click edit on a row in gridview. There's a textbox in one of the columns, I would like it to be blank even though there's data in the database for that column, but when I click update I would like whatever new text the user entered to update the database for that column. It would be one-way databinding, but the other way?
Here's how I did it using an sql datasource with the select,update and delete methods generated.
First, you'll need to make any column that you want to edit like this a template field with an item template and and edit item template:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
DataKeyNames="ID" DataSourceID="SqlDataSource1"
onrowupdating="GridView1_RowUpdating">
<Columns>
<asp:TemplateField HeaderText="City" SortExpression="City">
<ItemTemplate>
<asp:Label runat="server" Text='<%# Eval("City") %>'></asp:Label>
</ItemTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" ID="txtCityEdit" Text=""></asp:TextBox>
</EditItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Next handle the gridview's on update event:
protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
//find the value control who's value you want
TextBox tb = (TextBox)GridView1.Rows[e.RowIndex].FindControl("txtCityEdit");
//since we are not using a bound column for city we need to explicitly insert the value
//into the update parameter.
SqlDataSource1.UpdateParameters["City"].DefaultValue = tb.Text;
}
Even if you are using something other than an SQL Datasource this should be the basic solution.
// Find notes textbox
TextBox tb = (TextBox)MyActiveReferrals.Rows[e.RowIndex].FindControl("NotesTextBox");
e.NewValues.Add("Notes", tb.Text);
I used a linq data source.

give the ID as a custom attribute to checkbox in a grid for manual update

I like to update just the value of my checkbox in a asp grid.
so i thought i bind the id to the checkbox ( but how?) and fire an update in code behind by clicking the checkbox.
but how can I get the ID, and where I have to bind this id to get it in code behind in the event?
Thanks
Here is what I've managed to do. (Be aware there should be an easier way to do this. I'm very new to ASP.NET)
Here you have a TemplateField in a GridView. Inside it there is an UpdatePanel and the CheckBox is inside it. This is done to make the checking of the TextBox do post in the backgroung (ajax). You might not need it (UpdatePanel) at all.
<asp:TemplateField HeaderText="Private" SortExpression="IsPrivate">
<ItemTemplate>
<asp:UpdatePanel ID="upIsPrivate" runat="server" UpdateMode="Always" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:CheckBox ID="chkIsPrivate" runat="server" OnCheckedChanged="chkIsPrivate_CheckedChanged" AutoPostBack="true" />
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
And this is the method that handles this. Notice that I get the Id from the GridViewRow that contains the CheckBox:
GridViewRow row = (GridViewRow)((CheckBox)sender).Parent.Parent.Parent.Parent;
protected void chkIsPrivate_CheckedChanged(object sender, EventArgs e)
{
if (editMode)
{
GridViewRow row = (GridViewRow)((CheckBox)sender).Parent.Parent.Parent.Parent;
Int32 id = (Int32)uxPhoneCallList.DataKeys[row.RowIndex]["Id"];
CheckBox isPrivate = (CheckBox)row.FindControl("chkIsPrivate");
PhoneCall phoneCall = PhoneCallManager.GetById(id);
...
}
}

DataBound DropDownList in DataGrid - order of binding

I have a DataGrid that looks like this (slightly simplified here):
<asp:DataGrid ID="grdQuotas" runat="server" AutoGenerateColumns="False">
<HeaderStyle CssClass="quotas-header" />
<Columns>
<asp:TemplateColumn>
<HeaderTemplate>
Max order level</HeaderTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddlMaxOrderLevel" runat="server" DataSourceID="xdsOrderLevel"
DataTextField="Text" DataValueField="Value" SelectedValue='<%# Bind("MaxOrderLevel") %>'>
</asp:DropDownList>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
<asp:XmlDataSource ID="xdsOrderLevel" runat="server" DataFile="~/App_Data/OrderLevels.xml">
</asp:XmlDataSource>
In my Page_Load event handler I am creating a DataTable containing default values and DataBinding it to the DataGrid.
The problem is that this is taking place before the DropDownList ddlMaxOrderLevel has been bound to its DataSource, so I get a runtime error telling me that the SelectedValue cannot be set.
If ddlMaxOrderLevel was not in a DataGrid I could just call DataBind() on it. However I cannot do that in this scenario - since it is in an ItemTemplate.
Can anyone suggest a workaround or alternate approach?
You could do the Databinding of the DropDownlist in the Databound event of the DataGrid.
Edit:
I will give you an example that i have tested:
protected void dg_ItemDataBound(object sender, DataGridItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Header && e.Item.ItemType != ListItemType.Footer)
{
DropDownList dl = (DropDownList)((DataGridItem)e.Item).FindControl("ddlMaxOrderLevel");
dl.DataSource = levels;
dl.DataBind();
dl.SelectedValue = ((DataRowView)e.Item.DataItem)["number"].ToString();
}
}
Create another DataSource and bind it to the DataGrid. Where the SelectMethod would return the default values in a simple object.
Then all the binding should happily work together.

Resources