Bizarre issue with customized gridview control - c#
I have what may be a rather complicated issue. I have an extended gridview control that I swear used to work all the time, but I went away for a while, came back, and it doesn't work anymore (I'm the sole programmer).
The extended gridview is designed so that it always shows a footer row (for inserting new rows). It loads and displays existing data correctly. If there are no rows, then adding the data works fine. But if I'm adding a new row to a gridview that already has existing rows, I get an issue where gvPhones.FooterRow is null, so it can't find the control I'm referencing.
Here's the extended gridview class (gotten from a stackoverflow page):
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
//https://stackoverflow.com/questions/994895/always-show-footertemplate-even-no-data/10891744#10891744
namespace WebForms.LocalCodeLibrary.Controls
{
//modified from https://stackoverflow.com/questions/3437581/show-gridview-footer-on-empty-grid
public class GridViewExtended : GridView
{
private GridViewRow _footerRow;
[DefaultValue(false), Category("Appearance"), Description("Include the footer when the table is empty")]
public bool ShowFooterWhenEmpty { get; set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
public override GridViewRow FooterRow
{
get
{
if ((this._footerRow == null))
{
this.EnsureChildControls();
}
return this._footerRow;
}
}
protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)
{
//creates all the rows that would normally be created when instantiating the grid
int returnVal = base.CreateChildControls(dataSource, dataBinding);
//if no rows were created (i.e. returnVal == 0), and we need to show the footer row, then we need to create and bind the footer row.
if (returnVal == 0 && this.ShowFooterWhenEmpty)
{
Table table = this.Controls.OfType<Table>().First<Table>();
DataControlField[] dcf = new DataControlField[this.Columns.Count];
this.Columns.CopyTo(dcf, 0);
//creates the footer row
this._footerRow = this.CreateRow(-1, -1, DataControlRowType.Footer, DataControlRowState.Normal, dataBinding, null, dcf, table.Rows, null);
if (!this.ShowFooter)
{
_footerRow.Visible = false;
}
}
return returnVal;
}
private GridViewRow CreateRow(int rowIndex, int dataSourceIndex, DataControlRowType rowType, DataControlRowState rowState, bool dataBind, object dataItem, DataControlField[] fields, TableRowCollection rows, PagedDataSource pagedDataSource)
{
GridViewRow row = this.CreateRow(rowIndex, dataSourceIndex, rowType, rowState);
GridViewRowEventArgs e = new GridViewRowEventArgs(row);
if ((rowType != DataControlRowType.Pager))
{
this.InitializeRow(row, fields);
}
else
{
this.InitializePager(row, fields.Length, pagedDataSource);
}
//if the row has data, sets the data item
if (dataBind)
{
row.DataItem = dataItem;
}
//Raises the RowCreated event
this.OnRowCreated(e);
//adds the row to the gridview's row collection
rows.Add(row);
//explicitly binds the data item to the row, including the footer row and raises the RowDataBound event.
if (dataBind)
{
row.DataBind();
this.OnRowDataBound(e);
row.DataItem = null;
}
return row;
}
}
}
Here's the relevant stuff in the ASPX page:
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ContactEdit.aspx.cs" Inherits="WebForms.Directory.ContactEdit" %>
<%# Register TagPrefix="gcctl" Namespace="WebForms.LocalCodeLibrary.Controls" Assembly="WebForms" %>
<asp:Content ID="Content1" ContentPlaceHolderID="Head" runat="server">
<style>
</style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div id="bodycontent" class="body-content">
<h2><asp:Literal ID="formheader" runat="server" /></h2>
<!-- Start: Main Customer section -->
<asp:Panel ID="mainformcontent" runat="server" CssClass="formsection">
<section id="mainform" class="simplelayoutform">
<asp:TextBox id="customerid" type="hidden" runat="server" />
<asp:TextBox id="contactid" type="hidden" runat="server" />
</section>
</asp:Panel>
<!-- End: Main Customer section -->
<!-- Start: Phones section -->
<asp:SqlDataSource ID="gvPhonesDataSource" runat="server" OnInserted="gvPhonesDataSource_Inserted"
ConnectionString="<%$ ConnectionStrings:ConnString %>"
SelectCommand="SELECT p.[CustomerPhoneID]
,p.[CustomerID]
,LTRIM(COALESCE(cc.FirstName,'') + ' ' + COALESCE(cc.LastName,'')) AS ContactFullName
,p.CustomerContactID
,p.PhoneTypeID
,lp.PhoneType
,p.[PhoneNumber]
,p.[Extension]
,p.[FormattedPhone]
,p.[IsActive]
,CASE WHEN p.LocationID IS NULL THEN CASE WHEN p.CustomerContactID IS NULL THEN 0 ELSE 1 END ELSE 2 END AS SortOrder
FROM [dbo].[Phones] p
LEFT JOIN dbo.Contacts cc ON p.CustomerContactID = cc.CustomerContactID
LEFT JOIN list.PhoneTypes lp ON p.PhoneTypeID = lp.PhoneTypeID
WHERE p.CustomerContactID = #CustomerContactID"
DeleteCommand="DELETE FROM [dbo].[Phones] WHERE [CustomerPhoneID] = #CustomerPhoneID"
InsertCommand="INSERT INTO [dbo].[Phones] ([CustomerID]
, [CustomerContactID]
, [PhoneNumber]
, [Extension]
, [PhoneTypeID]
, LastModifiedByStaffID)
VALUES (#CustomerID
, #CustomerContactID
, CASE WHEN COALESCE(#FormattedPhone, '')='' THEN NULL ELSE LTRIM(RTRIM(LEFT(dbo.RemoveNonNumeric(#FormattedPhone),10))) END
, CASE WHEN COALESCE(#FormattedPhone, '')='' THEN NULL ELSE CASE WHEN LTRIM(RTRIM(SUBSTRING(dbo.RemoveNonNumeric(#FormattedPhone),11,1000))) = '' THEN NULL ELSE LTRIM(RTRIM(SUBSTRING(dbo.RemoveNonNumeric(#FormattedPhone),11,1000))) END END
, #PhoneTypeID
, #StaffID)"
UpdateCommand="UPDATE [dbo].[CustomerPhones]
SET [CustomerContactID] = #CustomerContactID
, [PhoneNumber] = CASE WHEN COALESCE(#FormattedPhone, '')='' THEN NULL ELSE LTRIM(RTRIM(LEFT(dbo.RemoveNonNumeric(#FormattedPhone),10))) END
, [Extension] = CASE WHEN COALESCE(#FormattedPhone, '')='' THEN NULL ELSE CASE WHEN LTRIM(RTRIM(SUBSTRING(dbo.RemoveNonNumeric(#FormattedPhone),11,1000))) = '' THEN NULL ELSE LTRIM(RTRIM(SUBSTRING(dbo.RemoveNonNumeric(#FormattedPhone),11,1000))) END END
, [PhoneTypeID] = #PhoneTypeID
, [IsActive] = #IsActive
, [DateModified] = getdate()
, [LastModifiedByStaffID] = #StaffID
WHERE [CustomerPhoneID] = #CustomerPhoneID">
<SelectParameters>
<asp:ControlParameter Name="CustomerContactID" Type="Int32" ControlID="contactid" PropertyName="Text" />
</SelectParameters>
<DeleteParameters>
<asp:Parameter Name="CustomerPhoneID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:ControlParameter Name="CustomerContactID" Type="Int32" ControlID="contactid" PropertyName="Text" />
<asp:Parameter Name="FormattedPhone" Type="String" />
<asp:Parameter Name="PhoneTypeID" Type="Int32" />
<asp:Parameter Name="IsActive" Type="Boolean" />
<asp:SessionParameter Name="StaffID" Type="Int32" SessionField="StaffID" />
<asp:Parameter Name="CustomerPhoneID" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:ControlParameter Name="CustomerID" Type="Int32" ControlID="customerid" PropertyName="Text" />
<asp:ControlParameter Name="CustomerContactID" Type="Int32" ControlID="contactid" PropertyName="Text" />
<asp:Parameter Name="PhoneTypeID" Type="Int32" />
<asp:Parameter Name="FormattedPhone" Type="String" />
<asp:SessionParameter Name="StaffID" Type="Int32" SessionField="StaffID" />
</InsertParameters>
</asp:SqlDataSource>
<asp:Panel ID="phonesformcontent" runat="server" CssClass="formsection separate">
<section id="phonesform" class="simplelayoutform">
<h3>All Phones</h3>
<gcctl:MyCheckBox ID="chkPhoneShowInactive" Text="Show Inactive?" Checked="false" AutoPostBack="true" OnCheckedChanged="chkPhoneShowInactive_CheckedChanged" runat="server" />
<asp:label id="lblPhoneMessage" CssClass="responsemsg" runat="server" enableviewstate="False" />
<gcctl:gridviewextended ID="gvPhones" runat="server" DataSourceID="gvPhonesDataSource"
AutoGenerateColumns="False" DataKeyNames="CustomerPhoneID" EmptyDataText="No phones on record."
CssClass="searchresultsgrid" ShowFooter="True" OnRowCommand="gvPhones_RowCommand" AllowSorting="True"
ShowFooterWhenEmpty="true" OnRowDataBound="gvPhones_RowDataBound">
<Columns>
<asp:BoundField DataField="CustomerPhoneID" InsertVisible="false" ReadOnly="true" Visible="False" />
<asp:TemplateField HeaderText="Phone Type" SortExpression="PhoneType">
<FooterTemplate>
<asp:DropDownList ID="cboPhoneTypeID" runat="server"
DataSourceID="DataSourcePhoneTypes" DataTextField="PhoneType" DataValueField="PhoneTypeID"
SelectedValue='<%# Bind("PhoneTypeID") %>'>
</asp:DropDownList>
</FooterTemplate>
<EditItemTemplate>
<asp:DropDownList ID="cboPhoneTypeID" runat="server"
DataSourceID="DataSourcePhoneTypes" DataTextField="PhoneType" DataValueField="PhoneTypeID"
SelectedValue='<%# Bind("PhoneTypeID") %>'>
</asp:DropDownList>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="lblPhoneTypeID" runat="server" Text='<%# Bind("PhoneType") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Phone" SortExpression="PhoneNumber">
<FooterTemplate>
<asp:TextBox runat="server" Text='<%# Bind("FormattedPhone") %>' ID="txtPhone"></asp:TextBox>
</FooterTemplate>
<EditItemTemplate>
<asp:TextBox runat="server" Text='<%# Bind("FormattedPhone") %>' ID="txtPhone"></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label runat="server" Text='<%# Bind("FormattedPhone") %>' ID="lblPhone"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Active?" SortExpression="IsActive">
<FooterTemplate>
<asp:CheckBox runat="server" Checked='<%# Bind("IsActive") %>' ID="chkPhoneIsActive"></asp:CheckBox>
</FooterTemplate>
<EditItemTemplate>
<asp:CheckBox runat="server" Checked='<%# Bind("IsActive") %>' ID="chkPhoneIsActive"></asp:CheckBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label runat="server" Text='<%# Bind("IsActive") %>' ID="lblPhoneIsActive"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ShowHeader="False">
<EditItemTemplate>
<asp:LinkButton runat="server" Text="Update" CommandName="Update" CausesValidation="True" ID="PhoneUpdate"></asp:LinkButton> <asp:LinkButton runat="server" Text="Cancel" CommandName="Cancel" CausesValidation="False" ID="PhoneEditCancel"></asp:LinkButton>
</EditItemTemplate>
<ItemTemplate>
<asp:LinkButton runat="server" Text="Edit" CommandName="Edit" CausesValidation="False" ID="PhoneEdit"></asp:LinkButton> <asp:LinkButton runat="server" Text="Delete" CommandName="Delete" CausesValidation="False" ID="PhoneDelete"></asp:LinkButton>
</ItemTemplate>
<FooterTemplate>
<asp:LinkButton runat="server" Text="Save New Phone" CommandName="FooterInsert" CausesValidation="True" ID="PhoneInsert"></asp:LinkButton>
</FooterTemplate>
</asp:TemplateField>
</Columns>
</gcctl:gridviewextended>
<div id="phonenotes" class="tip">
<div>NUMBERS ONLY - NO LETTER CODES IN THE PHONE FIELD!</div>
<div>Be sure to always enter the area code, especially if you're also adding an extension.</div>
<div>Note that only numbers will stay in the "Phone" field. Anything else you enter will disappear once it goes behind the scenes. The first 10 digits will become the phone number, and any remaining digits will become the extension.</div>
</div>
</section>
</asp:Panel>
<!-- End: Phones section -->
<div id="responsetextdiv" class="error"><asp:Literal ID="responsetext" runat="server"></asp:Literal></div>
</div>
<asp:XmlDataSource ID="DataSourcePhoneTypes" runat="server" DataFile="~/XML/PhoneTypes.xml" EnableCaching="true">
</asp:XmlDataSource>
</asp:Content>
Here's the code where I get the error:
protected void gvPhones_RowCommand(object sender, GridViewCommandEventArgs e)
{
// Insert data if the CommandName == "Insert"
// and the validation controls indicate valid data...
if (e.CommandName == "FooterInsert" && Page.IsValid)
{
//ERROR HAPPENS ON THE FOLLOWING LINE:
DropDownList PhoneTypeID = (DropDownList)gvPhones.FooterRow.FindControl("cboPhoneTypeID");
TextBox FormattedPhone = (TextBox)gvPhones.FooterRow.FindControl("txtPhone");
gvPhonesDataSource.InsertParameters["PhoneTypeID"].DefaultValue = PhoneTypeID.SelectedValue.ToString();
string sFormattedPhone = null;
if (!string.IsNullOrEmpty(FormattedPhone.Text))
sFormattedPhone = FormattedPhone.Text;
gvPhonesDataSource.InsertParameters["FormattedPhone"].DefaultValue = sFormattedPhone;
gvPhonesDataSource.InsertParameters["CustomerID"].DefaultValue = customerid.Text.ToString();
gvPhonesDataSource.InsertParameters["CustomerContactID"].DefaultValue = contactid.Text.ToString();
gvPhonesDataSource.InsertParameters["StaffID"].DefaultValue = System.Web.HttpContext.Current.Session["StaffID"].ToString();
// Insert new record
gvPhonesDataSource.Insert();
}
}
The full error I get is:
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
Source Error:
Line 276: if (e.CommandName == "FooterInsert" && Page.IsValid)
Line 277: {
Line 278: DropDownList PhoneTypeID = (DropDownList)gvPhones.FooterRow.FindControl("cboPhoneTypeID");
Line 279: TextBox FormattedPhone = (TextBox)gvPhones.FooterRow.FindControl("txtPhone");
Line 280:
Source File: <snip> Line: 278
Stack Trace:
[NullReferenceException: Object reference not set to an instance of an object.]
GCWebForms.Directory.ContactEdit.gvPhones_RowCommand(Object sender, GridViewCommandEventArgs e) in <snip>ContactEdit.aspx.cs:278
System.Web.UI.WebControls.GridView.OnRowCommand(GridViewCommandEventArgs e) +137
System.Web.UI.WebControls.GridView.HandleEvent(EventArgs e, Boolean causesValidation, String validationGroup) +95
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +49
System.Web.UI.WebControls.GridViewRow.OnBubbleEvent(Object source, EventArgs e) +146
System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) +49
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5450
When stepping through (when trying to add a new row to a gridview that already has data in it), I found that gvPhones.FooterRow says that it's null. Again, this only happens if there is data in gvPhones. If the datatable is empty, then the footerrow insert code works without a hitch.
Any help would be greatly appreciated! :-)
EDIT: adding the relevant code behind Page_Load. I just added the DataBind() statement, but it didn't make a difference.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
bool bolNewRec = (this.iContactID == null);
phonesformcontent.Visible = (!bolNewRec);
if (bolNewRec)
{ //snipping unrelated code
}
else
{
//snipping code that loads the data into the page
gvPhones.Sort("SortOrder, PhoneType", SortDirection.Ascending);
}
}
if (phonesformcontent.Visible)
gvPhones.DataBind();
}
...and, just in case, here's RowDataBound:
protected void gvPhones_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView rowView = (DataRowView)e.Row.DataItem;
bool bolShowInactive = chkPhoneShowInactive.Checked;
if (!bolShowInactive && (Convert.ToBoolean(rowView["IsActive"]) == false))
e.Row.Visible = false;
else
e.Row.Visible = true;
rowView = null;
}
if (e.Row.RowType == DataControlRowType.Footer)
{
CheckBox chkIsActive = (CheckBox)e.Row.FindControl("chkPhoneIsActive");
chkIsActive.Checked = true;
chkIsActive = null;
}
}
Try using the sender in your code as below:
Replace this line:
DropDownList PhoneTypeID = (DropDownList)gvPhones.FooterRow.FindControl("cboPhoneTypeID");
For this:
DropDownList PhoneTypeID = (DropDownList)((GridView)sender).FooterRow.FindControl("cboPhoneTypeID");
Also, check the page load if the problem is not with the postback.
My answer is based on this question:
Unable to get gridview footer values in RowCommand
UPDATE:
Change your GridViewExtended class,
ShowFooterWhenEmpty property:
[Category("Behavior")]
[Themeable(true)]
[Bindable(BindableSupport.No)]
public bool ShowFooterWhenEmpty
{
get
{
if (this.ViewState["ShowFooterWhenEmpty"] == null)
{
this.ViewState["ShowFooterWhenEmpty"] = false;
}
return (bool)this.ViewState["ShowFooterWhenEmpty"];
}
set
{
this.ViewState["ShowFooterWhenEmpty"] = value;
}
}
GridViewRow:
private GridViewRow _footerRow;
public override GridViewRow FooterRow
{
get
{
GridViewRow f = base.FooterRow;
if (f != null)
return f;
else
return _footerRow;
}
}
I based my changes on this link:
Always show FooterTemplate, even no data
I wound up scrapping this entire class. Instead, I made regular asp:gridviews that are based on datasources that have union selects with one row with -1 in the key column (since all of my tables have single autoincrement PKs, no row will legitimately have -1 in the key column), and then put the following in RowDataBound:
if (e.Row.RowType == DataControlRowType.DataRow)
{
DataRowView rowView = (DataRowView)e.Row.DataItem;
string sKeyName = gvPhones.DataKeyNames[0].ToString();
if ((rowView[sKeyName].ToString() == "-1"))
e.Row.Visible = false;
else
e.Row.Visible = true;
rowView = null;
}
This hides any row with -1 in the key column. So there's always at least one row in the gridview (even if that one row is hidden), and the footer row always shows.
Related
Modifying checkboxes from a datalist ( C# / asp.net )
I'm trying to modify this asp.net / C# code. The page lets an admin add a user and choose what roles this user will have from a list of checkboxes. There are 11 checkboxes with roles next to them to pick from. I'm wanting to organize these checkboxes. I'm wanting to divide the 11 checkboxes into 3 groups CMS, LMS, and Admin in order to group them into three sections on the page. I'm wanting only one checkbox to be selectable for the CMS group. The CMS group can be a dropdown box if that would make it easier. And, all of these roles must have a tooltip when the user hovers their mouse over them. You'll see in the code that the checkboxes get created using the userRolesDataSource. This datasource uses a method called "GetUserRoles" which has two parameters the "username" and the "onlyIncludeSystemAdminRole" boolean. The selection method checks if the username is a system admin and excludes all other role choices if this is true. This is the new GetUserRoles method: public List<Pair<string, bool>> GetUserRoles( string userName, bool onlyIncludeSystemAdminRole) { // Return a list of "role name"/"has role" pairs for the given user. // If the "onlyIncludeSystemAdminRole" flag is true, only return the // "System Admin" role, otherwise return all roles other than // "System Admin". List<Pair<string, bool>> userRolesList = null; if (!onlyIncludeSystemAdminRole) { string[] allRoles = Roles.GetAllRoles(); HashSet<string> rolesForUser = null; if (!String.IsNullOrWhiteSpace(userName)) { rolesForUser = new HashSet<string>( Roles.GetRolesForUser(userName)); } userRolesList = (from roleName in allRoles where roleName != RoleNames.SYSTEM_ADMIN select new Pair<string, bool>() { First = roleName, Second = rolesForUser != null ? rolesForUser.Contains(roleName) : false }).ToList(); } else { userRolesList = new List<Pair<string, bool>>() { new Pair<string, bool>( RoleNames.SYSTEM_ADMIN, Roles.IsUserInRole(userName, RoleNames.SYSTEM_ADMIN)) }; } return userRolesList; } Something like that will still have to be run on all the checkboxes. In order to solve the problem I planned on using something like this method. It can be given a list of roles as a parameter. And, it'll exclude the list provided and return every other role. public IEnumerable<string> GetRoles(params string[] roleNamesToExclude) { var query = Uow.Roles .AsQueryable() .Select(r => r.RoleName); if (roleNamesToExclude != null && roleNamesToExclude.Any()) { query = query .Where(r => !roleNamesToExclude.Contains(r)); } return query.ToList(); } My idea for re-building it was to replace userRolesDataSource with 3 new datasources: CMSRolesDataSource, LMSRolesDataSource, AdminRolesDataSource. Then create a new method that takes username, systemadmin boolean, as well as a list of excludable roles like the method above. I'm still pretty new to coding and I'm wondering if there's a simpler way of doing all of this. The code to the asp and c# are below. Thanks in advance. asp <asp:ObjectDataSource ID="userRolesDataSource" runat="server" OnSelecting="userRolesDataSource_Selecting" OldValuesParameterFormatString="{0}" SelectMethod="GetUserRoles" TypeName="VirtualExpertClinics.AutismProClassroom.Data.UserBLL"> <SelectParameters> <asp:Parameter Name="userName" Type="String" /> <asp:Parameter Name="onlyIncludeSystemAdminRole" Type="Boolean" /> </SelectParameters> </asp:ObjectDataSource> <asp:ObjectDataSource ID="titlesDataSource" runat="server" OldValuesParameterFormatString="{0}" SelectMethod="GetOrganizationTitles" TypeName="VirtualExpertClinics.AutismProClassroom.Data.OrganizationBLL" OnSelecting="titlesDataSource_Selecting"> <SelectParameters> <asp:Parameter Name="organizationId" Type="Int32" /> </SelectParameters> </asp:ObjectDataSource> <h5><%: Resources.Global.UserRoles %></h5> <asp:DataList ID="userRolesDataList" runat="server" CssClass="formTable" DataSourceID="userRolesDataSource" Enabled="false"> <ItemTemplate> <asp:CheckBox ID="userRoleCheckBox" runat="server" Text='<%# Eval("First") %>' Checked='<%# Eval("Second") %>' /> </ItemTemplate> </asp:DataList> </ItemTemplate> <EditItemTemplate> <asp:Panel ID="userDetailsPanel" runat="server" DefaultButton="saveButton"> <table class="formTable extraCellPadding"> <tr> <td class="alignTop"> <asp:Label ID="userRolesLabel" runat="server" Text="<%$ Resources:Global, UserRoles %>"></asp:Label> </td> <td> <asp:CustomValidator ID="userRolesCustomValidator" runat="server" ValidationGroup="validationGroup" OnServerValidate="userRolesCustomValidator_ServerValidate" Display="Dynamic">*</asp:CustomValidator> <asp:DataList ID="userRolesDataList" runat="server" CssClass="formTable" OnItemDataBound="userRolesDataList_ItemDataBound" DataSourceID="userRolesDataSource"> <ItemTemplate> <asp:CheckBox ID="userRoleCheckBox" runat="server" Text='<%# Eval("First") %>' Checked='<%# Eval("Second") %>' /> </ItemTemplate> </asp:DataList> </td> </tr> </table> <div style="margin-top: 1em;"> <asp:Button ID="saveButton" runat="server" CssClass="btn" ValidationGroup="validationGroup" CommandName="Update" Text="<%$ Resources:Global, Save %>" /> <asp:Button ID="cancelButton" runat="server" CssClass="btn" CommandName="Cancel" Text="<%$ Resources:Global, Cancel %>" /> </div> </asp:Panel> </EditItemTemplate> <InsertItemTemplate> <asp:Panel ID="userDetailsPanel" runat="server" DefaultButton="saveButton"> <table class="formTable extraCellPadding"> <tr> <td class="alignTop"> <asp:Label ID="userRolesLabel" runat="server" CssClass="label" Text="<%$ Resources:Global, UserRoles %>"></asp:Label> </td> <td> <asp:CustomValidator ID="userRolesCustomValidator" runat="server" ValidationGroup="validationGroup" OnServerValidate="userRolesCustomValidator_ServerValidate" Display="Dynamic">*</asp:CustomValidator> <asp:DataList ID="userRolesDataList" runat="server" CssClass="formTable" OnItemDataBound="userRolesDataList_ItemDataBound" DataSourceID="userRolesDataSource"> <ItemTemplate> <asp:CheckBox ID="userRoleCheckBox" runat="server" Text='<%# Eval("First") %>' Checked='<%# Eval("Second") %>' /> </ItemTemplate> </asp:DataList> </td> </tr> </table> <div style="margin-top: 1em;"> <asp:Button ID="saveButton" runat="server" CssClass="btn" ValidationGroup="validationGroup" CommandName="Insert" Text="<%$ Resources:Global, Save %>" /> <asp:Button ID="cancelButton" runat="server" CssClass="btn" CommandName="Cancel" Text="<%$ Resources:Global, Cancel %>" /> </div> </asp:Panel> </InsertItemTemplate> </asp:FormView> </div> C# protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { if (SessionWrapper.ActiveUserID == null) { userFormView.ChangeMode(FormViewMode.Insert); } } } protected void detailDataSource_Inserting( object sender, ObjectDataSourceMethodEventArgs e) { DataList userRolesDataList = (DataList)userFormView.Row.FindControl( "userRolesDataList"); List<string> roles = GetSelectedUserRoles(userRolesDataList); if (SessionWrapper.WorkingOrganizationId != null) { e.InputParameters["organizationId"] = SessionWrapper.WorkingOrganizationId; } else { e.InputParameters["organizationId"] = SessionWrapper.ActiveOrganizationID; } e.InputParameters["acceptedAgreement"] = false; e.InputParameters["changedPassword"] = false; e.InputParameters["mustUpdateProfile"] = true; e.InputParameters["timeZoneId"] = TimeZoneInfo.Utc.Id; e.InputParameters["roles"] = roles; e.InputParameters["registrationCodeId"] = null; e.InputParameters["createPasswordTicket"] = true; MiscUtil.TrimInputParameters(e.InputParameters); } protected void detailDataSource_Updating( object sender, ObjectDataSourceMethodEventArgs e) { DataList userRolesDataList = (DataList)userFormView.Row.FindControl( "userRolesDataList"); e.InputParameters["roles"] = GetSelectedUserRoles(userRolesDataList); UserProfileDS.UserProfileRow user = UserBLL.GetUserByUserID( (Guid)userFormView.SelectedValue); e.InputParameters["acceptedAgreement"] = user.AcceptedAgreement; e.InputParameters["changedPassword"] = user.ChangedPassword; e.InputParameters["mustUpdateProfile"] = user.MustUpdateProfile; e.InputParameters["timeZoneId"] = user.TimeZoneId; MiscUtil.TrimInputParameters(e.InputParameters); } protected void userRolesDataList_ItemDataBound( object sender, DataListItemEventArgs e) { Pair<string, bool> rolePair = (Pair<string, bool>)e.Item.DataItem; if (rolePair.First == RoleNames.CMS_OWNER || rolePair.First == RoleNames.CMS_DATA_ENTRY || rolePair.First == RoleNames.CMS_BROWSER) { // Disable the CMS roles if CMS access is not enabled for the // current organization. OrganizationDS.OrganizationRow organizationRow = OrganizationBLL.GetOrganizationById( SessionWrapper.ActiveOrganizationID); if (!organizationRow.EnableCMS) { CheckBox userRoleCheckBox = (CheckBox)e.Item.FindControl( "userRoleCheckBox"); userRoleCheckBox.Enabled = false; userRoleCheckBox.ToolTip = Resources.Global.CMSNotEnabled; } } else if (rolePair.First == RoleNames.WORKSHOPS_USER) { // Disable the "Workshops User" role if the corresponding // check box is not selected and the current organization has // reached their maximum number of learners. if (!rolePair.Second && OrganizationBLL.HasReachedMaxLearners( SessionWrapper.ActiveOrganizationID)) { CheckBox userRoleCheckBox = (CheckBox)e.Item.FindControl( "userRoleCheckBox"); userRoleCheckBox.Enabled = false; userRoleCheckBox.ToolTip = Resources.Global.OrganizationMaxLearnersReached; } } else if (rolePair.First == RoleNames.LOCAL_SYSTEM_ADMIN) { // Disable the "Local System Admin" role if the current user // is not a system admin. if (!Roles.IsUserInRole(RoleNames.SYSTEM_ADMIN)) { CheckBox userRoleCheckBox = (CheckBox)e.Item.FindControl( "userRoleCheckBox"); userRoleCheckBox.Enabled = false; } } } protected void userRolesDataSource_Selecting( object sender, ObjectDataSourceSelectingEventArgs e) { string userName; bool onlyIncludeSystemAdminRole; if (userFormView.CurrentMode == FormViewMode.Insert) { userName = ""; onlyIncludeSystemAdminRole = (SessionWrapper.ActiveOrganizationID == 0); } else { UserProfileDS.UserProfileRow userRow = (UserProfileDS.UserProfileRow)userFormView.DataItem; userName = userRow.UserName; onlyIncludeSystemAdminRole = Roles.IsUserInRole( userRow.UserName, RoleNames.SYSTEM_ADMIN); } e.InputParameters["userName"] = userName; e.InputParameters["onlyIncludeSystemAdminRole"] = onlyIncludeSystemAdminRole; } private List<string> GetSelectedUserRoles(DataList userRolesDataList) { List<string> roles = new List<string>(); foreach (DataListItem item in userRolesDataList.Items) { CheckBox userRoleCheckBox = (CheckBox)item.FindControl( "userRoleCheckBox"); if (userRoleCheckBox.Checked) { roles.Add(userRoleCheckBox.Text); } } return roles; } protected void userRolesCustomValidator_ServerValidate( object source, ServerValidateEventArgs args) { CustomValidator userRolesCustomValidator = (CustomValidator)source; DataList userRolesDataList = (DataList)userFormView.Row.FindControl( "userRolesDataList"); List<string> roles = GetSelectedUserRoles(userRolesDataList); AdministrationUtil.ValidateUserRoleSelection( roles, userRolesCustomValidator, args); } }
Retrieving Current Value from Telerik RadGrid EditFormItem
I have an ASP form created using Telerik's RadGrid control, already binding values from the database and displaying them, with the ability to add and delete entries from the table. I need to add editing functionality, but I've finally run into a block, and I can't find a way past it. The ASP page contains both the RadGrid and the SqlDataSource I need to use: <asp:SqlDataSource ID="ContactsSqlDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:DBConnectionString %>" SelectCommand="etc..." UpdateCommand="UPDATE Contacts SET Salutation=#Salutation, FirstName=#FirstName, etc... WHERE ClientContactID=#ClientContactID" CancelSelectOnNullParameter="False" onupdating="ContactsSqlDataSource_Updating"> <UpdateParameters> <asp:Parameter Name="Salutation" Type="String" /> <asp:Parameter Name="FirstName" Type="String" /> etc... <asp:Parameter Name="ClientContactID" Type="String" /> </UpdateParameters> <SelectParameters> <asp:QueryStringParameter Name="GroupID" QueryStringField="ID" /> </SelectParameters> </asp:SqlDataSource> etc... <telerik:RadGrid ID="ContactsRadGrid" runat="server" AutoGenerateColumns="False" AutoGenerateDeleteColumn="True" CellSpacing="0" DataSourceID="ContactsSqlDataSource" GridLines="None" Skin="Windows7" onitemcommand="ContactsRadGrid_ItemCommand" onitemdatabound="ContactsRadGrid_ItemDataBound" AllowAutomaticUpdates="True"> <MasterTableView DataSourceID="ContactsSqlDataSource" DataKeyNames="etc..."> <Columns> etc... </Columns> <EditFormSettings EditFormType="Template"> <FormTemplate> <asp:HiddenField ID="ClientIDHiddenField" runat="server" Value='<%# Bind("ClientContactID") %>' /> etc... <asp:TextBox ID="SalutationTextBox" runat="server" Text='<%# Bind("Salutation") %>' TabIndex="1" /> etc... <asp:TextBox ID="FirstNameTextBox" runat="server" Text='<%# Bind("FirstName") %>' TabIndex="2" /> </FormTemplate> </EditFormSettings> </MasterTableView> </telerik:RadGrid> In my code behind, I have: protected void ContactsRadGrid_ItemCommand(object sender, Telerik.Web.UI.GridCommandEventArgs e) { RadGrid grid = sender as RadGrid; GridDataItem dataItem = e.Item as GridDataItem; GridEditFormItem editItem = e.Item as GridEditFormItem; if (e.CommandName == "Delete") { etc... } else if (e.CommandName == "Update") { ContactsSqlDataSource.Update(); } } protected void ContactsSqlDataSource_Updating(object sender, SqlDataSourceCommandEventArgs e) { GridEditFormItem editItem = (ContactsRadGrid.EditItems[0] as GridDataItem).EditFormItem; string salutation = Convert.ToString(editItem.GetDataKeyValue("Salutation")); string firstName = Convert.ToString(editItem.GetDataKeyValue("FirstName")); etc... string clientContactID = Convert.ToString(editItem.GetDataKeyValue("ClientContactID")); e.Command.Parameters["#Salutation"].Value = salutation; e.Command.Parameters["#FirstName"].Value = firstName; etc... e.Command.Parameters["#ClientContactID"].Value = clientContactID; e.Command.Connection.Open(); e.Command.ExecuteNonQuery(); e.Command.Connection.Close(); } This is where I finally got to in order to stop getting errors on the SQL execution, however there's one major problem: the values being pushed onto the parameters are the old values, the ones that were already in the database, not the values I've entered in the EditFormItem. If I hardcode something onto a parameter (eg, append ".com" onto email), the alteration is correctly reflected in both the database and the table after the "Save" button in the edit form is clicked. Without the manual edit, no changes occur, because the database is simply being updated with the values that were already present. How can I get the current values in the edit form? (I'm not opposed to getting this done in some way other than the DataSource_Updating event, that's just the solution I came up with which was closest to correct. I do need to stick with the RadGrid, though.)
I managed to find a resolution to this issue. In the first part of ContactsSqlDataSource_Updating, I now have: string salutation = (editItem.FindControl("SalutationTextBox") as TextBox).Text; string firstName = (editItem.FindControl("FirstNameTextBox") as TextBox).Text; etc... string clientContactID = (editItem.FindControl("ClientIDHiddenField") as HiddenField).Value; The database now updates correctly.
Catching null values on databinding
I have a dropdownlist that is populated from an SQL select statement. The select statement filters for items where the 'bit' is set to false and the items, although still in the database are hidden. My problem is; when an item is out of stock or hidden (bit = false) the user may still have items that are now hidden so it throws an error. How and where can I catch this, show the original item or set the value to default? protected void GradeDropDownList_DataBinding (object sender, EventArgs e) { var ddl = (DropDownList)(sender); var a = ((Label)MyDetailsView.FindControl("GradeLabelEdit")).Text; a = a.Trim(); if (a != "") { ddl.SelectedValue = a; } } The select statement; <asp:SqlDataSource ID="getGrade" runat="server" ConnectionString="<%$ ConnectionStrings:CasesTimeConnection %>" SelectCommand="SELECT [gradeID], [gradeText] FROM [user_grades] WHERE ([visibleState] = #visibleState)"> <SelectParameters> <asp:Parameter DefaultValue="True" Name="visibleState" Type="Boolean" /> </SelectParameters> </asp:SqlDataSource> In page; <EditItemTemplate> <asp:DropDownList ID="GradeDropDownList" runat="server" DataSourceID="getGrade" DataTextField="gradeText" DataValueField="gradeID" OnDataBinding="GradeDropDownList_DataBinding" OnSelectedIndexChanged="GradeDropDownList_SelectedIndexChanged"> </asp:DropDownList> </EditItemTemplate> <ItemTemplate> <asp:Label ID="gradeLabel" runat="server" Text='<%# Bind("gradeText") %>'></asp:Label> </ItemTemplate>
Retrieve LastModifiedDate while retrieving the records and while trying to update the record check the existing lastModifiedDate value with the one you retrieved. If they are different then throw an alert message / display new quantity. Thanks Shashi
Problems with AJAX CascadingDropDown and DropDownList SelectedValue in EditItemTemplate
I am having problem in EditItemTemplate of FormView. When I use such code in InsertItemTemplate everything works: <asp:DropDownList ID="Lic_PosiadaczLicencjiIDDropDownList" runat="server" SelectedValue='<%# Bind("Lic_PosiadaczLicencjiID") %>' /> <asp:CascadingDropDown ID="CascadingDropDown1" runat="server" TargetControlID="Lic_PosiadaczLicencjiIDDropDownList" Category="Knt_Kod" ServicePath="~/ManagerLicencjiService.asmx" ServiceMethod="GetKontrahenci"> </asp:CascadingDropDown> But when I use exactly the same code in EditItemTemplate I am getting an error that SelectedValue is wrong cause it doesn't exists on the list of elements. I think that the problem is that DropDownList is checked for the values before it is populated by the service. When I run debugger the error occured before breakpoint in the service method. How to solve this problem?
<rant>I've found the CCD very clunky and full of poorly-documented workarounds</rant> but here is how you do something as simple as selecting a value when filling the ddl. Note that the selected value is not set on the DDL and that it is being passed to the web service where the selecting is done. <asp:ScriptManager ID="sm1" runat="server"></asp:ScriptManager> <asp:FormView ID="fv1" runat="server" DataSourceID="yourDataSource"> <EditItemTemplate> <asp:DropDownList ID="Lic_PosiadaczLicencjiIDDropDownList" runat="server" /> <asp:CascadingDropDown ID="CascadingDropDown1" runat="server" TargetControlID="Lic_PosiadaczLicencjiIDDropDownList" Category="Knt_Kod" ServicePath="~/ManagerLicencjiService.asmx" ServiceMethod="GetKontrahenci" UseContextKey="true" ContextKey='<%# Bind("Lic_PosiadaczLicencjiID") %>'> </asp:CascadingDropDown> </EditItemTemplate> </asp:FormView> <asp:sqldatasource id="yourDataSource" selectcommand="select Lic_PosiadaczLicencjiID FROM yourdatabase" UpdateCommand="Update yourdatabase set Lic_PosiadaczLicencjiID = #newvalue WHERE Lic_PosiadaczLicencjiID = #Lic_PosiadaczLicencjiID" connectionstring="<%$ ConnectionStrings:yourConnectionString %>" runat="server" onupdating="yourDataSource_Updating"> <UpdateParameters> <asp:Parameter Name="newvalue" DbType="String" /> </UpdateParameters> </asp:sqldatasource> code behind: protected void yourDataSource_Updating(object sender, SqlDataSourceCommandEventArgs e) { e.Command.Parameters["#newvalue"].Value = ((DropDownList)fv1.FindControl("Lic_PosiadaczLicencjiIDDropDownList")).SelectedValue; } and in your web service where you are getting your data from you need to add the context key to the signature exactly as shown as it is case sensitive. You then check your returned values for the selected value and set selected = true. If you want selected value instead of selected text then check for x.value instead of x.name. [WebMethod] public CascadingDropDownNameValue[] GetKontrahenci(string knownCategoryValues, string category, string contextKey) { CascadingDropDownNameValue[] results = getdata(); CascadingDropDownNameValue selectedVal = (from x in results where x.name == contextKey select x).FirstOrDefault(); if (selectedVal != null) selectedVal.isDefaultValue = true; return results; } Hope this helps!
Problem with cascading drop down in asp.net [duplicate]
I am having problem in EditItemTemplate of FormView. When I use such code in InsertItemTemplate everything works: <asp:DropDownList ID="Lic_PosiadaczLicencjiIDDropDownList" runat="server" SelectedValue='<%# Bind("Lic_PosiadaczLicencjiID") %>' /> <asp:CascadingDropDown ID="CascadingDropDown1" runat="server" TargetControlID="Lic_PosiadaczLicencjiIDDropDownList" Category="Knt_Kod" ServicePath="~/ManagerLicencjiService.asmx" ServiceMethod="GetKontrahenci"> </asp:CascadingDropDown> But when I use exactly the same code in EditItemTemplate I am getting an error that SelectedValue is wrong cause it doesn't exists on the list of elements. I think that the problem is that DropDownList is checked for the values before it is populated by the service. When I run debugger the error occured before breakpoint in the service method. How to solve this problem?
<rant>I've found the CCD very clunky and full of poorly-documented workarounds</rant> but here is how you do something as simple as selecting a value when filling the ddl. Note that the selected value is not set on the DDL and that it is being passed to the web service where the selecting is done. <asp:ScriptManager ID="sm1" runat="server"></asp:ScriptManager> <asp:FormView ID="fv1" runat="server" DataSourceID="yourDataSource"> <EditItemTemplate> <asp:DropDownList ID="Lic_PosiadaczLicencjiIDDropDownList" runat="server" /> <asp:CascadingDropDown ID="CascadingDropDown1" runat="server" TargetControlID="Lic_PosiadaczLicencjiIDDropDownList" Category="Knt_Kod" ServicePath="~/ManagerLicencjiService.asmx" ServiceMethod="GetKontrahenci" UseContextKey="true" ContextKey='<%# Bind("Lic_PosiadaczLicencjiID") %>'> </asp:CascadingDropDown> </EditItemTemplate> </asp:FormView> <asp:sqldatasource id="yourDataSource" selectcommand="select Lic_PosiadaczLicencjiID FROM yourdatabase" UpdateCommand="Update yourdatabase set Lic_PosiadaczLicencjiID = #newvalue WHERE Lic_PosiadaczLicencjiID = #Lic_PosiadaczLicencjiID" connectionstring="<%$ ConnectionStrings:yourConnectionString %>" runat="server" onupdating="yourDataSource_Updating"> <UpdateParameters> <asp:Parameter Name="newvalue" DbType="String" /> </UpdateParameters> </asp:sqldatasource> code behind: protected void yourDataSource_Updating(object sender, SqlDataSourceCommandEventArgs e) { e.Command.Parameters["#newvalue"].Value = ((DropDownList)fv1.FindControl("Lic_PosiadaczLicencjiIDDropDownList")).SelectedValue; } and in your web service where you are getting your data from you need to add the context key to the signature exactly as shown as it is case sensitive. You then check your returned values for the selected value and set selected = true. If you want selected value instead of selected text then check for x.value instead of x.name. [WebMethod] public CascadingDropDownNameValue[] GetKontrahenci(string knownCategoryValues, string category, string contextKey) { CascadingDropDownNameValue[] results = getdata(); CascadingDropDownNameValue selectedVal = (from x in results where x.name == contextKey select x).FirstOrDefault(); if (selectedVal != null) selectedVal.isDefaultValue = true; return results; } Hope this helps!