How to Bind Multiple Values with Multi-Select Dropdown List - asp.net

I have a multi-select dropdown that I need to grab the values from and place in another data table - the Incident table.
I am pulling the values for the dropdown from this model:
using System.ComponentModel.DataAnnotations;
namespace DWITracker.Model
{
public class Charge
{
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "Charge")]
public string ChargeCode { get; set; }
[Required]
[Display(Name = "Charge Description")]
public string ChargeDesc { get; set; }
}
}
And this is my Create.cs:
using DWITracker.Data;
using DWITracker.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace DWITracker.Pages.Incidents;
[BindProperties]
public class CreateModel : PageModel
{
private readonly ApplicationDbContext _db;
public Incident Incident { get; set; }
public CreateModel(ApplicationDbContext db)
{
_db = db;
}
public IEnumerable<City> DisplayPIAddressCityData { get; set; }
public IEnumerable<County> DisplayPIAddressCountyData { get; set; }
public IEnumerable<Ethnicity> DisplayPIEthnicityData { get; set; }
public IEnumerable<int> Charge { get; set; }
public IEnumerable<SelectListItem> ChargeList { get; set; }
public async Task<PageResult> OnGet()
{
await _db.City.Select(a => a.CityName).ToListAsync();
DisplayPIAddressCityData = await _db.City.ToListAsync();
await _db.County.Select(a => a.CountyName).ToListAsync();
DisplayPIAddressCountyData = await _db.County.ToListAsync();
await _db.Ethnicity.Select(a => a.EthnicityName).ToListAsync();
DisplayPIEthnicityData = await _db.Ethnicity.ToListAsync();
var charges = from c in _db.Charge
select c;
ChargeList = charges.Select(c => new SelectListItem { Value = c.Id.ToString(), Text = c.ChargeCode });
return Page();
}
public async Task<IActionResult> OnPost()
{
await _db.Incident.AddAsync(Incident);
await _db.SaveChangesAsync();
TempData["success"] = "Incident Information added successfully.";
return RedirectToPage("Index");
}
}
And here is the relevant part of my view:
<td style="width: 40%">
<div class="mb-3">
<label asp-for="Incident.ArrestCharges" class="control-label"></label>
<select asp-for="Incident.ArrestCharges" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
<option value="">Select Applicable Charge(s)</option>
</select>
</div>
</td>
Right now, if I select more than one item from the multi-select dropdown, it places the Id value of only the FIRST selection made into the Incident.ArrestCharges column.
I would like it to place a comma separated list of multiple charge codes (not IDs) that would look like this as an example: VTL 1192-1, VTL 1192-2, VTL 1192-2
Would appreciate some guidance in what I need to remove, add or change. This is my first time creating a multi-select dropdown and binding it. Thank you!

Ok, so if user selects 2, or 1 or 5 options?
Then you will need to add 2, or 1 or 5 recrods here.
You can NOT do this:
Eg: tblPeople
FirstName: John
LastName: Smith
HotelsBooked: 123, 832, 992
So, by storing 3 values (of hotels I am booked to), then now what?
How you going to report, or query or use sql to query what Hotels I am booked into?
Answer: you can't!!!
So, do NOT just out of the blue start shoving multiple ID's into ONE column, since then you just broken all possibilities of using SQL to query that data.
In other words, you going to break your relational model, and you do that, then your whole system can't report on say what hotels I am booked into (in above example).
And since the column data type is a "number" and "id"? Then of course you can't just shove in multiple ID values. You COULD as my above example shows shove in multiple values if using a string for that column, but as noted, the INSTANT you do that? that is the very same instant you broken the relational model, but it not really about some relational "mumbo-jumbo", but now that you have no practical means to use sql to query that data. (and even translate the "id's" back into their correct values for display in reports or even in a web page.
So in above, if I want to allow the person to be booked into multiple hotels, I can't just out of the blue shove in multiple values, but will need to create a new child table. In my exmaple, it would be
tblPeople - my above information about the person.
tblPeopleBookedInhotels - this is the new child table.
So, in place of above, (where I attempted to shove in multiple id's into that column, I will now have this:
So, old:
New:
So, you can see the "instant" we want multiple hotels to be booked, then we can't use the one column (hotel_id) that used to save ONE value and ONE id.
The issue is even worse, since your column data type is no doubt a number type, and thus you can't even shove in a string of "id's" anyway.
And worse yet, you don't want to do that, since then any and all ability to query that data also goes out the window.
So, don't shove multiple values into that one column (that does not allow multiple values).
you will have to get the user selections, AND THEN LOOP over the selections, adding a whole new row to this new table that now can contain a "list" of many hotel bookings.
I mean, you can try can kluge this, and change that one column type from a "number" to a string, and then try to shove in multiple values as a string, but you then break all existing software that assumed and expected ONLY one "id" value in that column.
Edit: Example to write out many rows
Ok, so assume we have "people", and then that listbox of hotels they want to visit or whatever.
So, we have this:
So, we will have People, a table of hotels, and then of course our table that allows each person to have mutliple hotel bookings.
So, tblHotelsBooked.
we in effect have this:
So, say we have some markup to display the Person.
Nothing special, just some markup, AND ALSO that listbox of hotels.
so, we have this markup (not all that important).
<div id="EditRecord" runat="server" style="float:left;display: normal;border:solid 2px;padding:15px;border-radius:12px">
<h3>Edit Bookings</h3>
<div style="float:left" class="iForm">
<label>First Name</label>
<asp:TextBox ID="tFN" runat="server" f="FirstName" Width="140" /> <br />
<label>Last Name</label>
<asp:TextBox ID="tLN" runat="server" f="LastName" Width="140" /> <br />
<label>City</label>
<asp:TextBox ID="tCity" runat="server" f="City" Width="140" /><br />
<label>Prov</label>
<asp:TextBox ID="tProvince" runat="server" f="Province" Width="75" ></asp:TextBox>
</div>
<div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
<label>Notes</label> <br />
<asp:TextBox ID="txtNotes" runat="server" Width="400" TextMode="MultiLine"
Height="150px" f="Notes" ></asp:TextBox> <br />
</div>
<div style="float:left;margin-left:20px;margin-top:-30px" class="iForm">
<label>Select Hotels</label> <br />
<asp:ListBox ID="lstHotels" runat="server"
DataValueField="id"
DataTextField="HotelName"
SelectionMode="Multiple" Height="180px" Width="183px">
</asp:ListBox>
</div>
<div style="clear:both;height:20px"></div>
<button id="cmdSave" runat="server" class="btn myshadow" onserverclick="cmdSave_ServerClick" >
<span aria-hidden="true" class="glyphicon glyphicon-floppy-saved"> Save</span>
</button>
<button id="cmdCancel" runat="server" class="btn myshadow" style="margin-left:15px" >
<span aria-hidden="true" class="glyphicon glyphicon-arrow-left"> Back/Cancel</span>
</button>
<button id="cmdDelete" runat="server" class="btn myshadow" style="margin-left:15px">
<span aria-hidden="true" class="glyphicon glyphicon-trash"> Delete</span>
</button>
</div>
Ok, so our code has to load hte one person, and load up the listbox of hotels we want to choose.
So, code to load is this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState["PKID"] = 16; // assume passed value from preivous page
LoadData();
}
}
void LoadData()
{
string strSQL =
#"SELECT id, HotelName FROM tblHotelsA
ORDER BY HotelName";
lstHotels.DataSource = General.MyRst(strSQL); // load list box
lstHotels.DataBind();
strSQL = $#"SELECT * from People WHERE ID = {ViewState["PKID"]}";
DataTable dtPerson = General.MyRst(strSQL);
General.FLoader(EditRecord, dtPerson.Rows[0]);
}
And now we see/have this:
So, the save code has to:
Save any edits to the one person, and THEN write out a new row for each hotel selected in the listbox.
So, the save code is this:
protected void cmdSave_ServerClick(object sender, EventArgs e)
{
int PKID = (int)ViewState["PKID"];
General.FWriter(EditRecord, PKID, "People"); // send form to database
// now save/process list box of multiple selected hotels
// create one new whole row for each hotel selected.
string strSQL = "SELECT * FROM tblHotelsBooked WHERE ID = 0";
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
conn.Open();
DataTable dtBookedHotels = new DataTable();
dtBookedHotels.Load(cmdSQL.ExecuteReader());
foreach (ListItem OneHotel in lstHotels.Items)
{
if (OneHotel.Selected)
{
DataRow MyNewBooking = dtBookedHotels.NewRow();
MyNewBooking["People_ID"] = PKID;
MyNewBooking["Hotel_id"] = OneHotel.Value;
dtBookedHotels.Rows.Add(MyNewBooking);
}
}
SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
SqlCommandBuilder daU = new SqlCommandBuilder(da);
// now save/send all rows in data table back to database
da.Update(dtBookedHotels);
}
}
}
So, now we written out a row of data for each selected hotel.
However, in this example?
Our problem now is WHEN we go back to the page to edit again.
We will now have to not only load the one reocrd to edit, but ALSO then load up the list box with possible choices, and ALSO select/highlight each existing hotel so the user can "see" and "know" which options are selected.
And if the user un-selects a hotel, then we have to remove/delete that child row from our tblHotelsBooked.
Now, if this was just a single value (like favorote food), or some such, then that UI can be rather nice. However, in my case, given we are selecting (and adding) hotels to the give user?).
Then we REALLY need to adopt a UI in which the user can see what hotels, and add more hotels, and remove hotels.
So, really, the listbox should become a table like view (say a grid view).
So, THEN when we return to that reocrd, we can more clear see, choose, add, remove, edit, change that information.
So, really, we should dump the listbox, and use a grid of some type.
The result then would be this:
Now, in above example, we kind of have "more" then the selected list.
So, if we were to JUST have a listbox with selected hotels? Sure, but we kind of need "more" in this example, since I want to add more hotels, or remove them.
So, if we "stick" to the list box?
Then we need to add code to select-highlight existing selections.
then we need to add code to "delete" a child row when we un-select.

I would like it to place a comma separated list of multiple charge codes (not IDs)
That is because your dropdown value has been set with the Id, change the value to ChargeCode like below:
ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });
And the multiple dropdownlist should match List<string> type property from your requirement, so you need create a separate List<string> property to receive the multiple selected value and then convert this list to string with comma. At last you can set this string value to Incident.ArrestCharges .
Here is a whole working demo you could follow:
Page
#page
#model CreateModel
<form method="post">
<div class="mb-3">
<label asp-for="Incident.ArrestCharges" class="control-label"></label>
//change the tag helper value here...........
<select asp-for="MultiCharge" class="form-select" multiple="multiple" asp-items="Model.ChargeList">
<option value="">Select Applicable Charge(s)</option>
</select>
</div>
<input type="submit" value="Post" />
</form>
PageModel
[BindProperties]
public class CreateModel : PageModel
{
//more properties......
public Incident Incident { get; set; }
public IEnumerable<SelectListItem> ChargeList { get; set; }
public List<string> MultiCharge { get; set; } //receive the multiple selected values
public async Task<PageResult> OnGet()
{
//other not important code......
//var charges = from c in _db.Charge
// select c;
//hard-coding here is just for easy testing
var charges = new List<Charge>()
{
new Charge(){ChargeCode="aa",Id=1},
new Charge(){ChargeCode="bb",Id=2},
new Charge(){ChargeCode="xx",Id=3}
};
// change the Value here....
ChargeList = charges.Select(c => new SelectListItem { Value = c.ChargeCode, Text = c.ChargeCode });
return Page();
}
public async Task<IActionResult> OnPost()
{
//convert list into string with comma.....
Incident.ArrestCharges = String.Join(",", MultiCharge);
//.....
return RedirectToPage("Index");
}
}

Related

Scaffold DynamicData dropdown in web application from EF source

I want to scaffold a basic insert form which has dropdowns for the foreign keys.
I cant seem to figure out how to do this. When creating the metadata all the MetaColumns are of type MetaColumns, and none of type MetaForeignKeyColumns - which means that it renders textboxes for all properties - and no dropdown lists.
UIHints seem ineffective. I am using entity framework code first. How do I go about making those textboxes into dropdowns? In fact, I think the MetaData property is not being created ( I am probably missing something here ).
AddPermission.aspx (form only)
<asp:FormView runat="server" ID="AddPermissionForm"
ItemType="Common.Models.tag_permission"
InsertMethod="AddPermissionsForm_InsertItem" DefaultMode="Insert"
RenderOuterTable="false" OnItemInserted="AddPermissionForm_ItemInserted">
<InsertItemTemplate>
<fieldset>
<ol>
<asp:DynamicEntity runat="server" Mode="Insert" EnableViewState="true" ></asp:DynamicEntity>
</ol>
<asp:Button class="btn btn-primary" runat="server" Text="Insert" CommandName="Insert" />
<asp:Button class="btn btn-default" runat="server" Text="Cancel" CausesValidation="false" OnClick="CancelButton_Click" />
</fieldset>
</InsertItemTemplate>
tag_permission.cs ( part of code first model )
public partial class tag_permission
{
[ScaffoldColumn(false)]
public short tp_tag_permission_id { get; set; }
//foreign key one
public string tp_security_group_id { get; set; }
//foreign key two
public short tp_tag_id { get; set; }
[Display(Name = "View")]
public Nullable<bool> tp_vis { get; set; }
}
I might have to use a DynamicDataManager or something, but I'm not sure where to find how or if it should be used on this page alongside the formview.
I've also attached screenshots comparing the rendering of Default_Insert.aspx.cs in my web app, and a basic DynamicData website - almost out of the box from template. In the below pictures, the MetaTable Table property is being inspected in DynamicData/Default_Insert.ascx.cs.
The later image has an incomplete MetaTable property. It has null values for the DataContextType and DataContextPropertyName, and ForeignKeyColumnNames. I'd really like to set the MetaTable on the FormView properly
For reference: The code from the images below is
public partial class Default_InsertEntityTemplate : System.Web.DynamicData.EntityTemplateUserControl {
private MetaColumn currentColumn;
protected override void OnLoad(EventArgs e) {
foreach (MetaColumn column in Table.GetScaffoldColumns(Mode, ContainerType)) {
currentColumn = column;
Control item = new DefaultEntityTemplate._NamingContainer();
EntityTemplate1.ItemTemplate.InstantiateIn(item);
EntityTemplate1.Controls.Add(item);
}
}
...
DynamicData Web Site
My Web Application
Theres a few parts to this one:
On startup, I registered the dynamic data provider in global.asax
Global.asax.cs / other
App.DefaultModel.RegisterContext(
new Microsoft.AspNet.DynamicData.ModelProviders.EFDataModelProvider(() => new MyDbContext()),
new ContextConfiguration { ScaffoldAllTables = true });
App.cs ( a static class I use to store permanent references )
public static class App
...
private static MetaModel s_defaultModel = new MetaModel();
public static MetaModel DefaultModel
{get{ return s_defaultModel; }}
}
then on the page where I want to get the meta data I can do this
ModelMetaTable meta = App.DefaultModel.GetTable( nameof(db.MyAwsomeName) );
and then set the metadata
MyAwesomeForm.SetMetaTable(table);
and then the form will render will all foreign keys / navigation properties as they would in a dynamic data website.
All the properties of the ModelMetadata are then set and populated as you would expect - eg with the DataContext and ForeignKeyName are no longer null

What is going on with the ajax AutoCompleteExtender?

This really is bizarre. I'm trying to implement the AutoCompleteExtender using a database. I enter the first character, let's say 'T' into the textbox, and the drop down results panel shows all of the page's source code (client side rather than server side), one character per line as seen in the image.
AutoComplete Results Screenshot
Incidentally, Google Chrome displays this straight away, while IE has a good think about it, saying localhost is not responding because it's running a long script.
If I then select any one of those characters it then displays the correct results beginning with 'T', though replaces the character I typed with whatever I selected.
I pulled the code from a tutorial on codeproject.com and apart from changing the textbox ID and a bit of ADO so it points to my database, it's identical.
I'll include the code. What's going wrong?
Now for some reason, it's not letting me post the code, regardless of how I format, but this is what I used. AutoComplete With DataBase and AjaxControlToolkit
It is very hard to give answer without seeing the code... try this
<asp:TextBox ID="txtSearchKey" runat="server" Width="350" AutoPostBack="true" OnTextChanged="txtSearchKey_TextChanged" />
<asp:TextBoxWatermarkExtender ID="weSearchKey" runat="server" Enabled="True" TargetControlID="txtSearchKey" WatermarkText="Search by Name" WatermarkCssClass="watermark" />
<asp:AutoCompleteExtender ServiceMethod="YourWebMethod" MinimumPrefixLength="3" CompletionInterval="100" EnableCaching="false" CompletionSetCount="10" TargetControlID="txtSearchKey" ID="searchExtender" runat="server" FirstRowSelected="false" OnClientItemSelected="GetSelectedId" CompletionListCssClass="completionList" CompletionListItemCssClass="listItem" CompletionListHighlightedItemCssClass="itemHighlighted" CompletionListElementID="divCompletionListElement" />
<input type="hidden" id="hdnSelectedId" name="hdnSelectedId" value="" />
Have a javascript method to ensure selected item is captured
function GetSelectedId(source, eventArgs) {
var selectedId = eventArgs.get_value();
var e = document.getElementById('hdnSelectedId');
if (e) {
e.value = selectedId;
}
}
Create a WebMethod in your back end code that is configured in ServiceMethod property of AutoCompleteExtender
[ScriptMethod()]
[WebMethod]
public static List<string> YourWebMethod(string prefixText, int count)
{
var totalRecords = 0;
var searchResults = <<get your results here into a list or whatever container>>
//I used DataTable as a container here for searchResults
if (searchResults.Rows.Count == 0)
return new List<string>() { "No result found" };
//Create a List from your search results and return it
List<string> items = new List<string>();
foreach (DataRow searchResult in searchResults.Rows)
items.Add(AutoCompleteExtender.CreateAutoCompleteItem(searchResult["Name"].ToString(), searchResult["BuilderID"].ToString());
return items;
}

Displaying user uploaded image in Umbraco master page

I'm using Umbraco 4.7.1 and have created a custom DocumentType called "Partner". I want to use that custom DocumentType in a masterpage. I'll show the code.
public class Partner : IdentifyingMarkRemoved.DocumentTypes.Page {
[DocumentTypeProperty(UmbracoPropertyType.Textstring, Mandatory = true, Tab = "Content")]
public string PartnerName { get; set; }
[DocumentTypeProperty(UmbracoPropertyType.Textstring, Mandatory = true, Tab = "Content")]
public string PartnerLevel { get; set; }
[DocumentTypeProperty(UmbracoPropertyType.Upload, Mandatory = true, Tab = "Content")]
public string PartnerLogo { get; set; }
[DocumentTypeProperty(UmbracoPropertyType.RichtextEditor, Mandatory = true, Tab = "Content")]
public string PartnerDescription { get; set; }
}
Thats the custom DocumentType.
The Masterpage looks like this:
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<div id="partnerName"><%= this.CurrentContent.PartnerName %></div>
<div id="partnerLevel"><%= this.CurrentContent.PartnerLevel %></div>
<div id="partnerLogo"><%= this.CurrentContent.PartnerLogo %></div>
<div id="partnerDescription"><%= this.CurrentContent.PartnerDescription %></div>
</asp:Content>
Essentially what happens is a user will create a new page of type "Partner" and the application will demand those four pieces of information from them.
The user will upload an image and when the resulting page is created, the image is shown on the page.
Problem is, what I am getting when I test is this:
partnersname
partnerslevel
/media/541/plogo.jpg
Random text again
I have tried to represent the PartnerLogo as a type "object" so I could cast it to a type "Image" but that gives me an error.
I've tried doing that in the code behind, but again, I get an error.
I have attempted various combinations of quotes and nested asp commands, but nothing seems to be working.
I do have the sense that I'm on the edge of getting this right, but I'm too much of an ASP noob to know where I'm going wrong.
Looking forward to some help!
The image is stored as a string, referencing the URL the image is to be found at. So you will need to add your own tag in the HTML.
Something like this should work:
<img src='<%= this.CurrentContent.PartnerLogo %>' />

How do I wrap header text if my data source is a DataTable?

I have a GridView whose data source is built dynamically as a DataTable - how can I specify column-specific header wrapping considering I'm specifying the structure in code?
I've not found anything to deal with this specific situation as I need to wrap only some columns in specific places, e.g. wrapping the second column below after 'Long' but leaving others alone. Adding \n or <br /> don't work as they're just treated as literals.
var statsTable = new DataTable();
statsTable.Columns.Add("Run Date", typeof(DateTime));
statsTable.Columns.Add("Needlessly Long Test Header", typeof(string));
...etc
statsTable.Rows.Add(runDate, "example", ...)
gridView.DataSource = statsTable;
gridView.DataBind();
Not sure if this is relevant, but I've found that I need to keep AutoGenerateColumns = true on my GridView otherwise nothing shows up. This is confusing me as I thought specifying the columns would do the trick - if this is unrelated to this question I'll ask another later.
Using .Net 3.5, if that affects answers. It seems like it'd be a simple/common problem.
You could use a custom class to achieve that:
class CustomDataRow
{
public string ColumnHeader { get; set; }
public string ColumnName { get; set; }
public string ColumnValue { get; set; }
}
Then, instead of a DataTable, you could use a List to bind the grid. Then, in the ItemDataBound event you could cast the DataItem to a CustomDataRow. If e.Item.ItemType is header, set the header text. If it's an item, set the Text values.
Give something like this a shot:
Markup:
<asp:TemplateField>
<HeaderTemplate>
<%#HttpUtility.HtmlDecode(InsertBreaks(Eval("DataField")))%>
</HeaderTemplate>
</asp:TemplateField>
With a LiteralControl:
<asp:TemplateField>
<HeaderTemplate>
<asp:Literal ID="litHeader" Text='<%#HttpUtility.HtmlDecode(InsertBreaks(Eval("DataField")))%>' Mode="PassThrough"></asp:Literal>
</HeaderTemplate>
</asp:TemplateField>
Code-behind:
protected string InsertLineBreaks(string val)
{
return val.Replace("long", "long<br/>").replace("foo", "foo<br/>");
}

Converting client side html radio buttons to asp.net web controls with dynamic ids. (ASP.net)(VB)

I have the following client side code in .aspx page within a datalist itemtemplate that takes questions from the database like this:
<Itemtemplate>
<b> <%=GetQuestionNum()%>)
<%#Server.HtmlEncode(Eval("Text").ToString())%></b>
<br />
<asp:Panel ID="A" runat="server" Visible='<%#GetVisible(Eval("OptionA").Tostring())%>'>
<input name="Q<%#Eval("ID")%>" type="radio" value="A">
<%#Server.HtmlEncode(Eval("OptionA").ToString())%>
</option><br />
</asp:Panel>
<asp:Panel ID="B" runat="server" Visible='<%#GetVisible(Eval("OptionB").Tostring())%>'>
<input name="Q<%#Eval("ID")%>" type="radio" value="B">
<%#Server.HtmlEncode(Eval("OptionB").ToString())%>
</option><br />
</asp:Panel>
<asp:Panel ID="C" runat="server" Visible='<%#GetVisible(Eval("OptionC").Tostring())%>'>
<input name="Q<%#Eval("ID")%>" type="radio" value="C">
<%#Server.HtmlEncode(Eval("OptionC").ToString())%>
</option><br />
</asp:Panel>
<asp:Panel ID="D" runat="server" Visible='<%#GetVisible(Eval("OptionD").Tostring())%>'>
<input name="Q<%#Eval("ID")%>" type="radio" value="D">
<%#Server.HtmlEncode(Eval("OptionD").ToString())%>
</option><br />
</asp:Panel></itemtemplate>
The output is like:
1) What is your age group?
- Option 1
- Option 2
- Option 3
- Option 4
The ID's of the radio buttons are dynamic ("Q" & QuestionID). If there is no answer to a question then the GetVisible function returns false and the containing panel is hidden.
I have been trying to get rid of the html and replace these with asp:radiobuttons but it is not possible to set id's from databinding.. only simply. I was trying something like:
<asp:RadioButton ID="Q<%#Eval("ID")%>" runat="server" Visible='<%#GetVisible(Eval("OptionA").Tostring())%>'
Text='<%#Server.HtmlEncode(Eval("OptionA").ToString())%>' />
Here is the function that provides data:
Public Shared Function GetQuestionsForSurvey(ByVal id As Integer) As DataSet
Dim dsQuestions As DataSet = New DataSet()
Try
Using mConnection As New SqlConnection(Config.ConnectionString)
Dim mCommand As SqlCommand = New SqlCommand("sprocQuestionSelectList", mConnection)
mCommand.CommandType = CommandType.StoredProcedure
Dim myDataAdapter As SqlDataAdapter = New SqlDataAdapter()
myDataAdapter.SelectCommand = mCommand
mCommand.CommandType = CommandType.StoredProcedure
mCommand.Parameters.AddWithValue("#id", id)
myDataAdapter.Fill(dsQuestions)
mConnection.Close()
Return dsQuestions
End Using
Catch ex As Exception
Throw
End Try
End Function
but I'm finding it impossible to work with the html controls, i.e get their .text value from codebehind, or adding events!
Please can an expert suggest a better way to replace the html with suitable asp.net web controls or from the codebehind and output it. Or point me in the right direction?
Thanks :0)
I had some experience with ASP controls and data binding. The problem you are facing is probably the fact that once you declare a control via markup you can't access it from data binding. Also, you should not confuse the server-side ID with the client-side ID.
The server-side ID, mapped to Id property of controls, is used to programmatically access the control from code behind. Client-side ID is the ID that will be placed in tag's id attribute and is mapped to ClientId property.
Judging from your question, what you need is to build a multi-choice survey, and, in my opinion, it's not important how the IDs are generated, just that they are properly grouped for each question.
I'll answer the part of programmatically accessing controls in data binding, which is a part of your question.
Here is an example from my code. Suppose you have a very simple GridView like this
<asp:GridView ID="example" runat="server" OnRowDataBound="DataBound">
<Columns>
<asp:TemplateField HeaderText="New">
<ItemTemplate>
<asp:Image ID="imgExample" runat="server" />
</ItemTemplate>
</Columns>
</asp:GridView>
It takes a data set during data binding and sets the image according to some property. It works the same as DataList, don't worry.
Now, in code behind, you handle the RowDataBoundEvent. You can't access the imgExample object directly, because it's a child of the ItemTemplate. When the row is bound, you have direct access to the row and then you can use the FindControl method of Control class
Here is C# code example (easy to convert to VB)
protected void DataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow) //Required
{
GridViewRow row = e.Row;
[...] //get an email message
(row.Cells[0].FindControl("imgExample") as Image).ImageUrl = (email.AlreadyRead)
? "Mail_Small.png"
: "Mail_New_Small.png";
}
}
Application to your case
In order to build a multi-choice survey, my advice is to create a DataList that will hold questions (the outer control) and then, for each row, declare a RadioButtonList that holds answers (the inner control). Bind the outer data list to the data set of questions and answers. Handle the RowDataBound event or whatever it's called in the DataList world. When you handle that event, bind the inner radiobuttonlist to the answers.
It should work for you
I am actually working on something similar at the moment. I am using javascript and jQuery to dynamically add controls to my page. After adding them to my page I have to get the new controls, their text, etc. The way I've been doing it is something like this:
<table id='BuilderTable' class="BuilderTable">
<tbody class='BuilderBody'>
</tbody>
</table>
<asp:Button runat="server" ID="saveButton" OnClick="SaveButton_Click" OnClientClick="SaveData()"
Text="Save Form" />
<asp:HiddenField runat="server" ID="controlsData" />
This table is where I put all my new controls.
Then when the client clicks the save button it first calls this javascript / jQuery function:
function SaveData() {
var controlRows = $('#BuilderTable tr.control');
var controls= [];
controlRows.each(function (index) {
//process control information here ...
controlText = //use jQuery to get text, etc...
var control = {
Index: (index + 1),
Text: controlText
};
controls.push(control);
});
var str = JSON.stringify(questions);
$('#<%= controlsData.ClientID %>').val(str);
}
Then the server side function for the button click is called (this in in C#, adapt to VB).
protected void SaveButton_Click(object sender, EventArgs e)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
string str = controlsData.Value;
List<Control> controls = jss.Deserialize<List<Control>>(str);
}
Using a Control class like this:
public class Control
{
public int Index { get; set; }
public string Text { get; set; }
}
This code uses javascript and jQuery to get your controls, JSON to serialize the data and save it in a asp hiddenfield then grab the data server-side and deserialize into objects that your code can use. Then take the data and do whatever you need to.

Resources