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.
Related
I have a web update form where every control is a property of the same table. It should work this way:
Whenever I select a value from the main(the first) dropdownlist, a query should be run getting all of the fields(properties) and filling the other controls depending of the value I selected.
Event Code:
Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs) Handles DropDownList1.SelectedIndexChanged
Dim objModel As New ModelDAO ' the data access class taht contains search method
Dim myobject = objModel.searchObject(DropDownList1.Text)
TextBox1.Text = myobject.Property2
DropDownList2.SelectedValue = myobject.Property3 'what's wrong here?
End Sub
Controls:
<asp:DropDownList ID="DropDownList1" runat="server" AppendDataBoundItems="True"
DataSourceID="SqlDataSource1" DataTextField="MODEL" DataValueField="MODEL"
AutoPostBack="true" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged">
<asp:ListItem Text="-Select-" Value="" />
</asp:DropDownList>
<asp:DropDownList ID="DropDownList2" runat="server">
<asp:ListItem Value="0">-Select-</asp:ListItem>
<asp:ListItem>EDS</asp:ListItem>
<asp:ListItem>BDS</asp:ListItem>
</asp:DropDownList>
It works except the second DropDownList, I don't know how to change the selected value, I tried this:
DropDownList2.Text = myobject.Property3
and this:
DropDownList2.SelectedValue = myobject.Property3
But in both cases dropdownlist2 shows no selected item.
NOTE: Since textbox.text does get the right value I don't think search method has any problem, that's why I'm not posting it.
It doesn't work that way unfortunately. I'll give you pseudocode since I'm a C#er, but this is how I normally do it:
Dim int i = 0
ForEach Item in DDL.Items
If Item.Text = databaseResult
DDL.SelectedIndex = i
ExitLoop
EndIf
i = i + 1
EndForEach
Here is the working code from the comment thread below:
Dim i As Integer = 0
For Each listitem In DropDownList1.Items
If DropDownList3.SelectedItem.Text = myobject.property Then
DropDownList3.SelectedIndex = i
Exit For
End If
i = i + 1
Next
If VB is anything like C# (where I've had similar problems), then try
DropDownList2.SelectedItem = myobject.Property3
Make sure the item you want selected is in the controls list of items otherwise it still won't work.
Here try this:
int i = 0;
foreach (ListItem item in YourDropDownList.Items)
{
//Check it see if your item actually exists in the list
//Could also be item.Text
if (item.Value.ToLower() == value)
{
item.Selected = true;
YourDropDownList.SelectedIndex = i;
break;
}
else
item.Selected = false;
i++;
}
if (YourDropDownList.SelectedIndex != -1)
{
YourDropDownList_SelectedIndexChanged(YourDropDownList, EventArgs.Empty);
}
Not sure if this will work, you can try it:
YourDropDownList.SelectedValue = myobject.Property3
YourDropDownList_SelectedIndexChanged(YourDropDownList, EventArgs.Empty);
So you have this working apart from the showing of the selectedValue in the second dropdown?
Remember when making comparsions it has to be equal to the parent value (usualy an integer)
another course of concerns is postback issues!
Its as simple as this...
ddlstate.SelectedIndex = ddlstate.Items.IndexOf(ddlstate.Items.FindByValue(dt.Rows[0]["state"].ToString()));
state is column name in database.
ddlstate is your dropdownlist id.
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 :)
I'm trying to create an ASP:Repeater programmatically and I have a problem when trying to bind the columns. The tutorials I've read are quite confusing and many of them don't get to the point of binding.
My problem is only in the point of binding data, when I would write this in a "static" repeater:
<%# DataBinder.Eval(Container.DataItem, "Name")%>
I don't know what should be when working in code-behind, it's within a class so I don't have an event handler. This is my code so far:
Dim DsArbol As New SqlDataAdapter(query, System.Configuration.ConfigurationManager.ConnectionStrings("CNX").ConnectionString)
Dim tablaCarpetas As New DataTable
DsArbol.Fill(tablaCarpetas)
Dim RepArbol As New Repeater
RepArbol.DataSource = tablaCarpetas
RepArbol.ID = "repArbolCarpetas"
Dim header As New TemplateBuilder
Dim item As New TemplateBuilder
Dim footer As New TemplateBuilder
header.AppendLiteralString("<ul class=""arbol-carpetas"">")
item.AppendLiteralString(String.Format("<li id=""li_carpeta_{0}"">{1}</li>", 1, DataBinder.Eval(Container.DataItem, "Name")))
footer.AppendLiteralString("</ul>")
RepArbol.HeaderTemplate = header
RepArbol.ItemTemplate = item
RepArbol.FooterTemplate = footer
RepArbol.DataBind()
PanelArbolCarpetas.Controls.Add(RepArbol)
What should I write instead of DataBinder.Eval(Container.DataItem, "Name")?
I am not too sure about using TemplateBuilder as it is meant for consumption for ASP.NET framework and there is not much documentation available. However, you may try changing below line as
item.AppendLiteralString("<li id=\"li_carpeta_1\"><%# Eval(\"Name\") %></li>")
The alternate way is to build your own template control - for example
public class MyTemplate : ITemplate
{
ListItemType _type;
public MyTemplate(ListItemType type)
{
_type = type;
}
public void InstantiateIn(Container control)
{
switch(_type)
{
case ListItemType.Header:
control.Contorls.Add(new LiteralControl("<ul class=\"arbol-carpetas\">"));
break;
case ListItemType.Footer:
control.Contorls.Add(new LiteralControl("</ul>"));
break;
case ListItemType.Item:
case ListItemType.AlternatingItem:
var c = new GenericHtmlControl("<li>");
c.ID = "L";
... // add needed attributes etc.
container.Controls.Add(c);
// manage data binding
container.DataBinding += (o,e) =>
{
c.InnerText = DataBinder.Eval(Container, "Name");
};
break;
}
}
}
RepArbol.HeaderTemplate = new MyTemplate(ListItemType.Header);
RepArbol.ItemTemplate = MyTemplate(ListItemType.Item);
RepArbol.FooterTemplate = MyTemplate(ListItemType.Footer);
RepArbol.DataBind()
Disclaimer: Untested code - just to give you an idea about building a template dynamically and manage data binding by capturing data-binding event.
GridView1.Columns.Count is always zero even SqlDataSource1.DataBind();
But Grid is ok
I can do
for (int i = 0; i < GridView1.HeaderRow.Cells.Count;i++)
I rename request headers here
but
GridView1.Columns[i].Visible = false;
I can't use it because of GridView1.Columns.Count is 0.
So how can I hide them ?
Try putting the e.Row.Cells[0].Visible = false; inside the RowCreated event of your grid.
protected void bla_RowCreated(object sender, GridViewRowEventArgs e)
{
e.Row.Cells[0].Visible = false; // hides the first column
}
This way it auto-hides the whole column.
You don't have access to the generated columns through grid.Columns[i] in your gridview's DataBound event.
The Columns collection is only populated when AutoGenerateColumns=false, and you manually generate the columns yourself.
A nice work-around for this is to dynamically populate the Columns collection yourself, before setting the DataSource property and calling DataBind().
I have a function that manually adds the columns based on the contents of the DataTable that I want to display. Once I have done that (and then set the DataSource and called DataBind(), I can use the Columns collection and the Count value is correct, and I can turn the column visibility on and off as I initially wanted to.
static void AddColumnsToGridView(GridView gv, DataTable table)
{
foreach (DataColumn column in table.Columns)
{
BoundField field = new BoundField();
field.DataField = column.ColumnName;
field.HeaderText = column.ColumnName;
gv.Columns.Add(field);
}
}
Note: This solution only works if your GridView columns are known ahead of time.
It sounds like you're using a GridView with AutoGenerateColumns=true, which is the default. I recommend setting AutoGenerateColumns=false and adding the columns manually:
<asp:GridView runat="server" ID="MyGridView"
AutoGenerateColumns="false" DataSourceID="MySqlDataSource">
<Columns>
<asp:BoundField DataField="Column1" />
<asp:BoundField DataField="Column2" />
<asp:BoundField DataField="Column3" />
</Columns>
</asp:GridView>
And only include a BoundField for each field that you want to be displayed. This will give you the most flexibility in terms of how the data gets displayed.
I was having the same problem - need my GridView control's AutogenerateColumns to be 'true', due to it being bound by a SQL datasource, and thus I needed to hide some columns which must not be displayed in the GridView control.
The way to accomplish this is to add some code to your GridView's '_RowDataBound' event, such as this (let's assume your GridView's ID is = 'MyGridView'):
protected void MyGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Cells[<index_of_cell>].Visible = false;
}
}
That'll do the trick just fine ;-)
You have to perform the GridView1.Columns[i].Visible = false; after the grid has been databound.
Try this to hide columns in an ASP.NET GridView with auto-generated columns, both RowDataBound/RowCreated work too.
Protected Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
If e.Row.RowType = DataControlRowType.DataRow Or _
e.Row.RowType = DataControlRowType.Header Then // apply to datarow and header
e.Row.Cells(e.Row.Cells.Count - 1).Visible = False // last column
e.Row.Cells(0).Visible = False // first column
End If
End Sub
Protected Sub GridView1_RowCreated(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowCreated
If e.Row.RowType = DataControlRowType.DataRow Or _
e.Row.RowType = DataControlRowType.Header Then
e.Row.Cells(e.Row.Cells.Count - 1).Visible = False
e.Row.Cells(0).Visible = False
End If
End Sub
In the rowdatabound method for 2nd column
GridView gv = (sender as GridView);
gv.HeaderRow.Cells[2].Visible = false;
e.Row.Cells[2].Visible = false;
#nCdy:
index_of_cell should be replaced by an integer, corresponding to the index number of the cell that you wish to hide in the .Cells collection.
For example, suppose that your GridView presents the following columns:
CONTACT NAME | CONTACT NUMBER | CUSTOMERID | ADDRESS LINE 1 | POST CODE
And you want the CUSTOMERID column not to be displayed.
Since collections indexes are 0-based, your CUSTOMERID column's index is..........? That's right, 2!! Very good.
Now... guess what you should put in there, to replace 'index_of_cell'??
As said by others, RowDataBound or RowCreated event should work but if you want to avoid events declaration and put the whole code just below DataBind function call, you can do the following:
GridView1.DataBind()
If GridView1.Rows.Count > 0 Then
GridView1.HeaderRow.Cells(0).Visible = False
For i As Integer = 0 To GridView1.Rows.Count - 1
GridView1.Rows(i).Cells(0).Visible = False
Next
End If
I found Steve Hibbert's response to be very helpful. The problem the OP seemed to be describing is that of an AutoGeneratedColumns on a GridView.
In this instance you can set which columns will be "visible" and which will be hidden when you bind a data table in the code behind.
For example:
A Gridview is on the page as follows.
<asp:GridView ID="gv" runat="server" AutoGenerateColumns="False" >
</asp:GridView>
And then in the code behind a PopulateGridView routine is called during the page load event.
protected void PopulateGridView()
{
DataTable dt = GetDataSource();
gv.DataSource = dt;
foreach (DataColumn col in dt.Columns)
{
BoundField field = new BoundField();
field.DataField = col.ColumnName;
field.HeaderText = col.ColumnName;
if (col.ColumnName.EndsWith("ID"))
{
field.Visible = false;
}
gv.Columns.Add(field);
}
gv.DataBind();
}
In the above the GridView AutoGenerateColumns is set to False and the codebehind is used to create the bound fields. One is obtaining the datasource as a datatable through one's own process which here I labeled GetDataSource(). Then one loops through the columns collection of the datatable. If the column name meets a given criteria, you can set the bound field visible property accordingly. Then you bind the data to the gridview. This is very similar to AutoGenerateColumns="True" but you get to have criteria for the columns. This approach is most useful when the criteria for hiding and un-hiding is based upon the column name.
Similar to accepted answer but allows use of ColumnNames and binds to RowDataBound().
Dictionary<string, int> _headerIndiciesForAbcGridView = null;
protected void abcGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (_headerIndiciesForAbcGridView == null) // builds once per http request
{
int index = 0;
_headerIndiciesForAbcGridView = ((Table)((GridView)sender).Controls[0]).Rows[0].Cells
.Cast<TableCell>()
.ToDictionary(c => c.Text, c => index++);
}
e.Row.Cells[_headerIndiciesForAbcGridView["theColumnName"]].Visible = false;
}
Not sure if it works with RowCreated().
Iterate through the GridView rows and make the cells of your target columns invisible. In this example I want to keeps columns 4-6 visible as is, so we skip those:
foreach (GridViewRow row in yourGridView.Rows)
{
for (int i = 0; i < rows.Cells.Count; i++)
{
switch (i)
{
case 4:
case 5:
case 6:
continue;
}
row.Cells[i].Visible = false;
};
};
Then you will need to remove the column headers separately (keep in mind that removing header cells changes the length of the GridView after each removal):
grdReportRole.HeaderRow.Cells.RemoveAt(0);
Try
Dim ds As DataSet = SqlHelper.ExecuteDataset(ConfigurationManager.ConnectionStrings("connstr").ConnectionString, CommandType.StoredProcedure, "Get_All", New SqlParameter("#userid", Session("userid")))
rptBundles.DataSource = ds.Tables(0)
Catch ex As Exception
showerror(ex, Me)
End Try
That is the code, I want to be able to parse through it and find certain rows that have a certain boolean set to 1 and then edit other variables in that row accordingly, how would I do this, I tried making a For Each row nested in a For Each table but when I tested it the repeater never populates with data...
For Each ds_table As DataTable In ds.Tables
For Each ds_row As DataRow In ds_table.Rows
If ds_row("isScorm") = 1 Then
ds_row("totalLessons") = 100
ds_row("TotalQuestions") = 100
ds_row("lessonscompleted") = 50
ds_row("TotalCorrect") = 50
End If
Next
Next
Only when I remove that code does the repeater populate as expected, but I got no errors!
If you're using a Repeater, or whatever datasource bound control, I would use the ItemDataBound event and set those values to your controls.
If this was your basic HTML
<html>
<asp:Repeater id="repeater" runat="server" OnItemDataBound="repeater_ItemDatabound">
<ItemTemplate>
<span><%# DataBinder.Eval(Container.DataItem, "isScorm") %></span>
<span id="totalLessonsSpan" runat="server"><%# DataBinder.Eval(Container.DataItem, "totalLessons") %></span>
</ItemTemplate>
</asp:Repeater>
</html>
I would have this in the code behind
protected void repeater_ItemDatabound(object sender, RepeaterItemEventArgs e)
{
DataRow row = e.Item.DataItem as DataRow;
if (row == null) { }
else
{
int isScorm = 0;
int.TryParse(Convert.ToString(row["isScorm"]), out isScorm);
if (isScorm > 0)
{
HtmlGenericControl totalLessonsSpan = e.Item.FindControl("totalLessonsSpan") as HtmlGenericControl;
totalLessonsSpan.Text = "100";
}
}
}
You probably don't want to loop through the data and swap it there, then bind when you can do it during the bind.
Alternately, something I hate that DB's do because of my need for data integrity, is change it in your SQL select with case statements.
Does adding rptBundles.DataBind() after setting DataSource fix the problem?
Also, you might want to check out the DataTable.Select method to only select (and then modify) rows where isScorm = 1.