I'm trying to update the contents of one DropDownList based on the value of 3 other DropDownLists. I have 2 tables, one containing Projects and one containing SubProjects. These 2 have 3 matching Properties:
Year
CountryID
OEID
I want to assign SubProjects to Projects using these matching criteria. Thus my SubProject Adapter has a method that wetches a view containing this data. In the view I try to use following code:
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<asp:DetailsView ID="DetailsViewProject" runat="server" AutoGenerateRows="False" DataKeyNames="Guid" DataSourceID="ObjectDataSourceProject" DefaultMode="Insert" Height="50px" Width="125px" OnItemInserted="DetailsViewProject_ItemInserted">
<Fields>
<asp:TemplateField HeaderText="Name">
<InsertItemTemplate>
<asp:TextBox ID="TextBoxName" Width="245px" runat="server" Text='<%# Bind("Name") %>' MaxLength="100" ></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidatorName" ControlToValidate="TextBoxName" runat="server" ErrorMessage="Please specifiy a Name">*</asp:RequiredFieldValidator>
<cc1:ValidatorCalloutExtender ID="ValidatorCalloutExtenderName" runat="server" TargetControlID="RequiredFieldValidatorName" />
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Country">
<InsertItemTemplate>
<asp:DropDownList ID="lstCountries" Width="250px" runat="server" DataSourceID="ObjectDataSourceCountries" DataTextField="Name" DataValueField="ID" SelectedValue='<%#Bind("CountryID") %>' AutoPostBack="True"></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Organisation Unit">
<InsertItemTemplate>
<asp:DropDownList ID="lstOEs" Width="250px" runat="server" DataSourceID="ObjectDataSourceOEs" DataTextField="Name" DataValueField="ID" SelectedValue='<%#Bind("OrganisationUnitID") %>' OnDataBinding="lstOEs_DataBound" AutoPostBack="True"></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Year">
<InsertItemTemplate>
<asp:DropDownList ID="lstProjectYears" Width="250px" runat="server" DataSourceID="SqlDataSourceProjectYears" DataTextField="ProjectYear" DataValueField="ProjectYear" SelectedValue='<%#Bind("ProjectYear") %>' OnDataBinding="lstProjectYears_DataBound" AutoPostBack="True"></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Project">
<InsertItemTemplate>
<asp:DropDownList ID="lstProjectOverall" Width="250px" runat="server" DataTextField="ProjectName" DataSourceID="ObjectDataSourcePOs" DataValueField="ID" SelectedValue='<%#Bind("ProjectOverallID")%>'></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:CommandField ButtonType="Button" ShowCancelButton="False" ShowInsertButton="True" InsertText="Insert" />
</Fields>
</asp:DetailsView>
<asp:ValidationSummary ID="ValidationSummary1" runat="server" />
<asp:ObjectDataSource ID="ObjectDataSourceProject" runat="server" ...and so on
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="ObjectDataSourceOEs" runat="server" ...and so on
</asp:ObjectDataSource>
<-- here -->
<asp:ObjectDataSource ID="ObjectDataSourcePOs" runat="server"
SelectMethod="GetProjectsOverallByParameters" TypeName="Projects">
<SelectParameters>
<asp:ControlParameter ControlID="DetailsViewProject$lstProjectYears" name="ProjectYear" PropertyName="SelectedValue"/>
<asp:ControlParameter ControlID="DetailsViewProject$lstOEs" name="OrganisationUnitID" PropertyName="SelectedValue"/>
<asp:ControlParameter ControlID="DetailsViewProject$lstCountries" name="CountryID" PropertyName="SelectedValue"/>
</SelectParameters>
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="ObjectDataSourceCountries" runat="server" OldValuesParameterFormatString="{0}" SelectMethod="GetCountries" TypeName="Countries"></asp:ObjectDataSource>
<asp:SqlDataSource ID="SqlDataSourceProjectYears" runat="server" ...and so on
</asp:SqlDataSource>
</asp:Content>
But I'm getting this error:
System.InvalidOperationException: 'Databinding methods such as Eval(),
XPath(), and Bind() can only be used in the context of a databound
control.'
Why am I getting this error and how can I solve this? How can I refetch the data from the adapter using either code behind or the aspnet update mechanism?
you have to use property AppendDataBoundItems="True|False" when you have to put existing Item as it is.
I solved the issue:
The problem was, that Project scheme and ProjectOverall scheme didn't match.
I changed the adapter and added an access method for ProjectOverall.
SELECT ID, CountryID, OrganisationUnitID, ProjectYear, ProjectName
FROM wc_ProjectsOverall
WHERE (CountryID = #CountryID) AND (OrganisationUnitID = #OrganisationUnitID) AND (ProjectYear = #ProjectYear)
In ProjectOverall.cs:
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, false)]
public WebControlling.ProjectsOverallDataTable GetProjectsOverallByAttributes(Guid CountryID, Guid OrganisationUnitID, Int32 ProjectYear)
{
if (CountryID == null || OrganisationUnitID == null || ProjectYear == 0)
return new WebControlling.ProjectsOverallDataTable();
return Adapter.GetProjectsByAttribute(CountryID, OrganisationUnitID, ProjectYear);
}
In the view ProjectAdd.aspx:
<asp:TemplateField HeaderText="Project">
<InsertItemTemplate>
<asp:DropDownList ID="lstProjectOverall" Width="250px" runat="server" DataTextField="ProjectName" DataSourceID="ObjectDataSourcePOs" DataValueField="ID" AppendDataBoundItems="False"></asp:DropDownList>
</InsertItemTemplate>
</asp:TemplateField>
<asp:ObjectDataSource ID="ObjectDataSourceProject" runat="server" InsertMethod="AddProject" OldValuesParameterFormatString="{0}" SelectMethod="GetProjects" TypeName="Projects" OnInserting="ObjectDataSourceProject_Inserting" >
<InsertParameters>
<asp:Parameter Name="CountryID" Type="Object" />
<asp:Parameter Name="Name" Type="String" />
<asp:Parameter Name="Objective" Type="String" />
<asp:Parameter Name="ObjectiveQuantitativ" Type="String"/>
<asp:Parameter Name="ProjectYear" Type="Int32" />
<asp:Parameter Name="BaseCampaign" Type="Boolean" />
<asp:Parameter Name="ManagerID" Type="Object" />
<asp:Parameter Name="OrganisationUnitID" Type="Object" />
<%-- This parameter is different because the ProjectOverall ID is not part of the Project model, thus we insert the DropDownList value --%>
<asp:ControlParameter ControlID="DetailsViewProject$lstProjectOverall" name="ID" PropertyName="SelectedValue"/>
</InsertParameters>
</asp:ObjectDataSource>
<asp:ObjectDataSource ID="ObjectDataSourcePOs" runat="server"
SelectMethod="GetProjectsOverallByAttributes" TypeName="ProjectsOverall">
<SelectParameters>
<asp:ControlParameter ControlID="DetailsViewProject$lstProjectYears" name="ProjectYear" PropertyName="SelectedValue"/>
<asp:ControlParameter ControlID="DetailsViewProject$lstOEs" name="OrganisationUnitID" PropertyName="SelectedValue"/>
<asp:ControlParameter ControlID="DetailsViewProject$lstCountries" name="CountryID" PropertyName="SelectedValue"/>
</SelectParameters>
</asp:ObjectDataSource>
And that solved my issue.
Related
O' Great and Powerful Wizards of Visual Studio,
(asp.net C#)
I have 2 gridviews of employee skills. To simplify the question, I have only the employee badge_ID (123 = John Doe) and the skill_ID (34 = knowledge of CNC lathe usage). My database table looks like this:
table: empl_skills
ref_no (key) badge_ID Skill_ID prof_value Need_Improve skill_update
------ -------- -------- ------------ ------------ ------------
1 123 35 Expert No 10/08/20
2 123 36 Proficient No 10/08/20
3 123 37 Expert No 10/08/20
4 319 12 Expert No 10/08/20
5 319 60 Basic Yes 10/08/20
6 225 62 Proficient No 10/08/20
7 225 71 Proficient No 10/08/20
The first ref_no column is the primary key that is (1,1) identity
The setup is quite simple. The first Gridview1 only displays the badge_ID and SELECT so you can pick the employee. Once selected, Gridview1 hides and the second Gridview2 displays only that employee's skills with the EDIT and DELETE commandfields turned on. This was all working great and I built the code in the designer, all working, in less than 5 minutes.
Now I want to add an INSERT function. I was going to do that in a FooterTemplate with an INSERT linkbutton in the primary key column (ref_no) and textboxes in the other columns for data entry. So to do that I first need to convert the default asp:boundfields to asp:templatefields. As soon as I did that conversion of the fields, my EDIT and DELETE functions in the commandfield stopped working and I can't figure out why.
It really is quite maddening because in ALL of these other webforms below - more than 30 times, I have converted the default asp:boundfields to asp:templatefields and I've never had this problem.
Hopefully, someone can tell me what's going on, please?
Here's Gridview1 to select an employee (I removed the styling stuff)
<asp:GridView ID="GridView1" runat="server" BackColor="White" BorderColor="#999999"
BorderStyle="Solid" BorderWidth="1px"
CellPadding="3" CssClass="centerMyHeader" DataKeyNames="badge_ID"
DataSourceID="SqlDataSource1" OnPreRender="GridView1_PreRender"
ForeColor="Black" GridLines="Vertical">
<AlternatingRowStyle BackColor="#EEEEEE" />
<Columns>
<asp:CommandField ShowSelectButton="True" />
</Columns>
</asp:GridView>
Here's Gridview2 with the boundfields converted to templatefields:
<asp:GridView ID="GridView2" runat="server" BackColor="White" BorderColor="#999999"
BorderStyle="Solid" BorderWidth="1px"
CellPadding="3" CssClass="centerMyHeader" DataSourceID="SqlDataSource2"
ForeColor="Black" GridLines="Vertical" AutoGenerateColumns="False">
<AlternatingRowStyle BackColor="#EEEEEE" />
<Columns>
<asp:CommandField ShowDeleteButton="True" ShowEditButton="True" />
<asp:TemplateField HeaderText="ref_no" InsertVisible="False" SortExpression="ref_no">
<EditItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("ref_no") %>'></asp:Label>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label6" runat="server" Text='<%# Bind("ref_no") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="badge_ID" SortExpression="badge_ID">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("badge_ID") %>'>
</asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Bind("badge_ID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="skill_ID" SortExpression="skill_ID">
<EditItemTemplate>
<asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("skill_ID") %>'></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Bind("skill_ID") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="prof_value" SortExpression="prof_value">
<EditItemTemplate>
<asp:TextBox ID="TextBox3" runat="server" Text='<%# Bind("prof_value") %>'>
</asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label3" runat="server" Text='<%# Bind("prof_value") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="need_improve" SortExpression="need_improve">
<EditItemTemplate>
<asp:TextBox ID="TextBox4" runat="server" Text='<%# Bind("need_improve") %>'>
</asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label4" runat="server" Text='<%# Bind("need_improve") %>'>
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="skill_update" SortExpression="skill_update">
<EditItemTemplate>
<asp:TextBox ID="TextBox5" runat="server" Text='<%# Bind("skill_update") %>'>
</asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label5" runat="server" Text='<%# Bind("skill_update") %>'>
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
The CodeFile just hides whichever gridviews are supposed to show or not show:
protected void GridView1_PreRender(object sender, EventArgs e)
{
if (GridView1.SelectedRow != null)
{
//when one employee is selected
GridView1.Visible = false;
Label1.Visible = false;
GridView2.Visible = true;
Label2.Visible = true;
btnReload2.Visible = true;
}
else
{
//when the selection list is up (no one selected yet)
GridView2.Visible = false;
Label2.Visible = false;
GridView1.Visible = true;
Label1.Visible = true;
btnReload2.Visible = false;
}
}
While it only took me 5 minutes to initially build the gridviews (you can see how many times I've done this), I've spent the last two days trying to make this work with templatefields. Can anyone help, please? Thanks, John
EDIT: Sorry - I didn't say what happens.
Edit linkbutton: On a row when I click Edit, the textboxes appear and I can change the data in any column. Then I click Update. RESULT: the change does NOT take place and no errors are thrown. Essentially, nothing happens
Delete Linkbutton: when clicking Delete, the webform throws the error: Must declare the scalar variable "#ref_no". Not sure what they mean by that or moreover where? Here is SqlDataSource2 with ref_no declared in the UpdateParameters:
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:TrainingDBConnection %>"
DeleteCommand="DELETE FROM [empl_skills] WHERE [ref_no] = #ref_no"
InsertCommand="INSERT INTO [empl_skills] ([badge_ID], [skill_ID], [need_improve], [skill_update], [prof_value])
VALUES (#badge_ID, #skill_ID, #need_improve, #skill_update, #prof_value)"
SelectCommand="SELECT [ref_no], [badge_ID], [skill_ID], [need_improve], [skill_update], [prof_value] FROM [empl_skills] WHERE ([badge_ID] = #badge_ID)"
UpdateCommand="UPDATE [empl_skills] SET [badge_ID] = #badge_ID, [skill_ID] = #skill_ID, [need_improve] = #need_improve, [skill_update] = #skill_update,
[prof_value] = #prof_value WHERE [ref_no] = #ref_no">
<DeleteParameters>
<asp:Parameter Name="ref_no" Type="Int32" />
</DeleteParameters>
<InsertParameters>
<asp:Parameter Name="badge_ID" Type="Int32" />
<asp:Parameter Name="skill_ID" Type="Int32" />
<asp:Parameter Name="need_improve" Type="String" />
<asp:Parameter DbType="Date" Name="skill_update" />
<asp:Parameter Name="prof_value" Type="String" />
</InsertParameters>
<SelectParameters>
<asp:ControlParameter ControlID="GridView1" Name="badge_ID" PropertyName="SelectedValue" Type="Int32" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="badge_ID" Type="Int32" />
<asp:Parameter Name="skill_ID" Type="Int32" />
<asp:Parameter Name="need_improve" Type="String" />
<asp:Parameter DbType="Date" Name="skill_update" />
<asp:Parameter Name="prof_value" Type="String" />
<asp:Parameter Name="ref_no" Type="Int32" />
</UpdateParameters>
</asp:SqlDataSource>
Thanks.
nevermind - I guess there's no interest. I converted the second detail Gridview to a DetailsView which already has Edit, Delete, and Insert commandfields. Problem solved - no custom code. Works flawlessly. I guess no one really cares about webforms anymore, too old.
I encounter difficulties to update an item into an ASP.NET GridView (old code).
My stored procedure can be simplified to:
UPDATE myTable SET FieldA = #FieldA, FieldB = #FieldB, FieldC = #FieldC WHERE Id = #Id
My DataSource is a s follows:
<asp:SqlDataSource ID="DS" runat="server" ConnectionString="<%$ ConnectionStrings:wsg_DataWriter %>"
SelectCommand="MySelectSP" SelectCommandType="StoredProcedure"
UpdateCommand="MyUpdateSP" UpdateCommandType="StoredProcedure">
<SelectParameters>
<asp:QueryStringParameter Name="Id" QueryStringField="Id" />
</SelectParameters>
<UpdateParameters>
<asp:QueryStringParameter Name="idWsgProgrammeOption" QueryStringField="idWsgProgrammeOption" />
</UpdateParameters>
</asp:SqlDataSource>
My FormView is as follows :
<asp:FormView ID="FV" runat="server" DataKeyNames="Id" DataSourceID="DS" >
<EditItemTemplate>
<asp:TextBox ID="TB_FieldA" runat="server" Text='<%# Bind("FieldA") %>' />
<asp:ImageButton ID="IB_FieldB" runat="server" CommandName="UpdateProgramme" CommandArgument='<%# Eval("Id") %>'
ImageUrl='<%# (!Convert.ToBoolean(Eval("FieldB"))) ? "~/1.gif" : "~/2.gif" %>' OnCommand="On_FieldB"/>
<asp:TextBox ID="TB_FieldC" T runat="server" Text='<%# Bind("FieldC") %>' />
<asp:ImageButton ID="i_imgbtnEnregistrer" runat="server" AlternateText="Enregistrer"
CommandName="Update" ImageUrl="~/App_Themes/admindefaut/image/bouton/enregitrer.gif" OnClick="OnClick"/>
</EditItemTemplate>
</asp:FormView >
It does not work: I suppose the stored procedure fails to execute.
I am very close of having it working. The issue seems to come from the binding of FieldB into the SP.
It works fine if I change it this way:
<UpdateParameters>
<asp:QueryStringParameter Name="idWsgProgrammeOption" QueryStringField="idWsgProgrammeOption" />
</UpdateParameters>
-->
<UpdateParameters>
<asp:QueryStringParameter Name="idWsgProgrammeOption" QueryStringField="idWsgProgrammeOption" />
<asp:Parameter Name="FieldB" />
</UpdateParameters>
And add:
protected void OnClick(object sender, EventArgs e)
{
FV.UpdateParameters["FieldB"].DefaultValue= "1";
}
So somehow, .NET manages to get correct values of FieldA and FieldB and pass them to the SP. But it does not manage to grab the correct FieldB value.
Can anyone give me a hand there? I would need to pass the correct FieldB value value without using the code behind.
Try binding FieldB to a hidden field on your aspx page using the Bind keyword, not Eval.
So i am doing a small web site in ASP.NET and i need to insert some data into a DB. So far i have this, and it isnt any different from what i've seen in the internet:
<h1> Criar Novo Doente </h1>
<asp:SqlDataSource
ID="insertDoente"
runat ="server"
ConnectionString ="<%$ ConnectionStrings:principal %>"
ProviderName = "System.Data.SqlClient"
InsertCommand="INSERT INTO Doentes (do_Nome,do_Sexo,do_DataNascimento,do_IsInternado) VALUES (#do_Nome,#do_Sexo,#do_DataNascimento,0)" >
<InsertParameters>
<asp:Parameter Name="do_Nome" Type="String" />
<asp:Parameter Name="do_Sexo" Type="String" />
<asp:Parameter Name="do_DataNascimento" Type="DateTime" />
</InsertParameters>
</asp:SqlDataSource>
<asp:FormView ID="criarDoente" DefaultMode ="Insert" runat ="server"
DataSourceID="insertDoente"
DataKeyNames="do_Id"
OnItemInserted="MensagemFormView_ItemInserted">
<InsertItemTemplate>
<ul>
<li> </li>
</ul>
<label>Nome: </label> <asp:TextBox ID="nome" runat ="server" MaxLength="100" TextMode="SingleLine" Text='<%# Bind("do_Nome") %>' />
<asp:RequiredFieldValidator ID="NomeRequiredFieldValidator" ErrorMessage="Tem de indicar o nome!"
ControlToValidate="nome" Display="Dynamic" runat="server" />
<br/>
<label>Sexo </label> <asp:RadioButtonList id="Sexo" runat="server" SelectedValue='<%# Bind("do_Sexo") %>'><asp:ListItem id="Masc" Text ="Masculino" Value = "Masculino"></asp:ListItem> <asp:ListItem id="fem" Text ="Feminino" Value ="Feminino"></asp:ListItem> </asp:RadioButtonList>
<br/>
<label>Data de Nascimento: </label> <asp:TextBox ID="dataNasc" runat ="server" MaxLength="100" TextMode="SingleLine" Text='<%# Bind("data") %>' />
<asp:CompareValidator id="dataCompare" ControlToValidate="dataNasc"
Display="Dynamic" Operator="DataTypeCheck"
Type="Date"
ErrorMessage="A data tem de estar no formato YYYY/MM/DD"
ForeColor="Red" runat="server" />
<br />
<asp:Button ID="Criar" runat="server" Text="Criar" CommandName="Insert" />
</InsertItemTemplate>
</asp:FormView>
When i press the button to create, it triggers the onItemInserted event but nothing is added to the database :S Am i doing something wrong ?
EDIT: The fields of the insert commands are ordered the same way they are in the DB, only the ID is missing but that isnt needed or is it?
well I've stumbled upon similar cases of this problem, and also followed the solution approaches. But my case is quite strange, as I have 2 different DetailsView-controls (with different data), one works, the other doesn't.
So here's the problem in detail. I get the following error message:
DropDownList2 has a SelectedValue which is invalid because it does not exist in the list of items.
Parameter name: value
I am aware of this thread, and maybe I'm stupid and not seeing something. But maybe you do. I have 2 Detailsviews, which bind their data based on a dataset for one user. Both DVs have DropdownList-Controls inside their EditItemTemplates, which bind the possible values for this very column. I use SelectedValue='<%# Bind("mycolumn") %>' for my DropDownList-template exactly the same way in the 2 DVs.
As said, I am aware of code-behind workarounds, but I want to avoid those, to keep my code clean and consistent. I can't really document why I use a workaround on one DetailsView, and why I don't on the other.
Here is the code of my 2 DetailsViews:
<asp:DetailsView ID="dv_theme_ava" runat="server" Height="50px" Width="125px" AutoGenerateRows="False"
DataSourceID="SqlDataSource1" DefaultMode="Edit" AutoGenerateEditButton="True" DataKeyNames="Pat_ID">
<Fields>
<asp:TemplateField HeaderText="Theme">
<EditItemTemplate>
<asp:DropDownList ID="DropDownList1" runat="server" DataSourceID="SqlDataSource2"
DataTextField="theme" DataValueField="theme"
SelectedValue='<%# Bind("theme") %>'>
</asp:DropDownList>
<asp:Label ID="lolbel2" runat="server" Text='<%# Bind("theme") %>'></asp:Label>
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT [theme] FROM [gui_themes]"></asp:SqlDataSource>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Avatar">
<EditItemTemplate>
<asp:DropDownList ID="DropDownList2" runat="server" DataSourceID="SqlDataSource3"
DataTextField="avatar" DataValueField="avatar">
</asp:DropDownList>
<asp:Label ID="lolbel" runat="server" Text='<%# Bind("avatar") %>'></asp:Label>
<asp:SqlDataSource ID="SqlDataSource3" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT [avatar] FROM [gui_avatars]"></asp:SqlDataSource>
</EditItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT * FROM [pat_gui_config] WHERE ([Pat_ID] = #Pat_ID)" DeleteCommand="DELETE FROM [pat_gui_config] WHERE [Pat_ID] = #Pat_ID"
InsertCommand="INSERT INTO [pat_gui_config] ([Pat_ID], [theme], [avatar]) VALUES (#Pat_ID, #theme, #avatar)"
UpdateCommand="UPDATE [pat_gui_config] SET [theme] = #theme, [avatar] = #avatar WHERE [Pat_ID] = #Pat_ID">
<DeleteParameters>
<asp:Parameter Name="Pat_ID" Type="Int32" />
</DeleteParameters>
<InsertParameters>
<asp:Parameter Name="Pat_ID" Type="Int32" />
<asp:Parameter Name="theme" Type="String" />
<asp:Parameter Name="avatar" Type="String" />
</InsertParameters>
<SelectParameters>
<asp:SessionParameter Name="Pat_ID" SessionField="sel_pat_id" Type="Int32" />
</SelectParameters>
<UpdateParameters>
<asp:Parameter Name="theme" Type="String" />
<asp:Parameter Name="avatar" Type="String" />
<asp:Parameter Name="Pat_ID" Type="Int32" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:DetailsView ID="dv_treat_edit" runat="server" AutoGenerateEditButton="True"
AutoGenerateRows="False" DataKeyNames="Tr_ID" DataSourceID="sql_newcat" DefaultMode="Edit"
Height="50px" Width="90%" AllowPaging="True" CssClass="dv_details" Style="margin: 0 auto;">
<Fields>
<asp:BoundField DataField="Tr_ID" HeaderText="Tr_ID" InsertVisible="False" ReadOnly="True"
SortExpression="Tr_ID" />
<asp:BoundField DataField="description" HeaderText="Description" SortExpression="description" />
<asp:BoundField DataField="syn_ger" HeaderText="Display Name (German)" SortExpression="syn_ger" />
<asp:TemplateField HeaderText="Type">
<EditItemTemplate>
<asp:DropDownList ID="ddl_type0" runat="server" DataSourceID="sql_ddl_type0" DataTextField="type"
DataValueField="type" SelectedValue='<%# Bind("type") %>'>
</asp:DropDownList>
<asp:SqlDataSource ID="sql_ddl_type0" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT [type] FROM [entry_type]"></asp:SqlDataSource>
</EditItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Exclusive for Patient_ID">
<EditItemTemplate>
<asp:DropDownList ID="ddl_excl_pat0" runat="server" DataSourceID="sql_ddl_exclpat0"
DataTextField="Pat_ID" DataValueField="Pat_ID" SelectedValue='<%# Bind("custom_cat_for_Pat") %>'
AppendDataBoundItems="true">
<asp:ListItem Text="" Value=""></asp:ListItem>
</asp:DropDownList>
<asp:SqlDataSource ID="sql_ddl_exclpat0" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT [Pat_ID] FROM [patients]"></asp:SqlDataSource>
</EditItemTemplate>
</asp:TemplateField>
</Fields>
<CommandRowStyle CssClass="dv_footer" />
<RowStyle CssClass="dv_tr" />
<PagerSettings Mode="NumericFirstLast" Position="Top" Visible="False" />
</asp:DetailsView>
<asp:SqlDataSource ID="sql_newcat" runat="server" ConnectionString="<%$ ConnectionStrings:interacct_SQL_convConnectionString %>"
SelectCommand="SELECT * FROM [treat_cat]" DeleteCommand="DELETE FROM [treat_cat] WHERE [Tr_ID] = #Tr_ID"
InsertCommand="INSERT INTO [treat_cat] ([description], [syn_ger], [type], [custom_cat_for_Pat]) VALUES (#description, #syn_ger, #type, #custom_cat_for_Pat)"
UpdateCommand="UPDATE [treat_cat] SET [description] = #description, [syn_ger] = #syn_ger, [type] = #type, [custom_cat_for_Pat] = #custom_cat_for_Pat WHERE [Tr_ID] = #Tr_ID">
<DeleteParameters>
<asp:Parameter Name="Tr_ID" Type="Int32" />
</DeleteParameters>
<InsertParameters>
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="syn_ger" Type="String" />
<asp:Parameter Name="type" Type="String" />
<asp:Parameter Name="custom_cat_for_Pat" Type="Int32" />
</InsertParameters>
<UpdateParameters>
<asp:Parameter Name="description" Type="String" />
<asp:Parameter Name="syn_ger" Type="String" />
<asp:Parameter Name="type" Type="String" />
<asp:Parameter Name="custom_cat_for_Pat" Type="Int32" />
<asp:Parameter Name="Tr_ID" Type="Int32" />
</UpdateParameters>
</asp:SqlDataSource>
Note: the data model is quite simple. For comparison I used the 2 fields "theme" and "type". Both tables only have 1 column in my database, holding string entries.
Now, the "type"-DDL gets it's items perfectly fine, and has the SelectedValue bound to the value brought to the DetailsView by the datasource. When I bind the "theme"-DDL with SelectedValue, I get the error. Interesting: in the same EditItemTemplate I have set up a Label (ID "lolbel2" :p) to check the databinding. It works (of course, when I remove the SelectedValue from the DDL). So without the SelectedValue in the DDL my output is like
[DROPDOWNLIST] with items "space", "magic"
[LABEL] with text "magic" (since this is the value of my test-user).
Am I missing something? Am I completely nuts?
So, sorry for re-asking this question for like the 10th-time, but I want to UNDERSTAND what my code does.
Thanks in advance!
Konrad
Ooooohkay. Found the problem, that's what you get when you are stubborn as a goat. :)
When debugging a workaround with a HiddenField I noticed that the value, which gets bound the same way as the Label-Control, has some trailing whitespace. In particular: instead of "dog" I got "dog ". While this isn't shown in the asp:Label, I guess this was the reason why the value wasn't found in the DropDownList.
Where did the whitespace come from? In my SQL table I created the columns for "theme" and avatar as "nchar", and not "nvarchar". Apparently when using "nchar" as DataType the not-used chars of the tupels are filled with whitespace, or let's say the fields have fixed width (always x chars).
Changing the datatype to "nvarchar" helped me to get rid of the whitespace, and now the databinding of the DDLs works just fine.
I am documenting this, as maybe someone else will stumble over this aswell - and since there are 50 solutions and workarounds, maybe just taking a look at the database sometimes does the trick.
I'm using the BulkEditGridView control as discussed http://roohit.com/site/showArc.php?shid=bbb62, and it's perfect for my needs. The problem I'm having is that whenever I save, every visible row (I have paging enabled) gets updated. Stepping through the code I see that grid.DirtyRows.Count is equal to the number of items per page minus 1 when I click the save button.
I can't find where rows are set as dirty. Any suggestions where I can look?
My code-behind has only this:
using System;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Collections;
using System.Data.Common;
public partial class MSDS_MSDS_Admin_GridUpdate : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
gridMSDS.DataKeyNames = new String[] { "id" };
gridMSDS.DataBind();
}
}
}
EDIT: Here is the aspx code.
<%# Page Language="C#" MasterPageFile="~/MSDS/MSDS.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="GridUpdate.aspx.cs" Inherits="MSDS_MSDS_Admin_GridUpdate" Title="Untitled Page" %>
<%# Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
<%# Register Assembly="RealWorld.Grids" Namespace="RealWorld.Grids" TagPrefix="cc2" %>
<asp:Content ID="Content1" ContentPlaceHolderID="PageContent" runat="Server">
<br />
<asp:Button ID="btnSave" runat="server" Text="Save" Width="100%" />
<cc2:BulkEditGridView ID="gridMSDS" runat="server" AllowPaging="True" AllowSorting="True"
DataSourceID="sqlData" EnableInsert="False" InsertRowCount="1" PageSize="20"
SaveButtonID="btnSave" AutoGenerateColumns="False">
<Columns>
<asp:BoundField DataField="ID" HeaderText="ID" InsertVisible="False" Visible="false"
ReadOnly="True" SortExpression="ID" />
<asp:BoundField DataField="ChemicalTitle" HeaderText="ChemicalTitle" SortExpression="ChemicalTitle" />
<asp:TemplateField HeaderText="SheetDate" SortExpression="SheetDate">
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("SheetDate") %>' Width="85px"></asp:TextBox>
<cc1:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" Enabled="True"
TargetControlID="TextBox1">
</cc1:CalendarExtender>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Eval("SheetDate") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Filename" HeaderText="Filename" SortExpression="Filename" />
<asp:BoundField DataField="Manufacturer" HeaderText="Manufacturer" SortExpression="Manufacturer" />
<asp:BoundField DataField="UsageDept" HeaderText="UsageDept" SortExpression="UsageDept" />
<asp:TemplateField HeaderText="Notes" SortExpression="Notes">
<EditItemTemplate>
<asp:TextBox ID="TextBox5" runat="server" Text='<%# Bind("Notes") %>' TextMode="MultiLine"></asp:TextBox>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label6" runat="server" Text='<%# Bind("Notes") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Status" SortExpression="Status">
<EditItemTemplate>
<asp:DropDownList ID="ddlStatus" runat="server" DataSourceID="sqlStatus" DataTextField="DisplayValue"
DataValueField="Value" SelectedValue='<%# Bind("Status") %>'>
</asp:DropDownList>
<asp:SqlDataSource ID="sqlStatus" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
SelectCommand="getOptionList" SelectCommandType="StoredProcedure">
<SelectParameters>
<asp:Parameter DefaultValue="msds_Status" Name="ListName" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
</EditItemTemplate>
<ItemTemplate>
<asp:DropDownList ID="ddlStatus" runat="server" DataSourceID="sqlStatus" disabled="true"
BackColor="White" DataTextField="DisplayValue" DataValueField="Value" SelectedValue='<%# Bind("Status") %>'>
</asp:DropDownList>
<asp:SqlDataSource ID="sqlStatus" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
SelectCommand="getOptionList" SelectCommandType="StoredProcedure">
<SelectParameters>
<asp:Parameter DefaultValue="msds_Status" Name="ListName" Type="String" />
</SelectParameters>
</asp:SqlDataSource>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Health" SortExpression="Health">
<EditItemTemplate>
<center>
<asp:TextBox ID="TextBox2" runat="server" Style="text-align: center" Text='<%# Bind("Health") %>'
Width="25px"></asp:TextBox>
</center>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label3" runat="server" Text='<%# Bind("Health") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Fire" SortExpression="Fire">
<EditItemTemplate>
<center>
<asp:TextBox ID="TextBox3" runat="server" Text='<%# Bind("Fire") %>' Width="25px"></asp:TextBox></center>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label4" runat="server" Text='<%# Bind("Fire") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Reactivity" SortExpression="Reactivity">
<EditItemTemplate>
<center>
<asp:TextBox ID="TextBox4" runat="server" Text='<%# Bind("Reactivity") %>' Width="25px"></asp:TextBox></center>
</EditItemTemplate>
<ItemTemplate>
<asp:Label ID="Label5" runat="server" Text='<%# Bind("Reactivity") %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="DateUpdated" HeaderText="DateUpdated" SortExpression="DateUpdated"
ReadOnly="True" />
<asp:BoundField DataField="UpdatedBy" ReadOnly="True" HeaderText="UpdatedBy" SortExpression="UpdatedBy" />
</Columns>
</cc2:BulkEditGridView>
<asp:SqlDataSource ID="sqlData" runat="server" ConnectionString="<%$ ConnectionStrings:NCLWebConnectionString %>"
SelectCommand="SELECT [ID], [ChemicalTitle], dbo.dateOnly([SheetDate]) As [SheetDate], [Filename], [Manufacturer], [UsageDept], [Notes], isnull([Status], 4) as [Status], [Health], [Fire], [Reactivity], [DateUpdated], [UpdatedBy] FROM [msds_Sheets] ORDER BY [ChemicalTitle]"
UpdateCommand="msds_UpdateSheet" UpdateCommandType="StoredProcedure">
<UpdateParameters>
<asp:Parameter Name="ID" Type="Int32" />
<asp:Parameter Name="ChemicalTitle" Type="String" />
<asp:Parameter Name="SheetDate" DbType="DateTime" />
<asp:Parameter Name="Filename" Type="String" />
<asp:Parameter Name="Manufacturer" Type="String" />
<asp:Parameter Name="UsageDept" Type="String" />
<asp:Parameter Name="Notes" Type="String" />
<asp:Parameter Name="Status" Type="Int16" />
<asp:Parameter Name="Health" Type="Int16" />
<asp:Parameter Name="Fire" Type="Int16" />
<asp:Parameter Name="Reactivity" Type="Int16" />
<asp:ProfileParameter Name="UpdatedBy" Type="String" PropertyName="Username" />
</UpdateParameters>
</asp:SqlDataSource>
</asp:Content>
Testing Procedure is as follows:
-Load the page.
-Edit something in the first row.
-Click save button.
The code you have posted looks fine. My test code is almost identical to yours, but I cannot produce the unwanted results you are seeing.
The most significant difference between your code and mine is that I don't have the code for your master page. My master page is the default page created by VS 2008. Other than that, the differences are in the SQL and the underlying database. Specifically, I am not using any stored procedures, and I have made reasonable assumptions about the data types involved: the ID is int, Status is smallint, SheetDate is Date, DateUpdated is DateTime, and everything else is varchar.
EDIT: Your SheetDate appears to be just the date portion of an underlying DateTime value from the database. The fact that your query transforms the underlying data value for display purposes is not relevant, because the grid view can't tell that it is getting a modified value. END OF EDIT
Here are a few suggestions for diagnosing the problem:
Test without master page, or with a
very simple dummy master page that is
guaranteed not to have scripts that
interact with the BulkEditGridView.
Since the ToolkitScriptManager is
apparently on your master page, you will
need to either move the script
manager control to the page itself,
or else temporarily get rid of the
CalendarExtender element, which
requires the script manager.
Temporarily replace the Status
template field with a bound data
field, to eliminate possibility of
interaction with the DropDownList.
The drop down list did not cause
problems in my tests, but there may
be subtle differences between your
code and mine.
If neither of these help, then a possible cause is a problem in your
version of the BulkEditGridView.
EDIT - Additional suggestions for diagnosis:
Based on examination of the source code for BulkEditGridView, it appears that the code is properly tracking which rows of the grid are "dirty". Therefore, the most likely cause of a spurious dirty row is that one or more controls in the grid are incorrectly detecting a change to their data content. You can detect this in the debugger, using the source code for BulkEditGridView, rather than the pre-compiled DLL.
Set a breakpoint at the start of HandleRowChanged, the event handler which detects changes in any cell of the grid. By examining the sender parameter of that object in the debugger, you can tell which controls in the grid are undergoing value changes. In particular, you will be able to tell which controls, if any, are incorrectly triggering a value change event.
Once you determine which control(s) are incorrectly reporting that their value has changed, you can focus on why this is happening.
END OF EDIT
Some other suggestions to improve the code are as follows. These will not solve the original problem, but will make the code cleaner:
In all of the template fields, remove the ItemTemplates. They can never be used, since the BulkEditGridView forces every row to be in the edit state.
In the code behind, the BindData()
call is not needed, since the source
data is specified in the declarative
markup, and therefore the grid view
control will automatically bind the
data.
I am not an expert in BulkEditGridView control, but this is what I found at Matt Dotson's blog entry
You may not be able to know that the row has been updated, unless you watch for the change explicitly. First, for each row add a change handler
protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
{
base.InitializeRow(row, fields);
foreach (DataControlFieldCell cell in row.Cells)
{
if (cell.Controls.Count > 0)
{
AddChangedHandlers(cell.Controls);
}
}
}
You can use this snippet
private void AddChangedHandlers(ControlCollection controls)
{
foreach (Control ctrl in controls)
{
if (ctrl is TextBox)
{
((TextBox)ctrl).TextChanged += new EventHandler(this.HandleRowChanged);
}
else if (ctrl is CheckBox)
{
((CheckBox)ctrl).CheckedChanged += new EventHandler(this.HandleRowChanged);
}
else if (ctrl is DropDownList)
{
((DropDownList)ctrl).SelectedIndexChanged += new EventHandler(this.HandleRowChanged);
}
}
}
Then you define a dirty row list and add rows that needs to be updated there (in the event handler)
private List<int> dirtyRows = new List<int>();
void HandleRowChanged(object sender, EventArgs args)
{
GridViewRow row = ((Control) sender).NamingContainer as GridViewRow;
if (null != row && !dirtyRows.Contains(row.RowIndex))
{
dirtyRows.Add(row.RowIndex);
}
}
Finally, to commit changes, iterate through all the dirty rows and save changes
public void Save()
{
foreach (int row in dirtyRows)
{
this.UpdateRow(row, false);
}
dirtyRows.Clear();
}
And here is your ASPX code
<asp:Button runat="server" ID="SaveButton" Text="Save Data" />
<blog:BulkEditGridView runat="server" id="EditableGrid" DataSourceID="AdventureWorks" AutoGenerateColumns="False" DataKeyNames="LocationID" SaveButtonID="SaveButton" >
<Columns>
<asp:BoundField DataField="LocationID" HeaderText="LocationID" InsertVisible="False" ReadOnly="True" SortExpression="LocationID" />
<asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />
<asp:BoundField DataField="Availability" HeaderText="Availability" SortExpression="Availability" />
<asp:BoundField DataField="CostRate" HeaderText="CostRate" SortExpression="CostRate" />
</Columns>
</blog:BulkEditGridView>