C#/ASP.Net - Extract bit value of column in a gridview - asp.net

I have a gridview that is SQL bound. In some of the columns there are bit values. When I use C# to get the values into the gridview, checkboxes are displayed. I need to extract the value of that column into text.
SqlConnection sConnection = new SqlConnection(MyConnectionString);
SqlCommand sCommand = new SqlCommand();
using (sConnection)
{
sCommand.Connection = sConnection;
sCommand.CommandText = "MyStoredProcedure";
sCommand.CommandType = CommandType.StoredProcedure;
sCommand.Connection.Open();
SqlDataReader reader = sCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
gridView.DataSource = reader;
gridView.DataBind();
}
for (int i = 0; i < gridView.Rows.Count; i++)
{
ListBox1.Items.Add(gridView.Rows[i].Cells[3].Text);
}
}
}
The gridview column data type is 'bit'. I do not have access to the database or stored procedure to change anything there. I need to somehow extract the '0' or '1' value, but when I do it like above, the text is blank.
I also tried to use 'GetOrdinal'. It returned a True/False value from the database, but I could not figure out how to get the value for each item in the gridview.
if (!reader.IsDBNull(reader.GetOrdinal("MyColumn1")))
{
ListBox1.Items.Add(reader.GetOrdinal("MyColumn1").ToString());
}

General overview:
You need to be able to find the CheckBox that's generated and get the value of it's "Checked" property.
To do this, you need to be able to use the FindControl() method on the GridViewRow.
To use FindControl, the CheckBox needs a predictable name.
To get a predictable name, you need to have that column be a TemplateColumn so that you can specify the name of the CheckBox in the markup on the ASPX page.
There's a full working set of code here: http://www.codeproject.com/Articles/25056/The-RIGHT-Way-to-Use-Checkboxes-in-a-NET-Repeater
This shows the code for a Repeater, but it's the same principle and general code for any DataBound control.
The code below should work with modifications to match your DB names:
<asp:TemplateField>
<ItemTemplate >
<asp:checkbox id="MyColumnNameCheckbox" runat="server" />
</ItemTemplate>
</asp:TemplateField>
string defaultvalue = "0"; // To be used to display the value of the original bit field.
foreach (GridViewRow row in GridView1.Rows)
{
CheckBox chkBx = (CheckBox)row.FindControl("MyColumnNameCheckbox");
if (chkBx != null && chkBx.Checked)
{
defaultvalue = "1";
}
}

I was able to figure it out. Thanks, David Stratton, for pointing me in the right direction.
I did it by assigning an id to the dynamically created control first. then did the FindControl()...
Control ctrl = GridView1.SelectedRow.Cells[4].Control[0];
ctrl.ID = "ctrl";
Boolean result = Convert.ToBoolean(((Checkbox)GridView1.Rows[0].Cells[4].FindControl("ctrl")).Checked);
TextBox1.Text = result.ToString();
This returns a value of "True" or "False"...
Thanks again.

Another way to resolve it:
bool result = (GridView1.SelectedRow.Cells[4].Control[0] as Checkbox).Checked;
TextBox1.Text = result.ToString();
it resolve the problem with less code :)

Related

How to get the values of the DataSource row that was used to compose another asp:GridView field?

The asp:GridView displays the order lines. It contains only two BoundFields: ID (not visible) and html_text_info (visible). The gvOrderLines.DataSource is explicitly assigned by the DataTable that was obtained by calling the SQL stored procedure. Before that,the html_text_info is added to the DataTable, and it is filled by the formatted HTML text where other columns are the source for the information. See the following method (simplified):
private void LoadGridViewOrderLines()
{
// Get the data table with the order-line data for the order.
DataTable dt = DbUtil.GetAppTable(
"usp_order_lines #order_code=#order_code",
new Hashtable { { "order_code", OrderCode } },
commandTimeoutSeconds: 180);
// Add the field html_text_info, and format the info into it.
dt.Columns.Add("html_text_info", typeof(string));
StringBuilder sb = new StringBuilder();
foreach (DataRow row in dt.Rows)
{
sb.Clear();
// Get the values from the row.
string product_code = row.Field<string>("product_code");
string product_name = row.Field<string>("product_name");
double quantity = ... get the quantity and convert to double...;
string unit = row.Field<string>("unit");
double price = ... get the price and convert to double...;
// Format it as an HTML string.
sb.Append($"{product_code}<br />");
sb.Append($"<b>{product_name}</b><br />");
sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
sb.Append($"Price: <b>{price:f3}</b><br />");
// Set the formatted value to the field.
row["html_text_info"] = sb.ToString();
}
gvOrderLines.DataSource = dt;
gvOrderLines.DataBind();
}
Now, I want to edit/update the order item. So, I need to access the row from the DataTable that is used as the DataSource. I already have the handler that gets correctly the ID into the property UpdateID (because it was named as DataKeyNames="ID" for the grid view; see below). How can I get the source values that form the composed field?
protected void gvOrderLines_RowEditing(object sender, System.Web.UI.WebControls.GridViewEditEventArgs e)
{
UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;
// How to get the source values that form the composed field?
tbCode.Text = ???;
tbName.Text = ???;
tbQuantity.Text = ???;
tbPrice.Text = ???;
gvOrderLines.EditIndex = -1; // cancel
gvOrderLines.DataBind();
}
Is the DataRow for current e.NewEditIndex easily accessible? Or do I have to search on my own in the DataTable (that is the gvOrderLines.DataSource)?
The "data row" source ONLY persists during the data binding operations.
So for example, you have "looping" code after you pull the data to on the fly create those extra columns. I often do that, but I still in most cases would suggest you do that "one row" operation in the gv, and not against the table.
This suggestion is not a "must" do, but it can help. Since then you are free to have other buttons, filters or whatever and JUST toss/pass/send/throw to the gv the data source. (so, each time you pull/create/have/use/filter etc. the data source, you don't have to modify it with that extra column.
Thus this:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView gData = e.Row.DataItem as DataRowView; // the data source row!!!!
Label Info = (Label)e.Row.FindControl("lblInfo");
StringBuilder sb = new StringBuilder();
sb.Clear();
// Get the values from the row.
string product_code = gData["product_code"].ToString();
string product_name = gData["product_name"].ToString();
double quantity = ... get the quantity and convert to double...;
string unit = gData["unit"].ToString();
double price = ... get the price and convert to double...;
// Format it as an HTML string.
sb.Append($"{product_code}<br />");
sb.Append($"<b>{product_name}</b><br />");
sb.Append($"Quantity: <b>{quantity:f2} {unit}</b><br />");
sb.Append($"Price: <b>{price:f3}</b><br />");
// Set the formatted value to the field.
Info.Text = sb.ToString();
}
}
however, as I stated/noted, the so-called data source (dataitem) that seems to show all over the place in most GV events, is ONLY persisted during the binding process. Once binding is done, then that data item goes out of scope.
Next up, a button click to edit/get the one row.
I (now) in most cases do NOT bother with the gv event model WHEN most of the code and editing is custom code (code you the developer is writing and wanting control of what the heck is going to occur!!!).
So, just drop in a plane jane button into the grid row, and use a plane jane standard button click event.
Say, like this:
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" />
<asp:BoundField DataField="Description" HeaderText="Description" ItemStyle-Width="270" />
<asp:TemplateField HeaderText="Edit">
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit"
CssClass="btn" OnClick="cmdEdit_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
So, note how I do (did not) bother with using the built in gv events.
So, the button click event - a plane jane one looks like this:
protected void cmdEdit_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
Debug.Print("row index click = " + gRow.RowIndex);
Debug.Print("database PK id = " + PKID);
string strSQL = "SELECT * FROM tblHotelsA WHERE ID = " + PKID;
DataRow rstData = MyRst(strSQL).Rows[0];
.. etc. etc.
From that row click, we get/use/have the database PK id, and thus have to re-pull the data. Note how we NEVER exposed/used/have the database PK id in the gv markup - (for reasons of security). (and I see you ALSO using datakeys - good!!).
So, your edit button click (now a plane jane click event) becomes somthing like this:
protected void cmdEdit_Click(object sender, EventArgs e)
{
GridViewRow gRow = btn.NamingContainer as GridViewRow;
int PKID = (int)GridView1.DataKeys[gRow.RowIndex]["ID"];
// How to get the source values that form the composed field?
// answer: we re-pull the database row based on pk
int UpdateID = (int)gvOrderLines.DataKeys[e.NewEditIndex].Value;
string strSQL = "SELECT * FROM MyTable WHERE ID = " + UpdateID;
DataRow OneRow = MyRst(strSQL).Rows[0];
// any display value in gv - you use find control ,or cells[] collection
TextBox txtHotelName = gRow.FindControl("txtHotel") as TextBox;
Just remember that dataitem" is available ONLY during binding, and after the databinding to the gv is done, then it (the dataitem) goes out of scope.
If the value(s) you need are not in the gv "display", then you need to re-pull that one row for such values.
templated controls in gv - use find control
built in, use cells[] collection.
Now, to be fair, I don't much use the built in "edit" function, and I just drop in a simple edit button, and either hide the gv, show the hidden edit "div" I have. Or better yet, pop that div like this:

Rows added to gridview don't persist when save event is triggered

I have a gridview on my webpage that can have rows added dynamically. There is a for loop that adds the rows like this:
protected void AddNewJob(object sender, EventArgs e)
{
for(int i = 0; i < Convert.ToInt32(newJobCount.Text);i++)
{
TableRow tr = new TableRow();
tr.Cells.Add(ServicesDDL("-- Select Service Type --"));
tr.Cells.Add(JobsDDL("-- Select Job Type --"));
tr.Cells.Add(TextBoxCell());
tr.Cells.Add(TextBoxCell());
tr.Cells.Add(TextBoxCell());
assetTable.Rows.Add(tr);
}
}
After the rows are added and changed from their default values the rows are looped through and data is saved to the database. I'm having problems getting the rows added to the gridview to persist and exist on the gridview when the page's save event is triggered. That code looks like this:
foreach (TableRow row in assetTable.Rows)
{
if (isFirst)
{
isFirst = false;
continue;
}
DropDownList service = (DropDownList)row.Cells[0].Controls[0];
string assetText = service.SelectedItem.Value;
DropDownList jobDescription = (DropDownList)row.Cells[1].Controls[0];
string serialText = jobDescription.SelectedItem.Value;
TextBox equipmentCount = (TextBox)row.Cells[2].Controls[0];
string leaseText = equipmentCount.Text;
TextBox jobSize = (TextBox)row.Cells[3].Controls[0];
string originText = jobSize.Text;
TextBox serialNo = (TextBox)row.Cells[4].Controls[0];
string deliveryText = serialNo.Text;
string oNo = orderNo.Text;
if (assetText != "0" && serialText != "0")
{
APICallClass.Job j = new APICallClass.Job();
j.JobTypeID = Convert.ToInt32(serialText);
j.serviceID = Convert.ToInt32(assetText);
j.equipment = leaseText;
j.jobSize = originText;
j.serialNumbers = deliveryText;
j.orderID = Convert.ToInt32(global.GlobalID);
APICallClass.API.AddJob(j, Session["Variable"].ToString());
}
}
When the code pasted above runs, it only sees the rows that are pulled in from the database. I think my problem could be fixed by calling something like .databind() somewhere that I'm not, but I've tried a few places and they have not fixed the problem.
Thanks in advance for any help, and helping me become a more robust ASP.NET developer.
When a GridView is DataBind()-ed, the contents of the GridView are "rebuilt" based on the the GridView's DataSource. If you need the new row to stay in the GridView, either do not call DataBind() after the new row is added, or ensure that the contents of your new row are in the GridView's DataSource before future calls to DataBind().
In a page I wrote once I had a similar situation and did the latter. The data for the initial rows was pulled from the database on the first page load and persisted in ViewState. As the user added, removed, and reordered rows in the GridView, the page just changed the data in ViewState and re-DataBind()ed the GridView.

Delete row in GridView using Two DataKeys in condition

I want to delete a row in GridView and database as well.. I already have a stored procedure for the delete, the method is also created, but my problem is how can i use Two datakeys for my where clause. I've search and saw a lot of answers but they are using CommandArgument and that is applicable only in 1 datakeys.
I want something like this.
DELETE FROM TableName WHERE ID = Variable1 AND Name = Variable2
What i want is how can i retrieve the values of 2 datakeys in GridView if i set DataKeys like: DataKeys="ID, Name"
Any help would be appreciated.
Thanks
(if I correctly understood you)
stored procedure
Create Procedure [dbo].[Procedure_Name](
#Variable1 type(),
#Variable2 type() --you can add as many variables as you want, but then you have to add them in c# code as a parameters.
) as
delete from Table_Name
where Table_Name.Variable1 = #Variable1 AND Table_Name.Variable2 = #Variable2
with button in aspx code
<asp:Button ID="btnDeleteSomething" runat="server" Text="Delete Something" OnClick="btnDeleteSomething_Click"/>
then in c# codebehind
protected void btnDeleteSomething_Click(object sender, EventArgs e)
{
using (SqlConnection cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DB_NAME"].ConnectionString))
{
SqlCommand cmd = new SqlCommand("Procedure_Name", cn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection.Open();
cmd.Parameters.Add("#Variable1", SqlDbType.VarChar, 70(its example with varchar)).Value = Variable1.Text(every Textbox, or label, or dropdownlist)
cmd.Parameters.Add("#Variable2", SqlDbType.Int).Value = Session["UserID]" - For example if u want do delete with parameter focused on user;
try
{
int rows = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Label1.Text = ex.Message;
}
}
}
And it works fine without Datakeys at all. If u want to do it "with" then u have to add ControlParameter in your SqlDataSource. Hope it's going to help you.
Finally i found the solution on this link:
http://www.aspsnippets.com/Articles/Using-Multiple-DataKeyNames-DataKeys-in-ASPNet-GridView-with-examples.aspx
here is what exactly im looking for..
Dim rowIndex As Integer = TryCast(TryCast(sender, ImageButton).NamingContainer GridViewRow).RowIndex
Dim ID As Integer = Convert.ToInt32(GridView1.DataKeys(rowIndex).Values(0))`
Dim Name As String = GridView1.DataKeys(rowIndex).Values(1).ToString()`
Thanks

dropdownList doesn't displays selected value

I am new at asp.net. I have a dropdownList which displays all categories for a specific article. The problem is that when I want to edit an article it doesn't displays the value that exists as default selected in dropdownList.
protected void datalist2_OnItemCreated(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.EditItem)
{
DropDownList drpdKategoria = e.Item.FindControl("drpdKategoria") as DropDownList;
SqlConnection con = new SqlConnection(connection);
string Qry = "select * from kategoria";
SqlDataAdapter da = new SqlDataAdapter(Qry, con);
DataSet ds = new DataSet();
con.Open();
da.Fill(ds);
drpdKategoria.DataSource = ds;
drpdKategoria.DataValueField = "id";
drpdKategoria.DataTextField = "emertimi";
drpdKategoria.DataBind();
drpdKategoria.SelectedValue = drpdKategoria.Items.FindByText(Qry).Value;
//drpdKategoria.Items.FindByValue(string val).Selected = true;
con.Close();
con.Dispose();
ds.Dispose();
da.Dispose();
}
}
EditArtikull.aspx
<asp:Label ID="Label5" runat="server" style="font-weight: 700">Kategoria</asp:Label>
<fieldset>
<asp:DropDownList ID="drpdKategoria" runat="server" AutoPostBack="false"></asp:DropDownList>
</fieldset>
<br/>
ERROR:SystemNullReference Exception {"Object reference not set to an instance of an object."}
This line probably isn't going to find anything:
drpdKategoria.Items.FindByText(Qry)
Since Qry is a SQL statement:
string Qry = "select * from kategoria";
And I'm assuming that the display values in your DropDownList aren't SQL queries. Thus, when you call .Value on that first line, you're trying to de-reference something that isn't found (which is null), hence the error.
What item are you actually trying to find? If you want to select a default item, you need to be able to identify that item in some way. In your data that would be either by a known id value or a known emertimi value. For example, if you have a known emertimi value, it would be:
drpdKategoria.SelectedValue = drpdKategoria.Items.FindByText("some known value").Value
To make this a little more robust, you probably want to add some null checking. Something like this:
var defaultValue = drpdKategoria.Items.FindByText("some known value");
if (defaultValue != null)
drpdKategoria.SelectedValue = defaultValue.Value

switch statement in linq

My code for sql connection using linq is:
var query1 = from u in dc.Usage_Computers
where u.DomainUser == s3
select u; // selects all feilds from table
GridView1.DataSource = query1;
GridView1.DataBind();
I have a field called "Operation" in the table "Domainuser" which has values like "1, 2, 3". When I populate these values to data grid I wanted to convert them to meaningful values like if the value of Operation is 1 then display in datagrid as "logon", if 2 then "logoff" etc...
How do i assign values for them after retrieving from database?
This technique does not seem particularly applicable to your problem, but here it is anyway.
You can create a SQL case statement in LinqToSql by using the C# ? : operator.
var query1 =
from u in dc.Usage_Computers
where u.DomainUser == s3
select new {usage = u,
operation =
u.DomainUser.Operation == 1 ? "login" :
u.DomainUser.Operation == 2 ? "logoff" :
"something else"
};
Use a template field in your gridview:
<asp:GridView ID="gvDomain" runat="server" OnRowDataBound="gvDomain_RowDataBound">
<Columns>
<asp:TemplateField>
<HeaderTemplate>
Operation
</HeaderTemplate>
<ItemTemplate>
<asp:Label id="lblLogon" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Then use the gridviews RowDataBound event to discover the label and assign its text:
Protected Sub gvDomain_RowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvStates.RowDataBound
Dim lblLogon As Label = DirectCast(e.Row.FindControl("lblLogon"), Label)
Dim drv As DataRowView = DirectCast(e.Row.DataItem, DataRowView)
If lblLogon IsNot Nothing Then
Select Case drv("Operation").ToString()
Case "1"
lblLogon.Text = "Logon"
Break
Case "2"
lblLogon.Text = "Logoff"
Break
//etc...
End Select
End If
End Sub
static Func<int?, string> MapSqlIntToArbitraryLabel = (i =>
{
// for performance, abstract this reference
// dictionary out to a static property
Dictionary<int, string> labels = new Dictionary<int, string>();
labels.Add(1, "logon");
labels.Add(2, "logoff");
labels.Add(...);
if (i == null) throw new ArgumentNullException();
if (i < 1 || i > labels.Count) throw new ArgumentOutOfRangeException();
return labels.Where(x => x.Key == i.Value)
.Select(x.Value)
.Single();
}
that return statement can also be expressed as:
return (from kvp in labels
where kvp.Key == i.Value
select kvp.Value).Single();
Then you can use call that function from your linq query like so:
var query1 = from u in dc.Usage_Computers
where u.DomainUser == s3
select {
Operation = MapSqlIntToArbitraryLabel(u.Operation)
// add other properties to this anonymous type as needed
};
I've tried every suggested method of fooling Linq2Sql into running my code and this method is the only one that i've found that allows me to run code as part of a deferred-execution projection.
I've done something similar using TemplateFields. Using an ASP:Label bound to the property and adding an OnPreRender event handler for the control. In the event handler for the control I translate the text based on it's current value and set the new value:
protected void label_OnPreRender( object sender, EventArgs e )
{
Label l = (Label)sender;
switch (l.Text) {
case "1":
l.Text = "Logon";
break;
...
default:
break;
}
}
If the form is in edit mode, you'll need to handle it differently. You'll also probably need to add handlers for Inserting and Updating to the View control you are using to translate the data supplied by the page into its database representation.

Resources