Extract a BoundField to a Label - asp.net

I have a DetailsView which works fine using a data access layer and a query string. However, I'd like to extract one of the fields and use it as the text in a label to go above the DetailsView as a title to that page.
Is this possible? And if so, how?
This is an abstract of the DetailsView:
<Fields>
<asp:BoundField DataField="bandname" HeaderText="Band" />
<asp:BoundField DataField="contactname" HeaderText="Contact" />
<asp:BoundField DataField="county" HeaderText="County" />
</Fields>
and the code behind:
if (Request.QueryString.Count != 0)
{
int id = int.Parse(Request.QueryString["bandid"]);
dtvBand.Visible = true;
List<Band> bandDetails = new List<Band> { BandDAL.AnonGetAllBandDetails(id) };
dtvBand.DataSource = bandDetails;
dtvBand.DataBind();
}
What I'd like to do is take the data in the first BoundField row and make it the text of a label. Pseudocode:
Label1.Text = (<asp:BoundField DataField="band")

I would not try to find the text on the DetailsView but in it's DataSource. You could use the DataBound event which is triggered after the DetailsView was databound, so it's ensured that the DataItem exists.
It depends on the Datasource of your DetailsView. Often it is a DataRowView. You have to cast it, then you can access it's column:
protected void DetailsView1_DataBound(Object sender, EventArgs e)
{
DetailsView dv = (DetailsView)sender;
string yourText = (string)((DataRowView)dv.DataItem)["ColumnName"];
Label1.Text = yourText;
}
If it's not a DataRowView use the debugger to see what dv.DataItem actually is.

I managed to achieve what I wanted using:
string titletext = dtvBand.Rows[0].Cells[1].Text.ToString();
dtvBand.Rows[0].Visible = false;
lblBand.Text = titletext;
It takes the first row of the DetailsView, puts it above the rest in a Label so it can be formatted as a header, then hides the first row of the DetailsView.

How about using a TemplateField as what Tim mentioned:
<Fields>
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="lblName" runat="server" Text='<%# Eval("Band") %>' />
</ItemTemplate>
</asp:TemplateField>
</Fields>

Related

How to read the data of the row when clicked the button in the same row

I have the data displayed in the grid as follows:
StartDate Enddate Button
16/3/2013 17/3/2013 Signup---> this is an ASP button
18/3/2013 19/3/2012 Signup----> this is an ASP button
20/3/2012 20/3/2012 Signup----> this is an ASP button
I want asp.net with c# code when i clicked on first row signup button i want to get the data of the first row. If clicked on second row i want only the data of the second row startdate and end time. How can i acheive this? Please hep me in this regard.
In the asp button, Set properties: CommandName="SignUp" CommandArgument='<%# Container.DataItemIndex%>'
Now, when this button is clicked it calls Gridview's RowCommand Event
In that event e.CommandArgument contains your Row Number.
GridViewRow gvr= gvLinkImages.Rows[e.CommandArgument];
Now you can use gvr.Cells[column number] to get the particular text (Not recommended)
or use findcontrol to get the label or literal of the start/end date( assuming you are using Templatefields)
Sample code below
C#
protected void gvResults_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "SignUp")
{
int rwNumber = Convert.ToInt32(e.CommandArgument);
GridViewRow gvr = gvResults.Rows(rwNumber);
System.DateTime rowStartDate = default(System.DateTime);
System.DateTime rowEndDate = default(System.DateTime);
//If you are using Templatefields
Literal lblRowStartDate = gvr.FindControl("lblStartDate") as Literal;
Literal lblRowEndDate = gvr.FindControl("lblEndDate") as Literal;
rowStartDate = Convert.ToDateTime(lblRowStartDate.Text);
rowEndDate = Convert.ToDateTime(lblRowEndDate.Text);
//Incase you are not using TemplateFields or Autobinding your grid
rowStartDate = Convert.ToDateTime(gvr.Cells[0].Text);
rowEndDate = Convert.ToDateTime(gvr.Cells[1].Text);
}
}
ASPX
<asp:GridView runat="server" ID="gvResults" AutoGenerateColumns="false" OnRowCommand="gvResults_RowCommand">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Literal runat="server" ID="lblStartDate" Text='<%#Container.DataItem.StartDate%>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Literal runat="server" ID="lblStartDate" Text='<%#Container.DataItem.EndDate%>'></asp:Literal>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" ID="btnSignUp" CommandName="SignUp" CommandArgument="<%# Container.DataItemIndex %>" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

How to add TemplateField created in .aspx markup to GridView column in code behind?

Basically, I want a button that adds a new column with my controls (i.e. TextBox, DropDownList) to my GridView.
Already seems simple enough with this:
protected void btn_AddColumn_Click(object sender, EventArgs e)
{
TemplateField MyTemplateField = new TemplateField();
gv_SupplierPrices.Columns.Add(MyTemplateField);
}
Say I want to add this markup:
<asp:TemplateField>
<ItemTemplate>
<table style="width:100%;">
<tr>
<td><asp:TextBox ID="tbox_ItemPrice" runat="server"></asp:TextBox></td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
Here's the problem. How do I use a TemplateField I created in markup on my .aspx page and add it to my new column on code behind? I don't know and don't want to create my TemplateField on code behind because I will not be able to create elaborate or fancy markups. They will just look like controls that are next to each other. It's very ugly.
P.S. If anyone is curious as to what I'm actually making, I'm basically turning my GridView into an Excel-like gridsheet. New columns represent a supplier and each row represents an item the supplier is selling. They want it that way because it's easier to compare prices side-by-side whilst they're filling it up.
Update: I already figured it out. I can simply call the column of my GridView from codebehind that represents the TemplateField I created in markup, then I add it.
<asp:GridView ID="gv_SupplierTable" runat="server" AutoGenerateColumns="False"
DataKeyNames="Id" DataSourceID="TestTable_DS">
<Columns>
<asp:BoundField DataField="Id" HeaderText="Id" InsertVisible="False"
ReadOnly="True" SortExpression="Id" />
<asp:BoundField DataField="AccountType" HeaderText="AccountType"
SortExpression="AccountType" />
<asp:TemplateField AccessibleHeaderText="MyTemplateFieldCreated in Markup">
<ItemTemplate>
<table style="width:100%;">
<tr>
<td><asp:TextBox ID="tbox_ItemPrice" runat="server"></asp:TextBox></td>
</tr>
</table>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Then in codebehind, I can access that column that represents the TemplateField created in markup and add it again on button click, like a new copy:
protected void btn_AddColumn_Click(object sender, EventArgs e)
{
DataControlField newColumn = this.gv_SupplierTable.Columns[2];
//this column at index #2 from my GridView represents
the TemplateField created in markup since the columns
at index #0 and index #1 are DataBound.
gv_SupplierTable.Columns.Add(newColumn);
}
You can toggle the visibility of each column.
gv_SupplierPrices.Columns[10].Visible = true;
In your position, I'd consider biting the bullet and investigating creating templated fields programmatically. It would be a nuisance, but once you were done, you'd be done. Of course, you'd want to abstract them away into a helper class or separate control.
Here are a couple examples on dynamically adding a column to your GridView:
protected void Page_Init(object sender, EventArgs e)
{
btnAddColumn.Click += new EventHandler(btnAddColumn_Click);
BoundField temp = new BoundField();
temp.DataField = "Source";
temp.HeaderText = "Source";
temp.SortExpression = "Source";
gv.Columns.Add(temp);
}
void btnAddColumn_Click(object sender, EventArgs e)
{
TemplateField temp = new TemplateField();
temp.HeaderText = txtNewColumn.Text;
txtNewColumn.Text = string.Empty;
gv.Columns.Add(temp);
}
ASPX:
<div>
<asp:TextBox ID="txtNewColumn" runat="server" />
<asp:Button ID="btnAddColumn" runat="server" Text="Add Column" />
</div>
<asp:GridView ID="gv"
runat="server"
AutoGenerateColumns="False"
DataSourceID="dsExceptions">
<Columns>
<asp:BoundField DataField="Message" HeaderText="Message" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="dsExceptions"
runat="server"
SelectMethod="GetExceptions"
TypeName="WebApplication.Data.MyDataContext" EnablePaging="True"
SelectCountMethod="GetExceptionCount" >
<SelectParameters>
<asp:Parameter Name="maximumRows" Type="Int32" />
<asp:Parameter Name="startPageIndex" Type="Int32" />
<asp:Parameter Name="startRowIndex" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
My ObjectDataSource is just getting a collection of Exceptions I create, so you can ignore that part of the code.
So I'm adding a column via code in the Page_Init handler, as well as adding a column with the header text set to the text in txtNewColumn. One thing about this, though, is that if you refresh the page, it will lose the columns you've added with the button on the page. You'll want to add some code in the button click event handler to store the new columns, so they're always in the GridView when it reloads. Since it sounds like the columns represent a vendor, you might just want to add a form for someone to add a vendor to your system. The vendor gets saved in a database/datastore somewhere, and in the Page_Init, you get all the vendors from that datastore and loop over them, creating the template fields for the vendors. It would be something like this:
// I am just creating a list here, you would pull the vendor names in from the
// datastore here instead.
var vendors = new List<string> { "Vendor A", "Vendor B", "Vendor C" };
foreach (var vendor in vendors)
{
TemplateField temp = new TemplateField();
temp.HeaderText = vendor;
gv.Columns.Add(temp);
}

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.

How to bind data to DropDownList in UpdatePanel inside DetailsView

I use DetailsView to insert rows in database. Row has fields id, subcategory_id etc. I want to fill dynamically dropdownlist ddl_subcategories, which is used in TemplateField. Selected item value of first dropdownlist ddl_categories is used as parameter for generating collection for ddl_subcategories. I try it with using UpdatePanel, but method DataBind returns error "Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.".
There's code of web form
<asp:DetailsView ID="dvw" runat="server" Height="50px" Width="125px"
AutoGenerateRows="False" DataSourceID="ods"
DefaultMode="Insert" DataKeyNames="Section_id"
OnDataBound="dvw_DataBound" OnItemUpdated="dvw_ItemUpdated"
OnItemCommand="dvw_ItemCommand">
<Fields>
<asp:TemplateField HeaderText="Category" >
<ItemTemplate>
<asp:DropDownList ID="ddl_categories" runat="server" AutoPostBack="true" DataSourceID="ods_categories"
DataTextField="Name" DataValueField="Category_id" OnSelectedIndexChanged="category_select_index_changed"/>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Subcategory" >
<ItemTemplate>
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:DropDownList ID="ddl_subcategories" runat="server"
SelectedValue='<%# Bind("Subcategory_id") %>' />
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddl_categories" EventName="SelectedIndexChanged" />
</Triggers>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
...
</Fields>
</asp:DetailsView>
There is part of behind-code:
protected void category_select_index_changed(object sender, EventArgs e)
{
DropDownList ddl_categories = (DropDownList)dvw.FindControl("ddl_categories");
List<SUBCATEGORY> sections = SUBCATEGORY.Select_all_by_parameters(Int32.Parse(ddl_categories.SelectedValue));//Select all subcategories by id of category
DropDownList ddl_subcategories= (DropDownList)dvw.FindControl("ddl_subcategories");
ddl_subcategories.DataSource = sections;
ddl_subcategories.DataTextField = "Name";
ddl_subcategories.DataValueField = "Subcategory_id";
ddl_subcategories.DataBind();
}
What is my error?
Thanks.
The problem is that when you are data binding the subcategories dropdown, there is no DataBinding Context (see here for more info, pay attention to David Fowler's comments). I don't think the UpdatePanel is the issue as the behavior is the same with or without it (and if you wrap the entire DetailsView in an UpdatePanel).
A solution is to load the items individually instead of data binding the control. It seems a little clumsy, I admit, but it does work.
protected void category_select_index_changed(object sender, EventArgs e)
{
DropDownList ddl_categories = (DropDownList)dvw.FindControl("ddl_categories");
IEnumerable<SUBCATEGORY> sections = new SUBCATEGORY[] { new SUBCATEGORY() { Name = "First " + ddl_categories.SelectedValue, Subcategory_id = "1" }, new SUBCATEGORY() { Name = "Second", Subcategory_id = "2" } };
DropDownList ddl_subcategories = (DropDownList)dvw.FindControl("ddl_subcategories");
ddl_subcategories.Items.Clear();
foreach (SUBCATEGORY cat in sections)
{
ddl_subcategories.Items.Add(new ListItem(cat.Name, cat.Subcategory_id));
}
}
A better option would be to not use Bind in the control, and just set the value in the ObjectDataSource's parameters on Insert/Update.
Update: You can set the ObjectDataSource value directly. Add an event handler for the OnInserting (or OnUpdating as needed) as follows:
protected void ods_OnInserting(object sender, ObjectDataSourceMethodEventArgs e)
{
DropDownList ddl_subcategories = (DropDownList)dvw.FindControl("ddl_subcategories");
e.InputParameters["Subcategory_id"] = ddl_subcategories.SelectedValue;
}
I can't actually test this right now, but it should be close.
Remove the Bind("DBFieldName") and add the following line to the YourDetailsView_ItemInserting like the following:
e.Values["DBFieldName"] =
((DropDownList)YourDetailsView.Rows[1].FindControl("ddl_categories")).SelectedValue;
Note: Don't forget to replace the Rows[1] to your Dropdown templete field index.
Try wrapping the entire DetailsView with the update panel to see if that makes a difference...

Passing multiple argument through CommandArgument of Button in Asp.net

I have a gridview with multiple rows, each has a Update button and I need to pass 2 values when someone clicks on Update button.
Aside from packing the arguments inside CommandArgument separated by commas (archaic and not elegant), how would I pass more than one argument?
<asp:LinkButton ID="UpdateButton" runat="server" CommandName="UpdateRow" CommandArgument="arg_value" Text="Update and Insert" OnCommand="CommandButton_Click" ></asp:LinkButton>
As a note, the values can't be retrieved from any controls on the page, so I am presently not seeking any design solutions.
You can pass semicolon separated values as command argument and then split the string and use it.
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:LinkButton ID="lnkCustomize" Text="Customize" CommandName="Customize" CommandArgument='<%#Eval("IdTemplate") + ";" +Eval("EntityId")%>' runat="server">
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
at server side
protected void gridview_RowCommand(object sender, GridViewCommandEventArgs e)
{
string[] arg = new string[2];
arg = e.CommandArgument.ToString().Split(';');
Session["IdTemplate"] = arg[0];
Session["IdEntity"] = arg[1];
Response.Redirect("Samplepage.aspx");
}
After poking around it looks like Kelsey is correct.
Just use a comma or something and split it when you want to consume it.
#Patrick's answer is a good idea, and deserves more credit!. You can have as many data items as you want, they are all separated, and can be used client side if necessary.
They can also be added declaratively rather than in code. I just did this for a GridView like this:
<asp:TemplateField HeaderText="Remind">
<ItemTemplate>
<asp:ImageButton ID="btnEmail"
data-rider-name="<%# ((Result)((GridViewRow) Container).DataItem).Rider %>"
data-rider-email="<%# ((Result)((GridViewRow) Container).DataItem).RiderEmail %>"
CommandName="Email" runat="server" ImageAlign="AbsMiddle" ImageUrl="~/images/email.gif" />
</ItemTemplate>
</asp:TemplateField>
In the RowCommand, you do this:
void gvMyView_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "Email")
{
var btnSender = (ImageButton)e.CommandSource;
var riderName = btnSender.Attributes["data-rider-name"];
var riderEmail = btnSender.Attributes["data-rider-email"];
// Do something here
}
}
So much cleaner than hacking all the values together with delimiters and unpacking again at the end.
Don't forget to test/clean any data you get back from the page, in case it's been tampered with!
My approach is using the attributes collection to add HTML data- attributes from code behind. This is more inline with jquery and client side scripting.
// This would likely be done with findControl in your grid OnItemCreated handler
LinkButton targetBtn = new LinkButton();
// Add attributes
targetBtn.Attributes.Add("data-{your data name here}", value.ToString() );
targetBtn.Attributes.Add("data-{your data name 2 here}", value2.ToString() );
Then retrieve the values through the attribute collection
string val = targetBtn.Attributes["data-{your data name here}"].ToString();
A little more elegant way of doing the same adding on to the above comment ..
<asp:GridView ID="grdParent" runat="server" BackColor="White" BorderColor="#DEDFDE"
AutoGenerateColumns="false"
OnRowDeleting="deleteRow"
GridLines="Vertical">
<asp:BoundField DataField="IdTemplate" HeaderText="IdTemplate" />
<asp:BoundField DataField="EntityId" HeaderText="EntityId" />
<asp:TemplateField ShowHeader="false">
<ItemTemplate>
<asp:LinkButton ID="lnkCustomize" Text="Delete" CommandName="Delete" CommandArgument='<%#Eval("IdTemplate") + ";" +Eval("EntityId")%>' runat="server">
</asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</asp:GridView>
And on the server side:
protected void deleteRow(object sender, GridViewDeleteEventArgs e)
{
string IdTemplate= e.Values["IdTemplate"].ToString();
string EntityId = e.Values["EntityId"].ToString();
// Do stuff..
}
Either store it in the gridview datakeys collection, or store it in a hidden field inside the same cell, or join the values together. That is the only way. You can't store two values in one link.
If you want to pass two values, you can use this approach
<asp:LinkButton ID="RemoveFroRole" Text="Remove From Role" runat="server"
CommandName='<%# Eval("UserName") %>' CommandArgument='<%# Eval("RoleName") %>'
OnClick="RemoveFromRole_Click" />
Basically I am treating {CommmandName,CommandArgument} as key value. Set both from database field. You will have to use OnClick event and use OnCommand event in this case, which I think is more clean code.

Resources