asp:CheckBoxField in GridView - VS 2008 - c#

I have a gridview control bound to an object data source. in addition to the columns that i want to display i want to display this
<Columns>
<asp:CheckBoxField DataField="Locked" Visible="true" AccessibleHeaderText="On Hold" ReadOnly="false"/>
</Columns>
Couple of questions here:
1. If I do the above said, my page loads and certain rows have their records marked as checked and certain rows do not, as per data. However, the user is unable to click on any records to undo their check marks. It appears that this is in a disabled state.
It seems there is no onclick event with this checkboxfield. I want to update my records instantly when the user checks or unchecks each record. yes bad design here but my hands are tied
If i were to go with <asp:checkbox> within an <itemtemplate> how do i bind that to my locked column within the object datasource or do i have to do that by overiding onne of the methods of the gridview control?

To answer #2, I would go with a <asp:CheckBox> control in the <ItemTemplate> and then set the Checked property in the GridView_RowDataBound event:
protected void grdSomething_RowDataBound ( Object sender, GridViewRowEventArgs e )
{
if ( e.Row.RowType == DataControlRowType.DataRow )
{
BusinessObject data = ( BusinessObject ) e.Row.DataItem;
CheckBox chkLocked = ( CheckBox ) e.Row.FindControl( "chkLocked" );
chkLocked.Checked = data.Locked;
}
}
As for question #1, one solution that I've used in the past with good results is to have the client-side onClick event of the <asp:CheckBox> call an ASP.NET Ajax asynchronous page method (essentially a private web service) that updates the appropriate business object. This wouldn't be the simplest approach for you, but the user's experience is pretty slick (i.e. no full-page postback):
You would need a static method in your code-behind file to update the appropriate business object. Something like (this is pretty rough):
[WebMethod]
public static void ToggleLockedStatus ( string key )
{
if ( BusinessObjectDictionary.ContainsKey( key ) )
{
BusinessObjectDictionary[ key ].Locked = !BusinessObjectDictionary[ key ].Locked;
}
else
{
throw new Exception( "The business object specified was not found." );
}
}
For details on how to do this with ASP.NET Ajax, check out MSDN here.

For the click Event I could not figure out the OnClickChange either, so I rolled my own.
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Web;
using System.Web.UI.WebControls;
namespace Web.Library.Controls.Commands
{
public class CommandCheckBox : System.Web.UI.WebControls.CheckBox
{
private static readonly object EventCommand = new object();
public event CommandEventHandler Command
{
add
{
Events.AddHandler(EventCommand, value);
}
remove
{
Events.RemoveHandler(EventCommand, value);
}
}
public string CommandName
{
get
{
if (this.ViewState["CommandName"] == null)
return string.Empty;
return (string)this.ViewState["CommandName"];
}
set { this.ViewState["CommandName"] = value; }
}
public string CommandArgument
{
get
{
if (this.ViewState["CommandArgument"] == null)
return string.Empty;
return (string)this.ViewState["CommandArgument"];
}
set { this.ViewState["CommandArgument"] = value; }
}
protected virtual void OnCommand(CommandEventArgs args)
{
CommandEventHandler commandEventHand = Events[EventCommand] as CommandEventHandler;
if (commandEventHand != null)
{
commandEventHand(this, args);
}
base.RaiseBubbleEvent(this, args);
}
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
CommandEventArgs args = new CommandEventArgs(this.CommandName, this.CommandArgument);
this.OnCommand(args);
}
public CommandCheckBox()
{
}
}
}
As for the databind and updating I just handled it myself on GridRowCommand.

Your control is locked because the GridView will only let you edit a row if the row is in Edit mode. I think what you're really looking for is a checkbox defined in a TemplateField. Then you can either set the initial "checked" value in the code behind or by doing something like:
<asp:CheckBox ID="MyCheckBox" runat="server" Checked='<%#Eval("Locked")' ... />
As for the trigger, you can point the OnCheckedChange property to the function of your choosing, but I'm not sure how you'd tell it which row was checked.
A better alternative could be to use the Button or ImageButton control to do the same thing. That way you can populate the CommandArgument with the row ID or whatever you need and you'll have access to it when you handle the RowCommand of the GridView.

Related

Empty Datagridview cell with bound datasourse

I have a DataGridView with "Type" and "Value" columns. The user selects a data type, and then enters a value compatible with that type, some types (e.g. "Text") accept any string. Other types (e.g. "Yes/No") are restricted to a list of possible values. For each row, I set the cell in the "Value" column to be either a Textbox for freeform types, or a combobox for list types. The DataGridView is bound to a DataTable.
The problem comes in if the user enters a value for one type, but then switches the row to a different type for which the current value is not allowed. No matter what I try, I cannot clear the Value cell. Then when I assign a combobox to the cell, I get a DataError, because the current value of the cell is incompatible. How can I clear the value before changing the cell from a textbox to a combobox?
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name { get; set; }
public int ID {get; set; }
public string strID
{
get { return ID.ToString(); }
}
//constructors & other methods;
}
public static DataTable ParameterTable;
public List<IndexedItem> YesNoList;
In the form constructor (dgvInputs is the DataGridView):
ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
YesNoList = new List<IndexedItem>();
YesNoList.Add(new IndexedItem("Yes", 1));
YesNoList.Add(new IndexedItem("No", 0));
var D = (DataGridViewComboBoxColumn)dgvInputs.Columns[0];
D.ValueMember = "Value";
D.DisplayMember = "Display";
D.DataSource = new DataType[] {
DataType.Text,
DataType.YesNo
}.Select(x => new { Display = x.ToString(), Value = (int)x }).ToList();
BindingSource ParamSource = new BindingSource();
ParamSource.DataSource = ParameterTable;
dgvInputs.AutoGenerateColumns = false;
dgvInputs.DataSource = ParamSource;
dgvInputs.Columns[0].DataPropertyName = "Type";
dgvInputs.Columns[1].DataPropertyName = "Value";
And Events:
private void dgvInputs_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
if (dgvInputs.IsCurrentCellDirty) {
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
private void dgvInputs_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
if (e.RowIndex >= 0 && e.ColumnIndex == 0) {
var cb = (DataGridViewComboBoxCell)dgvInputs[0, e.RowIndex];
if (cb.Value != null && cb.Value != DBNull.Value) {
DataType Type = (DataType)cb.Value;
dgvInputs[1, e.RowIndex].Value = string.Empty;
dgvInputs.CommitEdit(DataGridViewDataErrorContexts.Commit);
switch (Type) {
case DataType.YesNo:
dgvInputs[1, e.RowIndex].Dispose();
var newBox = new DataGridViewComboBoxCell();
newBox.DisplayMember = "Name";
newBox.ValueMember = "strID";
newBox.DataSource = YesNoList;
dgvInputs[1, e.RowIndex] = newBox;
break;
default:
dgvInputs[1, e.RowIndex] = new DataGridViewTextBoxCell();
break;
}
}
}
}
If you have it set to "text" and enter something arbitrary, then switch to "YesNo", it gives an error "System.ArgumentException: DataGridViewComboBoxCell value is not valid.", that will reappear any time the cursor is over the cell. Changing it back to a text row causes the original value to reappear.
I am assuming that the problem is that the value is saved in ParameterTable, but I can't get it to propagate my clearing of the original value to ParameterTable. I've tried null and DBNull.Value instead of string.Empty, but neither one made any difference. I added the "CommitEdit" line in hopes of getting it to make the change, but that made no difference either.
Edit:
As it turns out, the problem was this code that I had in the cell change event:
string Default = dgvInputs[4, e.RowIndex].Value as string;
// code switching out text box and combo box above
try
{
dgvInputs[4, e.RowIndex].Value = Default;
} catch (Exception e2) {
MessageBox.Show(e2.GetType().ToString());
}
The idea had been to preserve the value if possible, and I had the messagebox to show me the specific exception I needed to catch, as I was not sure. But apparently this assignment does not immediately induce the exception. That only occurs later, apparently during some other event I am not handling.
It is obvious in hindsight that I should have included this code in the sample. I have no idea now how I overlooked it. My apologies to everybody I led on a wild goose chase by leaving out the critical information. I do appreciate all of your assistance.
You problem is not with clearing the value but with the YesNoList.
The grid compbobox tries to find the value for the current record and there is no empty neither null value in your YesNoList.
You will even get an error if you try to add a new record and first set the DataType without setting the Value.
You can solve this by either adding an empty item to your YesNoList or by setting a default value to the existing record when switching DataType.
Edit:
I know below does not specifically answer your question as it states, but the example may help you out. Consider having two controllers in one cell.
Original
I am not sure if this will help you or not, but I tried to make a very basic program that you discussed. A dataset is created with 2 entries. The first column is the DataType, the second is of Value. If the DataType Text is chosen, the Value cell turns into a Textbox. If the DataType Yes/No is chosen, it hides the Textbox and shows a DropDownList. The idea is to hide one component when not needed.
Default.aspx.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DataGridViewBounds
{
public partial class _Default : Page
{
public enum DataType
{
Text,
YesNo
}
public class IndexedItem
{
public string Name
{ get; set; }
public int ID
{ get; set; }
public string strID
{ get { return ID.ToString(); } }
}
protected void Page_Load (object sender, EventArgs e)
{
if (!IsPostBack)
{
Bind();
}
for (int i = 0; i < dg.Items.Count; ++i)
{
bool ShowText = ((DropDownList)dg.Items[i].Cells[0].Controls[1]).SelectedValue.Equals("text");
((DropDownList)dg.Items[i].Cells[1].Controls[1]).Visible = !ShowText;
((TextBox)dg.Items[i].Cells[1].Controls[3]).Visible = ShowText;
}
}
private void Bind ()
{
DataTable ParameterTable = new DataTable("ParameterTable");
ParameterTable.Columns.Add("", typeof(string));
ParameterTable.Columns.Add("Type", typeof(DataType));
ParameterTable.Columns.Add("Value", typeof(string));
List<ListItem> YesNoList = new List<ListItem>(); // Should be ListItem, not IndexedItem
YesNoList.Add(new ListItem("Yes", "1"));
YesNoList.Add(new ListItem("No", "0"));
DataRow row = ParameterTable.NewRow();
row["Type"] = DataType.Text;
row["Value"] = "Some text";
DataRow row2 = ParameterTable.NewRow();
ParameterTable.Rows.Add(row);
row2["Type"] = DataType.YesNo;
row2["Value"] = "false";
ParameterTable.Rows.Add(row2);
dg.DataSource = ParameterTable;
dg.DataBind();
dg.ShowHeader = true;
dg.Visible = true;
for (int i = 0; i < dg.Items.Count; ++i)
{ // Showing 2 ways to bind the DropDownList items
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Text", "text"));
((DropDownList)dg.Items[i].Cells[0].Controls[1]).Items.Add(new ListItem("Yes/No", "bool"));
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataSource = YesNoList;
((DropDownList)dg.Items[i].Cells[1].Controls[1]).DataBind();
}
}
}
}
And the Default.aspx page
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DataGridViewBounds._Default" %>
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">
<asp:DataGrid ID="dg" runat="server" AutoGenerateColumns="false">
<Columns>
<asp:TemplateColumn HeaderText="Type">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListType" AutoPostBack="true"></asp:DropDownList>
<asp:Label id="TypeLabel" runat="server" Visible="false"></asp:Label>
</ItemTemplate>
</asp:TemplateColumn>
<asp:TemplateColumn HeaderText="Value">
<ItemTemplate>
<asp:DropDownList runat="server" ID="ddListValue" AutoPostBack="true" Visible="false"></asp:DropDownList>
<asp:TextBox id="ValueLabel" runat="server" Visible="false"></asp:TextBox>
</ItemTemplate>
</asp:TemplateColumn>
</Columns>
</asp:DataGrid>
</asp:Content>
This is the best I could do at the moment without seeing more of the code, but may you can use it. One suggestion, dgvInputs_CurrentCellDirtyStateChanged appears to commit code. I am assuming this is SQL code. You may want to wait until committing until a final 'Submit' button or 'Accept Changes' button has been pressed so that you don't have to call SQL so much, but also if there is an error that occurs between the start of the first SQL call and the last. If an interruption occurs between the two, you may not necessarily want to commit.

Unable to delete data from database

I encountered a problem whereby when the user clicked on the delete button, Nothing happens and when I insert breakpoint to check, The selectLocStation is null. Why is it happening? Can anyone kindly solve my doubts about it?
Here are the codes for you to see my Delete codes. Appreciate any helps that were offered.
private void btnDel_Click(object sender, EventArgs e)
{
using (satsEntities Setupctx = new satsEntities())
{
int selectLocStation = Convert.ToInt32(cbLocStation.SelectedValue);
var DeleteRTiming =
(from delLocStation in Setupctx.requiredtimings
where delLocStation.RequiredLocationStationID == selectLocStation
select delLocStation).SingleOrDefault();
if (DeleteRTiming != null)
{
Setupctx.DeleteObject(DeleteRTiming);
Setupctx.SaveChanges();
cbLocStation.SelectedIndex = -1;
this.Edit_TS_Load(null, EventArgs.Empty);
MessageBox.Show("Selected Required Timing And " +
"The Location Station Has Been Deleted.");
}
}
}
This is the codes that were used to bind.
private void Edit_TS_Load(object sender, EventArgs e)
{
using (satsEntities Setupctx = new satsEntities())
{
var DeleteRT = (from DelRT in Setupctx.requiredtimings
join locationstationname ls in Setupctx.locationstationnames on DelRT.RequiredLocationStationID equals ls.locationstationID
select ls.locStatname).Distinct().ToList();
foreach (var locstationData in DeleteRT)
{
cbLocStation.Items.Add(locstationData);
}
}
}
How can selectLocStation be null ? it is of type int, are you getting any exception or you mean your object DeleteRTiming is null ? probably its the DeleteRTiming which is null
The simple answer to that the record you are looking in the database against selectLocStation is not there.
You need to put a break point and see what is being held by 'selectLocStation` and then check the database manually if the record exists in the database.
when you bind your control, you must set it in ! IsPostback and persist you with ViewState
If(! IsPostBack)
{
BindYourControl(); //For just first load.
}
Persist with
EnableViewState = true;
In order to don't erase your selected value
If this is a silverlight application then you might want to use
cbLocStation.SelectedItem
Or it is a ASP.Net site and you rebind the data on every pageLoad
Either way you should have an exception because you are trying to convert a null value.
See the method that you have used to bind the combo box
cbLocStation.Items.Add(locstationData);
This will create the combobox with "ValueField" and "TextFiled" with value of the variable "locstationData"
So when you try to get selected value from cbLocStation.SelectedValue ,it will always contain name and so you cannot parse it to integer.
There is another overload for the "Add" Which will accept Value(Id) and Text(Text to display)
cbLocStation.Items.Add(new ListItem(locstationData.locStatname, locstationData.locStatID));
Try to change the binding portion like this

Dropdownlist SelectedIndexChanged firing on every postback

I am dynamically adding a custom user control to an update panel. My user control contains two dropdownlists and a textbox. When a control outside of the update panel triggers a postsback, I am re-adding the user control to the update panel.
The problem is...on postback when I re-add the user controls, it's firing the "SelectedIndexChanged" event of the dropdownlists inside the user control. Even if the selectedindex did not change since the last postback.
Any ideas?
I can post the code if necessary, but there's quite a bit in this particular scenario.
Thanks in advance!
EDIT...CODE ADDED BELOW
*.ASCX
<asp:DropDownList ID="ddlColumns" OnSelectedIndexChanged="ddlColumns_SelectedChanged" AppendDataBoundItems="true" AutoPostBack="true" runat="server">
*.ASCX.CS
List<dataColumnSpecs> dataColumns = new List<dataColumnSpecs>();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
fillDDLColumns();
}
}
public void fillDataColumnsList()
{
dataColumns.Clear();
//COMMON GETDATATABLE RETURNS A DATA TABLE POPULATED WITH THE RESULTS FROM THE STORED PROC COMMAND
DataTable dt = common.getDataTable(storedProcs.SELECT_COLUMNS, new List<SqlParameter>());
foreach (DataRow dr in dt.Rows)
{
dataColumns.Add(new dataColumnSpecs(dr["columnName"].ToString(), dr["friendlyName"].ToString(), dr["dataType"].ToString(), (int)dr["dataSize"]));
}
}
public void fillDDLColumns()
{
fillDataColumnsList();
ddlColumns.Items.Clear();
foreach (dataColumnSpecs dcs in dataColumns)
{
ListItem li = new ListItem();
li.Text = dcs.friendlyName;
li.Value = dcs.columnName;
ddlColumns.Items.Add(li);
}
ddlColumns.Items.Insert(0, new ListItem(" -SELECT A COLUMN- ", ""));
ddlColumns.DataBind();
}
protected void ddlColumns_SelectedChanged(object sender, EventArgs e)
{
//THIS CODE IS BEING FIRED WHEN A BUTTON ON THE PARENT *.ASPX IS CLICKED
}
*.ASPX
<asp:UpdatePanel ID="upControls" runat="server">
<ContentTemplate>
<asp:Button ID="btnAddControl" runat="server" Text="+" OnClick="btnAddControl_Click" />
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="btnGo" runat="server" Text="Go" OnClick="btnGo_Click" ValidationGroup="vgGo" />
<asp:GridView...
*.ASPX.CS
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
uc_Counter = 0;
addControl();
gridview_DataBind();
}
else
{
reloadControls();
}
}
protected void btnGo_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
//THIS BUTTON CLICK IS WHAT'S TRIGGERING THE
//SELECTEDINDEXCHANGED EVENT TO FIRE ON MY *.ASCX
gridview_DataBind();
}
}
private void reloadControls()
{
int count = this.uc_Counter;
for (int i = 0; i < count; i++)
{
Control myUserControl = Page.LoadControl("~/Controls/myUserControl.ascx");
myUserControl.ID = "scID_" + i;
upControls.ContentTemplateContainer.Controls.AddAt(i, myUserControl);
((customUserControl)myUserControl).fillDDLColumns();
}
}
private void addControl()
{
Control myUserControl = Page.LoadControl("~/Controls/myUserControl.ascx");
myUserControl.ID = "scID_" + uc_Counter.ToString();
upControls.ContentTemplateContainer.Controls.AddAt(upControls.ContentTemplateContainer.Controls.IndexOf(btnAddControl), myUserControl);
//((customUserControl)myUserControl).fillDDLColumns();
this.uc_Counter++;
}
protected int uc_Counter
{
get { return (int)ViewState["uc_Counter"]; }
set { ViewState["uc_Counter"] = value; }
}
Even though this is already answered I want to put an answer here since I've recently tangled with this problem and I couldn't find an answer anywhere that helped me but I did find a solution after a lot of digging into the code.
For me, the reason why this was happening was due to someone overwriting PageStatePersister to change how the viewstate hidden field is rendered. Why do that? I found my answer here.
One of the greatest problems when trying to optimize an ASP.NET page to be more search engine friendly is the view state hidden field. Most search engines give more score to the content of the firsts[sic] thousands of bytes of the document so if your first 2 KB are view state junk your pages are penalized. So the goal here is to move the view state data as down as possible.
What the code I encountered did was blank out the __VIEWSTATE hidden fields and create a view_state hidden field towards the bottom of the page. The problem with this is that it totally mucked up the viewstate and I was getting dropdownlists reported as being changed when they weren't, as well as having all dropdownlists going through the same handler on submit. It was a mess. My solution was to turn off this custom persister on this page only so I wouldn't have to compensate for all this weirdness.
protected override PageStatePersister PageStatePersister
{
get
{
if (LoginRedirectUrl == "/the_page_in_question.aspx")
{
return new HiddenFieldPageStatePersister(Page);
}
return new CustomPageStatePersister(this);
}
}
This allowed me to have my proper viewstate for the page I needed it on but kept the SEO code for the rest of the site. Hope this helps someone.
I found my answer in this post .net DropDownList gets cleared after postback
I changed my counter that I was storing in the viewstate to a session variable.
Then I moved my reloadControls() function from the Page_Load of the *.ASPX to the Page_Init.
The key was dynamically adding my user control in the Page_Init so it would be a member of the page before the Viewstate was applied to controls on the page.

Passing two values through CommandArgument and splitting it

I'm using the commandArgument property of the LinkButton ( Which is wrapped inside a repeater ) to pass two values -as one string- to a second repeater then I try to split them into two values, So I could use them as parameters in my ADO.NET Code (SqlCommand Parameters)....after testing my queries don't return any results but If I passed fixed values for the parameter or change the source of the parameter (just for test from a textbox or querystring or something) I get my results, so I think the problem is in splitting.
I Conduct some arugment values from the ArgumentCommand property of the LinkButton -which is wrapped inside a repeater:
<ItemTemplate>
<asp:LinkButton id="sort_lnkbtn" Text='<%# Eval("value")%>'
CommandArgument='<%#string.Format("{0}|{1}",Eval("arrange_by_id"),Eval("value"))%>' runat="server">
</asp:LinkButton>
</ItemTemplate>
Then I receive these values and cut them into two pieces of information:
string sortByAndArrangeBy = (e.CommandArgument).ToString();
char[] separator = { '|' };
string[] sortByAndArrangeByArray = sortByAndArrangeBy.Split(separator);
Now the ado.net code uses this values as a
using (SqlConnection cn1 = new SqlConnection(ConfigurationManager.ConnectionStrings["testConnectionString"].ConnectionString))
{
using (SqlCommand cm1 = new SqlCommand("SELECT [name] FROM brands WHERE (name like #SearchString + '%' )", cn1))
{
cm1.Parameters.Add("#SearchString", System.Data.SqlDbType.Char);
cm1.Parameters["#SearchString"].Value = sortByAndArrangeByArray[1];
cn1.Open();
using (SqlDataReader dr1 = cm1.ExecuteReader())
{
List_rpt.DataSource = dr1;
List_rpt.DataBind();
}
}
}
Here is a simple solution:
Wrap your item template for the repeater in a control. The control will have the same markup as your item template without the bindings:
Control Markup:
<div>
<asp:LinkButton ID="LnkBtnSort" runat="server" Text="Sort" OnClick="LnkBtnSort_Clicked"/>
</div>
Control Code:
public class SomeControl
{
public event EventHandler Click;
public string ArrangeById
{
set { ViewState["byid"] = value; }
get { return ViewState["byid"].ToString(); }
}
public string Value
{
set { ViewState["val"] = value; }
get { return ViewState["val"].ToString(); }
}
protected void LnkBtnSort_Clicked(object sender, EventArgs e)
{
if( Click != null )
{
this.Click(this, EventArgs.Empty);
}
}
}
So now in the repeater all you have to do is bind an instance of that control to the Container.DataItem:
<ItemTemplate>
<ctrl:SomeControl
ID="someControl"
runat="server"
OnClick="SomeControl_Clicked"
ArrangeById='<%# Eval("arrange_by_id") %>'
Value='<%# Eval("value") %>' />
</ItemTemplate>
The page/control that has the repeater will have one simple method:
protected void SomeControl_Clicked(object sender, EventArgs e)
{
//Here cast the sender to the type of control you made:
SomeControl ctrl = (SomeControl)sender;
string byId = ctrl.ArrangeById;
string val = ctrl.Value;
}
Note: this code may not be 100% correct but it illustrates the point. The flow is simple - the control binds its public properties to whatever you need to bind. When the link is clicked (inside your control) the control doesn't propagate this event to the page. Instead it fires its own event (Click) thus sending a signal to the page that an event occured. however by doing so, it changes the source of the event to itself instead of the actual link button. The page handles the event and everyone is happy.
This way you don't have to care what the CommandArgument is... If this comes up empty, it means that either your data source is empty... or something else happened in the code.

Programmatically Select Item in Asp.Net ListView

After doing a quick search I can't find the answer to this seemingly simple thing to do.
How do I Manually Select An Item in an Asp.Net ListView?
I have a SelectedItemTemplate, but I don't want to use an asp:button or asp:LinkButton to select an item. I want it to be done from a URL. Like a QueryString, for example.
The way I imagine would be on ItemDataBound, check a condition and then set it to selected if true, but how do I do this?
For example:
protected void lv_ItemDataBound(object sender, ListViewItemEventArgs e) {
using (ListViewDataItem dataItem = (ListViewDataItem)e.Item) {
if (dataItem != null) {
if( /* item select condition */ ) {
// What do I do here to Set this Item to be Selected?
// edit: Here's the solution I'm using :
((ListView)sender).SelectedIndex = dataItem.DisplayIndex;
// Note, I get here and it gets set
// but the SelectedItemTemplate isn't applied!!!
}
}
}
}
I'm sure it's one or two lines of code.
EDIT: I've updated the code to reflect the solution, and it seems that I can select the ListView's SelectedItemIndex, however, it's not actually rendering the SelectedItemTemplate. I don't know if I should be doing this in the ItemDataBound event as suggested below.
I looked at some of what's going on in ListView under the hood and think this is probably the best approach.
void listView_ItemCreated(object sender, ListViewItemEventArgs e)
{
// exit if we have already selected an item; This is mainly helpful for
// postbacks, and will also serve to stop processing once we've found our
// key; Optionally we could remove the ItemCreated event from the ListView
// here instead of just returning.
if ( listView.SelectedIndex > -1 ) return;
ListViewDataItem item = e.Item as ListViewDataItem;
// check to see if the item is the one we want to select (arbitrary) just return true if you want it selected
if (DoSelectDataItem(item)==true)
{
// setting the SelectedIndex is all we really need to do unless
// we want to change the template the item will use to render;
listView.SelectedIndex = item.DisplayIndex;
if ( listView.SelectedItemTemplate != null )
{
// Unfortunately ListView has already a selected a template to use;
// so clear that out
e.Item.Controls.Clear();
// intantiate the SelectedItemTemplate in our item;
// ListView will DataBind it for us later after ItemCreated has finished!
listView.SelectedItemTemplate.InstantiateIn(e.Item);
}
}
}
bool DoSelectDataItem(ListViewDataItem item)
{
return item.DisplayIndex == 0; // selects the first item in the list (this is just an example after all; keeping it simple :D )
}
NOTES
ListView selects the template an item will use after it's DataBinding event fires. So if the SelectedIndex is set before then, no more work is necessary
Setting the SelectedIndex anywhere after DataBinding works, you just don't get the SelectedItemTemplate. For that you have either rebind the data; or reinstantiate the SelectedItemTemplate on the ListViewItem. be sure to clear the ListViewItem.Controls collection first!
UPDATE I have removed most of my original solution, since this should work better and for more cases.
You can set the ListViews SelectedIndex
list.SelectedIndex = dataItem.DisplayIndex; // don't know which index you need
list.SelectedIndex = dataItem.DataItemIndex;
Update
If your loading the data on page load you may have to traverse the data to find the index then set the SelectedIndex value before calling the DataBind() method.
public void Page_Load(object sender, EventArgs e)
{
var myData = MyDataSource.GetPeople();
list.DataSource = myData;
list.SelectedIndex = myData.FirstIndexOf(p => p.Name.Equals("Bob", StringComparison.InvariantCultureIgnoreCase));
list.DataBind();
}
public static class EnumerableExtensions
{
public static int FirstIndexOf<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
int count = 0;
foreach(var item in source)
{
if (predicate(item))
return count;
count++;
}
return -1;
}
}
list.SelectedIndex = list.Items.IndexOf(item);
Expanding on #Jeremy and #bendewey's answers, you shouldn't need to do this in ItemDataBound. You only need to have the ListView binding already have taken place before you set the SelectedValue. You should be able to do this during PreRender. See this page life cycle docs for more information on when binding takes place.

Categories

Resources